亚洲欧美日韩综合系列在线_91精品人妻一区二区_欧美大肥婆一级特大AA片_九色91视频免费观看_亚洲综合国产精品_av中文字幕在线不卡_久久精品色综合网_看黄色视频的软件_无卡无码高清中文字幕码2024_亚洲欧美日韩天堂网

簡(jiǎn)單web服務(wù)器的實(shí)現(xiàn)(C++)

來(lái)源:Jialuhu 發(fā)布時(shí)間:2019-03-28 14:28:18 閱讀量:1833

一、具體功能實(shí)現(xiàn)

GET方法請(qǐng)求解析

POST方法請(qǐng)求解析

返回請(qǐng)求資源頁(yè)面

利用GET方法實(shí)現(xiàn)加減法

利用POST方法實(shí)現(xiàn)加減法

HTTP請(qǐng)求行具體解析

400403404錯(cuò)誤碼返回的處理

二、什么是web服務(wù)器

web服務(wù)器就是在物理服務(wù)器基礎(chǔ)上的具有服務(wù)端功能的網(wǎng)絡(luò)連接程序,簡(jiǎn)而言之就是處理客戶(hù)端發(fā)來(lái)的各種請(qǐng)求然后根據(jù)服務(wù)器的邏輯處理返回一個(gè)結(jié)果給客戶(hù)端。在web服務(wù)器和客戶(hù)端之間的通信是基于HTTP協(xié)議進(jìn)行的。而客戶(hù)端可以是瀏覽器也可以是支持HTTP協(xié)議的APP

那么瀏覽器應(yīng)該怎么連接上自己的web服務(wù)器呢,最簡(jiǎn)單的web服務(wù)器就是通過(guò)TCP三次握手建立連接后,服務(wù)器直接返回一個(gè)結(jié)果給瀏覽器。瀏覽器和服務(wù)器是通過(guò)TCP三路握手建立連接的。瀏覽器在通過(guò)URL(統(tǒng)一資源定位符,就是我們俗稱(chēng)的網(wǎng)絡(luò)地址)去請(qǐng)求服務(wù)器的連接,并且通過(guò)URL中的路徑請(qǐng)求服務(wù)器上的資源。舉個(gè)栗子就是這樣的:

最簡(jiǎn)單的web服務(wù)器:

 

#include<stdio.h>

#include<stdlib.h>

#include<sys/socket.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<sys/sendfile.h>

#include<fcntl.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include<assert.h>

#include<unistd.h>

#include<string.h>

const int port = 8888;

int main(int argc,char *argv[])

{

    if(argc<0)

    {

        printf("need two canshu\n");

        return 1;

    }

    int sock;

    int connfd;

    struct sockaddr_in sever_address;

    bzero(&sever_address,sizeof(sever_address));

    sever_address.sin_family = PF_INET;

    sever_address.sin_addr.s_addr = htons(INADDR_ANY);

    sever_address.sin_port = htons(8888);

 

    sock = socket(AF_INET,SOCK_STREAM,0);

 

    assert(sock>=0);

 

    int ret = bind(sock, (struct sockaddr*)&sever_address,sizeof(sever_address));

    assert(ret != -1);

 

    ret = listen(sock,1);

    assert(ret != -1);

    while(1)

    {

        struct sockaddr_in client;

        socklen_t client_addrlength = sizeof(client);

        connfd = accept(sock, (struct sockaddr*)&client, &client_addrlength);

        if(connfd<0)

        {

            printf("errno\n");

        }

        else{

                char request[1024];

                recv(connfd,request,1024,0);

                request[strlen(request)+1]='\0';

                printf("%s\n",request);

                printf("successeful!\n");

                char buf[520]="HTTP/1.1 200 ok\r\nconnection: close\r\n\r\n";//HTTP響應(yīng)

                int s = send(connfd,buf,strlen(buf),0);//發(fā)送響應(yīng)

                //printf("send=%d\n",s);

                int fd = open("hello.html",O_RDONLY);//消息體

                sendfile(connfd,fd,NULL,2500);//零拷貝發(fā)送消息體

                close(fd);

                close(connfd);

        }

    }

    return 0;

}

最簡(jiǎn)單的html文件:

 

<html>

    <body bgcolor="blue">

     this is the html.

     <hr>

     <p>hello word! waste young! </p><br>

    </body>

</html>

 

 

運(yùn)行web.c文件,生成執(zhí)行文件a.out,在終端執(zhí)行后,我們?cè)跒g覽器的網(wǎng)址欄中輸入:http://localhost:8888 然后確認(rèn)后,就會(huì)返回hello.html的文件頁(yè)面

 

 

 

這里的URLlocalhost:實(shí)際就是hostname,然后8888是端口,如果在端口后面再加上比如/hello.html這樣的路徑就表示請(qǐng)求服務(wù)器上的一個(gè)hello.html,這里請(qǐng)求方法是GET,所以要求服務(wù)器返回該資源的頁(yè)面。

 

那么此時(shí)再來(lái)看下服務(wù)器接收到的東西,就是HTTP請(qǐng)求。

 

 

 

第一行就是請(qǐng)求行,請(qǐng)求行的格式是這樣的:請(qǐng)求方法+空格+URL+空格+協(xié)議版本+\r+\n 這里的請(qǐng)求方法是GET ,URL/(在這里,URL就相當(dāng)于資源的路徑,若在網(wǎng)址欄輸入的是http://localhost:8888/hello.html的話(huà),這里瀏覽器發(fā)送過(guò)來(lái)的URL就是/hello.html),協(xié)議版本是HTTP/1.1(現(xiàn)在多數(shù)協(xié)議版本都是這個(gè))。

 

第二行到最后一行都是請(qǐng)求頭部,請(qǐng)求頭部的格式是這樣的: 頭部字段:+空格+數(shù)值+\r+\n 然后多個(gè)頭部子段組織起來(lái)就是請(qǐng)求頭部,在最后的頭部字段的格式中需要有兩個(gè)換行符號(hào),最后一行的格式是:頭部字段:+空格+數(shù)值+\r+\n+\r+\n 因?yàn)樵诤竺孢€要跟著請(qǐng)求數(shù)據(jù),為了區(qū)分請(qǐng)求數(shù)據(jù)和請(qǐng)求頭的結(jié)束,就多了一個(gè)換行符。

 

三、HTTP請(qǐng)求和響應(yīng)

1HTTP請(qǐng)求

簡(jiǎn)而言之就是客戶(hù)端發(fā)送給服務(wù)端的請(qǐng)求。請(qǐng)求格式上面略提到了一點(diǎn)點(diǎn),大概的格式就如下所示:

 

 

 

其中的細(xì)節(jié)就很多了,但是主要的是請(qǐng)求方法。其中頭部字段有很多,大家可以上網(wǎng)百度。主要實(shí)現(xiàn)的就是GET方法和POST方法,其中GET方法是請(qǐng)求資源,但是不改變服務(wù)器上資源的,POST方法的話(huà)就會(huì)請(qǐng)求更改服務(wù)器上的資源。除了這兩個(gè)方法外,還有PUTDELETE,HEAD,TRACE等等。對(duì)應(yīng)增刪查改的就是PUTDELETE、POSTGET。

 

 

 

然后URL就是要請(qǐng)求的資源路徑,協(xié)議版本為HTTP/1.1,頭部字段根據(jù)每個(gè)頭部字段名都代表著給服務(wù)器的一個(gè)信息,具體可以根據(jù)以下網(wǎng)址查看:https://blog.csdn.net/sinat_22840937/article/details/64438253

 

 

 

2HTTP響應(yīng)

HTTP響應(yīng)就是服務(wù)端返回給客戶(hù)端的響應(yīng)消息。響應(yīng)格式大概如下:

 

 

 

其中響應(yīng)首行格式如:HTTP/1.1+狀態(tài)響應(yīng)碼+\r\n 狀態(tài)響應(yīng)碼參考如下:https://baike.baidu.com/item/HTTP狀態(tài)碼/5053660?fr=aladdin

 

這里大概用的是200400,403,404,其中頭部字段需要注意content-length,在服務(wù)器中響應(yīng)碼若沒(méi)有消息題的長(zhǎng)度,瀏覽器就只能通過(guò)關(guān)閉客戶(hù)端才可以得知消息體的長(zhǎng)度,才可以顯示出消息體的具體表現(xiàn)。而且消息體的長(zhǎng)度必須要和消息體吻合。如果服務(wù)端發(fā)送的消息體長(zhǎng)度不正確的話(huà),會(huì)導(dǎo)致超時(shí)或者瀏覽器一直顯示不了要的資源文件。詳細(xì)可以參考博客:https://www.cnblogs.com/lovelacelee/p/5385683.html

 

四、如何寫(xiě)出小型 web服務(wù)器

1、代碼預(yù)備知識(shí)

了解TCP三次握手和TCP四次揮手

線(xiàn)程同步機(jī)制包裝類(lèi)

線(xiàn)程池創(chuàng)建

epoll多路復(fù)用

1TCP三次握手

 

服務(wù)器需要準(zhǔn)備好接受外來(lái)連接,通過(guò)socket bind listen三個(gè)函數(shù)完成,然后我們稱(chēng)為被動(dòng)打開(kāi)。

客戶(hù)則通過(guò)connect發(fā)起主動(dòng)連接請(qǐng)求,這就導(dǎo)致客戶(hù)TCP發(fā)送一個(gè)SYN(同步)分節(jié)去告訴服務(wù)器客戶(hù)將在待建立的連接中發(fā)送的數(shù)據(jù)的初始序列號(hào),通常SYN不攜帶數(shù)據(jù),其所在IP數(shù)據(jù)只有一個(gè)IP首部,一個(gè)TCP首部以及可能有的TCP選項(xiàng)。

服務(wù)器確認(rèn)客戶(hù)的SYN后,同時(shí)自己也要發(fā)送一個(gè)SYN分節(jié),它含有服務(wù)器將在同一個(gè)連接中發(fā)送的數(shù)據(jù)的初始化列序號(hào),服務(wù)器在單個(gè)分節(jié)中發(fā)送SYN和對(duì)客戶(hù)SYN的確認(rèn)

客戶(hù)必須去確認(rèn)服務(wù)器的SYN

 

 

 

 

2TCP四次揮手

 

某一個(gè)應(yīng)用進(jìn)程首先調(diào)用close,稱(chēng)為該端執(zhí)行主動(dòng)關(guān)閉,該端的TCP會(huì)發(fā)送一個(gè)FIN分節(jié),表示數(shù)據(jù)已經(jīng)發(fā)送完畢

接到FIN的對(duì)端將執(zhí)行被動(dòng)關(guān)閉,這個(gè)FINTCP確認(rèn),它的接受也作為一個(gè)文件結(jié)束符傳遞給接收端應(yīng)用進(jìn)程(放在已排隊(duì)等候該應(yīng)用進(jìn)程接收的任何其他數(shù)據(jù)之后),因?yàn)?/span>FIN的接收意味著接收端應(yīng)用進(jìn)程在相應(yīng)連接上已無(wú)額外數(shù)據(jù)可以接收

一段時(shí)間后,接收到這個(gè)文件結(jié)束符的應(yīng)用進(jìn)程會(huì)調(diào)用close關(guān)閉它的套接字,這會(huì)導(dǎo)致它的TCP也要發(fā)送一個(gè)FIN

接收這個(gè)最終FIN的原發(fā)送端TCP(即執(zhí)行主動(dòng)關(guān)閉的那一端)確認(rèn)這個(gè)FIN

 

 

參考網(wǎng)站:https://www.cnblogs.com/Andya/p/7272462.html

 

 3)線(xiàn)程池的創(chuàng)建

 

我用的是半同步/半反應(yīng)堆線(xiàn)程池。該線(xiàn)程池通用性比較高,主線(xiàn)程一般往工作隊(duì)列中加入任務(wù),然后工作線(xiàn)程等待后并通過(guò)競(jìng)爭(zhēng)關(guān)系從工作隊(duì)列中取出任務(wù)并且執(zhí)行。而且應(yīng)用到服務(wù)器程序中的話(huà)要保證客戶(hù)請(qǐng)求都是無(wú)狀態(tài)的,因?yàn)橥粋€(gè)連接上的不同請(qǐng)求可能會(huì)由不同的線(xiàn)程處理。

 

 

 

ps:若工作隊(duì)列為空,則線(xiàn)程就處于等待狀態(tài),就需要同步機(jī)制的處理。 

 

代碼:

 

 

#ifndef _THREADPOOL_H

#define _THREADPOOL_H

#include<iostream>

#include<list>

#include<cstdio>

#include<semaphore.h>

#include<exception>

#include<pthread.h>

#include"myhttp_coon.h"

#include"mylock.h"

using namespace std;

 

template<typename T>

/*線(xiàn)程池的封裝*/

class threadpool

{

private:

    int max_thread;//線(xiàn)程池中的最大線(xiàn)程總數(shù)

    int max_job;//工作隊(duì)列的最大總數(shù)

    pthread_t *pthread_poll;//線(xiàn)程池?cái)?shù)組

    std::list<T*> m_myworkqueue;//請(qǐng)求隊(duì)列

    mylocker m_queuelocker;//保護(hù)請(qǐng)求隊(duì)列的互斥鎖

    sem m_queuestat;//由信號(hào)量來(lái)判斷是否有任務(wù)需要處理

    bool m_stop;;//是否結(jié)束線(xiàn)程

public:

    threadpool();

    ~threadpool();

    bool addjob(T* request);

private:

    static void* worker(void *arg);

    void run();

};

/*線(xiàn)程池的創(chuàng)建*/

template <typename T>

threadpool<T> :: threadpool()

{

    max_thread = 8;

    max_job = 1000;

    m_stop = false;

    pthread_poll = new pthread_t[max_thread];//為線(xiàn)程池開(kāi)辟空間

    if(!pthread_poll)

    {

        throw std::exception();

    }

    for(int i=0; i<max_thread; i++)

    {

        cout << "Create the pthread:" << i << endl;

        if(pthread_create(pthread_poll+i, NULL, worker, this)!=0)

        {

            delete [] pthread_poll;

            throw std::exception();

        }

        if(pthread_detach(pthread_poll[i]))//將線(xiàn)程分離

        {

            delete [] pthread_poll;

            throw std::exception();

        }

    }

}

 

template <typename T>

threadpool<T>::~threadpool()

{

    delete[] pthread_poll;

    m_stop = true;

}

 

template <typename T>

bool threadpool<T>::addjob(T* request)

{

    m_queuelocker.lock();

    if(m_myworkqueue.size()> max_job)//如果請(qǐng)求隊(duì)列大于了最大請(qǐng)求隊(duì)列,則出錯(cuò)

    {

        m_queuelocker.unlock();

        return false;

    }

    m_myworkqueue.push_back(request);//將請(qǐng)求加入到請(qǐng)求隊(duì)列中

    m_queuelocker.unlock();

    m_queuestat.post();//將信號(hào)量增加1

    return true;

}

template <typename T>

void* threadpool<T>::worker(void *arg)

{

    threadpool *pool = (threadpool*)arg;

    pool->run();

    return pool;

}

 

template <typename T>

void threadpool<T> :: run()

{

    while(!m_stop)

    {

        m_queuestat.wait();//信號(hào)量減1,直到為0的時(shí)候線(xiàn)程掛起等待

        m_queuelocker.lock();

        if(m_myworkqueue.empty())

        {

            m_queuelocker.unlock();

            continue;

        }

        T* request = m_myworkqueue.front();

        m_myworkqueue.pop_front();

        m_queuelocker.unlock();

        if(!request)

        {

            continue;

        }

        request->doit();//執(zhí)行工作隊(duì)列

    }

}

#endif

 

 

4)同步機(jī)制的包裝類(lèi)

 

因?yàn)椴捎昧司€(xiàn)程池,就相當(dāng)于用了多線(xiàn)程編程,此時(shí)就需要考慮各個(gè)線(xiàn)程對(duì)公共資源的訪問(wèn)的限制,因?yàn)榉奖阒蟮拇a采用了三種包裝機(jī)制,分別是信號(hào)量的類(lèi),互斥鎖的類(lèi)和條件變量的類(lèi)。在服務(wù)器中我使用的是信號(hào)量的類(lèi)。其中信號(hào)量的原理和System V IPC信號(hào)量一樣(不抄書(shū)了,直接拍照了。。。)

 

 

 

 

 

代碼實(shí)現(xiàn):

 

#ifndef _MYLOCK_H

#define _MYLOCK_H

#include<iostream>

#include<list>

#include<cstdio>

#include<semaphore.h>

#include<exception>

#include<pthread.h>

#include"myhttp_coon.h"

using namespace std;

 

/*封裝信號(hào)量*/

class sem{

private:

    sem_t m_sem;

public:

    sem();

    ~sem();

    bool wait();//等待信號(hào)量

    bool post();//增加信號(hào)量

};

//創(chuàng)建信號(hào)量

sem :: sem()

{

    if(sem_init(&m_sem,0,0) != 0)

    {

        throw std ::exception();

    }

}

//銷(xiāo)毀信號(hào)量

sem :: ~sem()

{

    sem_destroy(&m_sem);

}

//等待信號(hào)量

bool sem::wait()

{

    return sem_wait(&m_sem) == 0;

}

//增加信號(hào)量

bool sem::post()

{

    return sem_post(&m_sem) == 0;

}

 

/*封裝互斥鎖*/

class mylocker{

private:

    pthread_mutex_t m_mutex;

public:

    mylocker();

    ~mylocker();

    bool lock();

    bool unlock();

};

 

mylocker::mylocker()

{

    if(pthread_mutex_init(&m_mutex, NULL) != 0)

    {

        throw std::exception();

    }

}

 

mylocker::~mylocker()

{

    pthread_mutex_destroy(&m_mutex);

}

/*上鎖*/

bool mylocker::lock()

{

    return pthread_mutex_lock(&m_mutex)==0;

}

/*解除鎖*/

bool mylocker::unlock()

{

    return pthread_mutex_unlock(&m_mutex) == 0;

}

 

/*封裝條件變量*/

class mycond{

private:

    pthread_mutex_t m_mutex;

    pthread_cond_t m_cond;

public:

    mycond();

    ~mycond();

    bool wait();

    bool signal();

};

 

mycond::mycond()

{

    if(pthread_mutex_init(&m_mutex,NULL)!=0)

    {

        throw std::exception();

    }

    if(pthread_cond_init(&m_cond, NULL)!=0)

    {

        throw std::exception();

    }

}

 

mycond::~mycond()

{

    pthread_mutex_destroy(&m_mutex);

    pthread_cond_destroy(&m_cond);

}

 

/*等待條件變量*/

bool mycond::wait()

{

    int ret;

    pthread_mutex_lock(&m_mutex);

    ret = pthread_cond_wait(&m_cond,&m_mutex);

    pthread_mutex_unlock(&m_mutex);

    return ret == 0;

}

 

/*喚醒等待條件變量的線(xiàn)程*/

bool mycond::signal()

{

    return pthread_cond_signal(&m_cond) == 0;

}

 

#endif

5epoll多路復(fù)用 

 

 epoll系列系統(tǒng)調(diào)用函數(shù)(#include<sys/epoll.h>):

 

int epoll_create(int size);創(chuàng)建內(nèi)核事件表

 

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);操作epoll的內(nèi)核事件表

 

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);一段時(shí)間內(nèi)等待一組文件描述符上的就緒事件

 

除此這些函數(shù)外,還需要了解epollLT模式和ET模式還有EPOLLONESHOT事件.

 

下面三篇博客了解下:?

 

https://blog.csdn.net/davidsguo008/article/details/73556811

 

https://blog.csdn.net/men_wen/article/details/53456491

 

https://blog.csdn.net/yusiguyuan/article/details/15027821

 

代碼:

 

#include<iostream>

#include<unistd.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include<stdio.h>

#include<errno.h>

#include<string.h>

#include<fcntl.h>

#include<stdlib.h>

#include<assert.h>

#include<sys/epoll.h>

#include"threadpool.h"

//#include"myhttp_coon.h"

using namespace std;

const int port = 8888;

 

int setnonblocking(int fd)

{

    int old_option = fcntl(fd, F_GETFL);

    int new_option = old_option | O_NONBLOCK;

    fcntl(fd, F_SETFL, new_option);

    return old_option;

}

 

void addfd(int epfd, int fd, bool flag)

{

    epoll_event ev;

    ev.data.fd = fd;

    ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP;

    if(flag)

    {

        ev.events = ev.events | EPOLLONESHOT;

    }

    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);

    setnonblocking(fd);

}

 

int main(int argc, char *argv[])

{

    threadpool<http_coon>* pool = NULL;

    pool = new threadpool<http_coon>;

    http_coon* users = new http_coon[100];

    assert(users);

    struct sockaddr_in address;

    bzero(&address, sizeof(address));

    address.sin_family = AF_INET;

    address.sin_port = htons(port);

    address.sin_addr.s_addr = htons(INADDR_ANY);

 

    int listenfd = socket(AF_INET,SOCK_STREAM,0);

    assert(listenfd >= 0);

 

    int ret;

    ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));

    assert(ret != -1);

 

    ret = listen(listenfd,5);

    assert(ret >= 0);

 

    int epfd;

    epoll_event events[1000];

    epfd = epoll_create(5);

    assert(epfd != -1);

    addfd(epfd, listenfd, false);//listen不能注冊(cè)EPOLLONESHOT事件,否則只能處理一個(gè)客戶(hù)連接

    while(true)

    {

        int number = epoll_wait(epfd, events, 1000, -1);

        if( (number < 0) && (errno != EINTR) )

        {

            printf("my epoll is failure!\n");

            break;

        }

        for(int i=0; i<number; i++)

        {

            int sockfd = events[i].data.fd;

            if(sockfd == listenfd)//有新用戶(hù)連接

            {

                struct sockaddr_in client_address;

                socklen_t client_addresslength = sizeof(client_address);

                int client_fd = accept(listenfd,(struct sockaddr*)&client_address, &client_addresslength);

                if(client_fd < 0)

                {

                    printf("errno is %d\n",errno);

                    continue;

                }

                /*如果連接用戶(hù)超過(guò)了預(yù)定于的用戶(hù)總數(shù),則拋出異常*/

               /* if(http_coon::m_user_count > MAX_FD)

                {

                    show_error(client_fd, "Internal sever busy");

                    continue;

                }*/

                //初始化客戶(hù)連接

                cout << epfd << " " << client_fd << endl;

                addfd(epfd, client_fd, true);

                cout << "client_fd:" << client_fd << "****\n";

                users[client_fd].init(epfd,client_fd);

            }

            else if(events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))

            {

                /*出現(xiàn)異常則關(guān)閉客戶(hù)端連接*/

                users[sockfd].close_coon();

            }

            else if(events[i].events & EPOLLIN)//可以讀取

            {

                

                if(users[sockfd].myread())

                {

                    

                    /*讀取成功則添加任務(wù)隊(duì)列*/

                    pool->addjob(users+sockfd);

                }

                else{

                    users[sockfd].close_coon();

                }

            }

            else if(events[i].events & EPOLLOUT)//可寫(xiě)入

            {

                if(!users[sockfd].mywrite())

                {

                    users[sockfd].close_coon();

                }

            }

        }

    }

    close(epfd);

    close(listenfd);

    delete[] users;

    delete pool;

    return 0;

    

}

 

 

2、主要邏輯思路

首先創(chuàng)建和客戶(hù)端的連接

服務(wù)器通過(guò)客戶(hù)端的HTTP請(qǐng)求解析來(lái)判斷返回何種結(jié)果.HTTP解析是以行為單位的,前提條件是根據(jù)\r\n來(lái)判斷是否完整度入一行,若完整讀入一行了那么就可以進(jìn)行解析了。

通過(guò)HTTP請(qǐng)求的解析后,在寫(xiě)緩沖區(qū)寫(xiě)如HTTP響應(yīng),發(fā)送給客戶(hù)端(HTTP應(yīng)答包括一個(gè)狀態(tài)行,多個(gè)頭部字段,一個(gè)空行和資源內(nèi)容,其中前三個(gè)部分的內(nèi)容一般會(huì)被web服務(wù)器放置在一塊內(nèi)存中,而文檔的內(nèi)容通常會(huì)被放到另一個(gè)單獨(dú)的內(nèi)存中)

發(fā)送響應(yīng)首行后,就可以發(fā)送主要的消息體了

主要就是封裝在myhttp_coon.h中:

 

 

#ifndef _MYHTTP_COON_H

#define _MYHTTP_COON_H

#include<iostream>

#include<stdio.h>

#include<string.h>

#include<sys/wait.h>

#include<sys/socket.h>

#include<errno.h>

#include<stdlib.h>

#include<unistd.h>

#include<assert.h>

#include<sys/sendfile.h>

#include<sys/epoll.h>

#include<sys/fcntl.h>

#include<sys/stat.h>

#include<sys/types.h>

using namespace std;

#define READ_BUF 2000

class http_coon{

public:

    /*NO_REQUESTION是代表請(qǐng)求不完整,需要客戶(hù)繼續(xù)輸入;BAD_REQUESTIONHTTP請(qǐng)求語(yǔ)法不正確;GET_REQUESTION代表獲得并且解析了一個(gè)正確的HTTP請(qǐng)求;FORBIDDEN_REQUESTION是代表訪問(wèn)資源的權(quán)限有問(wèn)題;FILE_REQUESTION代表GET方法資源請(qǐng)求;INTERNAL_ERROR代表服務(wù)器自身問(wèn)題;NOT_FOUND代表請(qǐng)求的資源文件不存在;DYNAMIC_FILE表示是一個(gè)動(dòng)態(tài)請(qǐng)求;POST_FILE表示獲得一個(gè)以POST方式請(qǐng)求的HTTP請(qǐng)求*/

    enum HTTP_CODE{NO_REQUESTION, GET_REQUESTION, BAD_REQUESTION, FORBIDDEN_REQUESTION,FILE_REQUESTION,INTERNAL_ERROR,NOT_FOUND,DYNAMIC_FILE,POST_FILE};

    /*HTTP請(qǐng)求解析的狀態(tài)轉(zhuǎn)移。HEAD表示解析頭部信息,REQUESTION表示解析請(qǐng)求行*/

    enum CHECK_STATUS{HEAD,REQUESTION};

private:

    char requst_head_buf[1000];//響應(yīng)頭的填充

    char post_buf[1000];//Post請(qǐng)求的讀緩沖區(qū)

    char read_buf[READ_BUF];//客戶(hù)端的http請(qǐng)求讀取

    char filename[250];//文件總目錄

    int file_size;//文件大小

    int check_index;//目前檢測(cè)到的位置

    int read_buf_len;//讀取緩沖區(qū)的大小

    char *method;//請(qǐng)求方法

    char *url;//文件名稱(chēng)

    char *version;//協(xié)議版本

    char *argv;//動(dòng)態(tài)請(qǐng)求參數(shù)

    bool m_linger;//是否保持連接

    int m_http_count;//http長(zhǎng)度

    char *m_host;//主機(jī)名記錄

    char path_400[17];//出錯(cuò)碼400打開(kāi)的文件名緩沖區(qū)

    char path_403[23];//出錯(cuò)碼403打開(kāi)返回的文件名緩沖區(qū)

    char path_404[40];//出錯(cuò)碼404對(duì)應(yīng)文件名緩沖區(qū)

    char message[1000];//響應(yīng)消息體緩沖區(qū)

    char body[2000];//post響應(yīng)消息體緩沖區(qū)

    CHECK_STATUS status;//狀態(tài)轉(zhuǎn)移

    bool m_flag;//true表示是動(dòng)態(tài)請(qǐng)求,反之是靜態(tài)請(qǐng)求

public:

    int epfd;

    int client_fd;

    int read_count;

    http_coon();

    ~http_coon();

    void init(int e_fd, int c_fd);//初始化

    int myread();//讀取請(qǐng)求

    bool mywrite();//響應(yīng)發(fā)送

    void doit();//線(xiàn)程接口函數(shù)

    void close_coon();//關(guān)閉客戶(hù)端鏈接

private:

    HTTP_CODE analyse();//解析Http請(qǐng)求頭的函數(shù)

    int jude_line(int &check_index, int &read_buf_len);//該請(qǐng)求是否是完整的以行\r\n

    HTTP_CODE head_analyse(char *temp);//http請(qǐng)求頭解析

    HTTP_CODE requestion_analyse(char *temp);//http請(qǐng)求行解析

    HTTP_CODE do_post();//對(duì)post請(qǐng)求中的參數(shù)進(jìn)行解析

    HTTP_CODE do_file();//對(duì)GET請(qǐng)求方法中的url 協(xié)議版本的分離

    void modfd(int epfd, int sock, int ev);//改變socket為狀態(tài)

    void dynamic(char *filename, char *argv);//通過(guò)get方法進(jìn)入的動(dòng)態(tài)請(qǐng)求處理

    void post_respond();//POST請(qǐng)求響應(yīng)填充

    bool bad_respond();//語(yǔ)法錯(cuò)誤請(qǐng)求響應(yīng)填充

    bool forbiden_respond();//資源權(quán)限限制請(qǐng)求響應(yīng)的填充

    bool succeessful_respond();//解析成功請(qǐng)求響應(yīng)填充

    bool not_found_request();//資源不存在請(qǐng)求響應(yīng)填充

};

 

void http_coon::init(int e_fd, int c_fd)

{

    epfd = e_fd;

    client_fd = c_fd;

    read_count = 0;

    m_flag = false;

}

 

http_coon::http_coon()

{

    

}

 

http_coon::~http_coon()

{

 

}

/*關(guān)閉客戶(hù)端鏈接*/

void http_coon::close_coon()

{

    epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, 0);

    close(client_fd);

    client_fd = -1;

 

}

/*改變事件表中的事件屬性*/

void http_coon::modfd(int epfd, int client_fd, int ev)

{

    epoll_event event;

    event.data.fd = client_fd;

    event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;

    epoll_ctl(epfd, EPOLL_CTL_MOD, client_fd, &event);

    

}

/*read函數(shù)的封裝*/

int http_coon::myread()

{

    bzero(&read_buf,sizeof(read_buf));

    while(true)

    {

        int ret = recv(client_fd, read_buf+read_count, READ_BUF-read_count, 0 );

        if(ret == -1)

        {

            if(errno == EAGAIN || errno == EWOULDBLOCK)//讀取結(jié)束

            {

                break;

            }

            return 0;

        }

        else if(ret == 0)

        {

            return 0;

        }

        read_count = read_count + ret;

    }

    strcpy(post_buf,read_buf);

    return 1;

}

/*響應(yīng)狀態(tài)的填充,這里返回可以不為bool類(lèi)型*/

bool http_coon::succeessful_respond()//200

{

    m_flag = false;

    bzero(requst_head_buf,sizeof(requst_head_buf));

    sprintf(requst_head_buf,"HTTP/1.1 200 ok\r\nConnection: close\r\ncontent-length:%d\r\n\r\n",file_size);

}

bool http_coon::bad_respond()//400

{

    bzero(url, strlen(url));

    strcpy(path_400,"bad_respond.html");

    url = path_400;

    bzero(filename,sizeof(filename));

    sprintf(filename,"/home/jialuhu/linux_net/web_sever/%s",url);

    struct stat my_file;

    if(stat(filename,&my_file)<0)

    {

        cout << "文件不存在\n";

    }

    file_size = my_file.st_size;

    bzero(requst_head_buf,sizeof(requst_head_buf));

    sprintf(requst_head_buf,"HTTP/1.1 400 BAD_REQUESTION\r\nConnection: close\r\ncontent-length:%d\r\n\r\n",file_size);

}

bool http_coon::forbiden_respond()//403

{

    bzero(url, strlen(url));

    strcpy(path_403,"forbidden_request.html");

    url = path_403;

    bzero(filename,sizeof(filename));

    sprintf(filename,"/home/jialuhu/linux_net/web_sever/%s",url);

    struct stat my_file;

    if(stat(filename,&my_file)<0)

    {

        cout << "失敗\n";

    }

    file_size = my_file.st_size;

    bzero(requst_head_buf,sizeof(requst_head_buf));

    sprintf(requst_head_buf,"HTTP/1.1 403 FORBIDDEN\r\nConnection: close\r\ncontent-length:%d\r\n\r\n",file_size);

}

bool http_coon::not_found_request()//404

{

    bzero(url, strlen(url));

    strcpy(path_404,"not_found_request.html");

    url = path_404;

    bzero(filename,sizeof(filename));

    sprintf(filename,"/home/jialuhu/linux_net/web_sever/%s",url);

    struct stat my_file;

    if(stat(filename,&my_file)<0)

    {

        cout << "草擬\n";

    }

    file_size = my_file.st_size;

    bzero(requst_head_buf,sizeof(requst_head_buf));

    sprintf(requst_head_buf,"HTTP/1.1 404 NOT_FOUND\r\nConnection: close\r\ncontent-length:%d\r\n\r\n",file_size);

}

 

/*動(dòng)態(tài)請(qǐng)求處理*/

void http_coon::dynamic(char *filename, char *argv)

{

    int len = strlen(argv);

    int k = 0;

    int number[2];

    int sum=0;

    m_flag = true;

    bzero(requst_head_buf,sizeof(requst_head_buf));

    sscanf(argv,"a=%d&b=%d",&number[0],&number[1]);

    if(strcmp(filename,"/add")==0)

    {

        sum = number[0] + number[1];

        sprintf(body,"<html><body>\r\n<p>%d + %d = %d </p><hr>\r\n</body></html>\r\n",number[0],number[1],sum);

        sprintf(requst_head_buf,"HTTP/1.1 200 ok\r\nConnection: close\r\ncontent-length: %d\r\n\r\n",strlen(body));

    }

    else if(strcmp(filename,"/multiplication")==0)

    {

        cout << "\t\t\t\tmultiplication\n\n";

        sum = number[0]*number[1];

        sprintf(body,"<html><body>\r\n<p>%d * %d = %d </p><hr>\r\n</body></html>\r\n",number[0],number[1],sum);

        sprintf(requst_head_buf,"HTTP/1.1 200 ok\r\nConnection: close\r\ncontent-length: %d\r\n\r\n",strlen(body));

    }

}

/*POST請(qǐng)求處理*/

void http_coon::post_respond()

{

    if(fork()==0)

    {

        dup2(client_fd,STDOUT_FILENO);

        execl(filename,argv,NULL);

    }

    wait(NULL);

}

 

/*判斷一行是否讀取完整*/

int http_coon::jude_line(int &check_index, int &read_buf_len)

{

    cout << read_buf << endl;

    char ch;

    for( ; check_index<read_buf_len; check_index++)

    {

        ch = read_buf[check_index];

        if(ch == '\r' && check_index+1<read_buf_len && read_buf[check_index+1]=='\n')

        {

            read_buf[check_index++] = '\0';

            read_buf[check_index++] = '\0';

            return 1;//完整讀入一行

        }

        if(ch == '\r' && check_index+1==read_buf_len)

        {

            return 0;

        }

        if(ch == '\n')

        {

            if(check_index>1 && read_buf[check_index-1]=='\r')

            {

                read_buf[check_index-1] = '\0';

                read_buf[check_index++] = '\0';

                return 1;

            }

            else{

                return 0;

            }

        }

    }

    return 0;

}

 

/*解析請(qǐng)求行*/

http_coon::HTTP_CODE http_coon::requestion_analyse(char *temp)

{

    char *p = temp;

    cout << "p=" << p << endl;

    for(int i=0; i<2; i++)

    {

        if(i==0)

        {

            method = p;//請(qǐng)求方法保存

            int j = 0;

            while((*p != ' ') && (*p != '\r'))

            {

                p++;

            }

            p[0] = '\0';

            p++;

            cout << "method:" <<method << endl;

          //  method++;

        }

        if(i==1)

        {

            url = p;//文件路徑保存

            while((*p != ' ') && (*p != '\r'))

            {

                p++;

            }

            p[0] = '\0';

            p++;

            cout << "url:" << url << endl;

        }

    }

    version = p;//請(qǐng)求協(xié)議保存

    while(*p != '\r')

    {

        p++;

    }

    p[0] = '\0';

    p++;

    p[0] = '\0';

    p++;

    cout << version << endl;

    if(strcmp(method,"GET")!=0&&strcmp(method,"POST")!=0)

    {

        return BAD_REQUESTION;

    }

    if(!url || url[0]!='/')

    {

        return BAD_REQUESTION;

    }

    if(strcmp(version,"HTTP/1.1")!=0)

    {

        return BAD_REQUESTION;

    }

    status = HEAD;//狀態(tài)轉(zhuǎn)移到解析頭部

    return NO_REQUESTION;//繼續(xù)解析

}

 

/*解析頭部信息*/

http_coon::HTTP_CODE http_coon::head_analyse(char *temp)

{

    if(temp[0]=='\0')

    {

        //獲得一個(gè)完整http請(qǐng)求

        return GET_REQUESTION;

    }

    //處理其他頭部

    else if(strncasecmp(temp,"Connection:", 11) == 0)

    {

        temp = temp+11;

        while(*temp==' ')

        {

            temp++;

        }

        if(strcasecmp(temp, "keep-alive") == 0)

        {

            m_linger = true;

        }

    }

    else if(strncasecmp(temp,"Content-Length:", 15)==0)

    {

       

        temp = temp+15;

        while(*temp==' ')

        {

            cout << *temp << endl;

            temp++;

        }

        m_http_count = atol(temp);//content-length需要填充

    }

    else if(strncasecmp(temp,"Host:",5)==0)

    {

        temp = temp+5;

        while(*temp==' ')

        {

            temp++;

        }

        m_host = temp;

    }

    else{

        cout << "can't handle it's hand\n";

    }

    return NO_REQUESTION;

}

 

http_coon::HTTP_CODE http_coon::do_file()//GET方法請(qǐng)求,對(duì)其請(qǐng)求行進(jìn)行解析,存寫(xiě)資源路徑

{

    char path[40]="/home/jialuhu/linux_net/web_sever";

    char* ch;

    if(ch=strchr(url,'?'))

    {

        argv = ch+1;

        *ch = '\0';

        strcpy(filename,url);

        return DYNAMIC_FILE;

    }

    else{

            strcpy(filename,path);

            strcat(filename,url);

            struct stat m_file_stat;

            if(stat(filename, &m_file_stat) < 0)

            {

                //cout << "打不開(kāi)\n";

                return NOT_FOUND;//NOT_FOUND 404

            }

            if( !(m_file_stat.st_mode & S_IROTH))//FORBIDDEN_REQUESTION 403

            {

                return FORBIDDEN_REQUESTION;

            }

            if(S_ISDIR(m_file_stat.st_mode))

            {

                return BAD_REQUESTION;//BAD_REQUESTION 400

            }

            file_size = m_file_stat.st_size;

            return FILE_REQUESTION;

    }

}

http_coon::HTTP_CODE http_coon::do_post()//POST方法請(qǐng)求,分解并且存入?yún)?shù)

{

    int k = 0;

    int star;

    char path[34]="/home/jialuhu/linux_net/web_sever";

    strcpy(filename,path);

    strcat(filename,url);

    star = read_buf_len-m_http_count;

    argv = post_buf + star;

    argv[strlen(argv)+1]='\0';

    if(filename!=NULL && argv!=NULL)

    {

        return POST_FILE;

    }

    return BAD_REQUESTION;

}

 

/*http請(qǐng)求解析*/

http_coon::HTTP_CODE http_coon::analyse()

{

    status = REQUESTION;

    int flag;

    char *temp = read_buf;

    int star_line = 0;

    check_index = 0;

    int star = 0;

    read_buf_len = strlen(read_buf);

    int len = read_buf_len;

    while((flag=jude_line(check_index, len))==1)

    {

        temp = read_buf + star_line;

        star_line = check_index;

        switch(status)

        {

            case REQUESTION://請(qǐng)求行分析,包括文件名稱(chēng)和請(qǐng)求方法

            {

                cout << "requestion\n";

                int ret;

                ret = requestion_analyse(temp);

                if(ret==BAD_REQUESTION)

                {

                    cout << "ret == BAD_REQUESTION\n";

                    //請(qǐng)求格式不正確

                    return BAD_REQUESTION;

                }

                break;

            }

            case HEAD://請(qǐng)求頭的分析

            {

                int ret;

                ret = head_analyse(temp);

                if(ret==GET_REQUESTION)//獲取完整的HTTP請(qǐng)求

                {

                    if(strcmp(method,"GET")==0)

                    {

                        return do_file();//GET請(qǐng)求文件名分離函數(shù)     

                    }

                    else if(strcmp(method,"POST")==0)

                    {

                        return do_post();//POST請(qǐng)求參數(shù)分離函數(shù)

                    }

                    else{

                        return BAD_REQUESTION;

                    }

                }

                break;

            }

            default:

            {

                return INTERNAL_ERROR;

            }

        }

    }

    return NO_REQUESTION;//請(qǐng)求不完整,需要繼續(xù)讀入

}

 

 

 

/*線(xiàn)程取出工作任務(wù)的接口函數(shù)*/

void http_coon::doit()

{

    int choice = analyse();//根據(jù)解析請(qǐng)求頭的結(jié)果做選擇

    switch(choice)

    {

        case NO_REQUESTION://請(qǐng)求不完整

        {

            cout << "NO_REQUESTION\n";

            /*改變epoll的屬性*/

            modfd(epfd, client_fd, EPOLLIN);

            return;

        }

        case BAD_REQUESTION: //400

        {

            cout << "BAD_REQUESTION\n";

            bad_respond();

            modfd(epfd, client_fd, EPOLLOUT);

            break;

        }

        case FORBIDDEN_REQUESTION://403

        {

            cout << "forbiden_respond\n";

            forbiden_respond();

            modfd(epfd, client_fd, EPOLLOUT);

            break;

        }

        case NOT_FOUND://404

        {

            cout<<"not_found_request"<< endl;

            not_found_request();

            modfd(epfd, client_fd, EPOLLOUT);

            break;   

        }

        case FILE_REQUESTION://GET文件資源無(wú)問(wèn)題

        {

            cout << "文件file request\n";

            succeessful_respond();

            modfd(epfd, client_fd, EPOLLOUT);

            break;

        }

        case DYNAMIC_FILE://動(dòng)態(tài)請(qǐng)求處理

        {

            cout << "動(dòng)態(tài)請(qǐng)求處理\n";

            cout << filename << " " << argv << endl;

            dynamic(filename, argv);

            modfd(epfd, client_fd, EPOLLOUT);

            break;

        }

        case POST_FILE://POST 方法處理

        {

            cout << "post_respond\n";

            post_respond();

            break;

        }

        default:

        {

            close_coon();

    }

 

    }

}

 

 

 

bool http_coon::mywrite()

{

    if(m_flag)//如果是動(dòng)態(tài)請(qǐng)求,返回填充體

    {

        int ret=send(client_fd,requst_head_buf,strlen(requst_head_buf),0);

        int r = send(client_fd,body,strlen(body),0);

        if(ret>0 && r>0)

        {

            return true;

        }

    }

    else{

            int fd = open(filename,O_RDONLY);

            assert(fd != -1);

            int ret;

            ret = write(client_fd,requst_head_buf,strlen(requst_head_buf));

            if(ret < 0)

            {

                close(fd);

                return false;

            }

            ret = sendfile(client_fd, fd, NULL, file_size);

            if(ret < 0)

            {

                close(fd);

                return false;

            }

            close(fd);

            return true;

    }

    return false;

}

#endif

其中兩個(gè)附加功能加法和減法的實(shí)現(xiàn)(通過(guò)GET方法請(qǐng)求),以及POST方法請(qǐng)求的加法和減法的實(shí)現(xiàn)

 

動(dòng)態(tài)請(qǐng)求是什么樣子(GET)

sum.html文件:

 

<html>

<head>

<meta charset="utf-8">

<title>sum</title>

</head>

<body>

 

<form action="add">

a: <input type="text" name="a"><br>

b: <input type="text" name="b"><br>

<input type="submit" value="提交">

</form>

 

<p>點(diǎn)擊"提交"按鈕,表單數(shù)據(jù)將被發(fā)送到服務(wù)器上的“add”程序上。</p>

 

</body>

</html>

服務(wù)器收到的請(qǐng)求是這樣的,首先是打開(kāi)sum.html文件

 

 

 

然后在表單上提交要相加的兩個(gè)數(shù)字

 

 

 

點(diǎn)擊提交后,此時(shí)服務(wù)器收到的請(qǐng)求是這樣的:

 

 

 

看到了/add?a=33&b=33 這就是通過(guò)方法GET提交上來(lái)的參數(shù)ab ,此時(shí)我們?cè)诮馕稣?qǐng)求行的時(shí)候就可以通過(guò)問(wèn)好來(lái)判斷是否是GET的動(dòng)態(tài)請(qǐng)求,若是那么根據(jù)sscanf()函數(shù),分離出參數(shù)ab,進(jìn)行相加后就可以填充HTTP響應(yīng)發(fā)送給瀏覽器了。此處我根據(jù)提交的程序名稱(chēng)來(lái)選擇函數(shù),在函數(shù)中相加填充返回給瀏覽器。當(dāng)然我覺(jué)得正確的做法是重新寫(xiě)一個(gè)add.c然后執(zhí)行生產(chǎn)add文件,再在fork()一個(gè)子線(xiàn)程通過(guò)execl( )函數(shù)去執(zhí)行。

 

那么POST請(qǐng)求又是什么樣子呢,其實(shí)POST請(qǐng)求將參數(shù)放在了請(qǐng)求

修改后的sum.html文件

 

<html>

<head>

<meta charset="utf-8">

<title>sum</title>

</head>

<body>

 

<form action="add" method="post">

a: <input type="text" name="a"><br>

b: <input type="text" name="b"><br>

<input type="submit" value="提交">

</form>

 

<p>點(diǎn)擊"提交"按鈕,表單數(shù)據(jù)將被發(fā)送到服務(wù)器上的“add”程序上。</p>

 

</body>

</html>

加入了屬性method="post",此時(shí)打開(kāi)sum.html文件依然是GET方法,只是點(diǎn)擊提交表單后用的是POST方法。

 

 

 

GET不同的是,參數(shù)被在請(qǐng)求的數(shù)據(jù)部分,也就是空行之后,此時(shí)若方法是POST的話(huà),根據(jù)read_buf_lenContent_Length就可以求出參數(shù)在read_buf中的起始位置。然后又可以通過(guò)sscanf( )分離參數(shù)了,然后fork()一個(gè)進(jìn)程,利用dup2函數(shù),將標(biāo)準(zhǔn)輸出重定向到瀏覽器的sockfd上,再執(zhí)行execl( )函數(shù)。此時(shí)我們的add執(zhí)行文件的.c文件如下:

 

#include<stdio.h>

#include<string.h>

int main(int argc, char *argv[])

{

    char re_head[1000];

    char message[1000];

    int ret;

    int a,b,result;

    ret = sscanf(argv[0],"a=%d&b=%d", &a, &b);

    //printf("a=%d\t b=%d\n",a,b);

    if(ret < 0 || ret != 2)

    {

        sprintf(message,"<html><body>\r\n");

        sprintf(message,"%s<p>failure</p>\r\n",message);

        sprintf(message,"%s</body></html>");

 

        sprintf(re_head,"HTTP/1.1 GET\r\n");

        sprintf(re_head,"%scontent-length: %d\r\n",re_head,strlen(message));

        sprintf(re_head,"%scontent-type: text/html\r\n",re_head);

        sprintf(re_head,"%sconection: close\r\n\r\n");

        /*錯(cuò)誤提示消息*/

    }

    else{

        result = a+b;

        /*返回正確信息*/

        sprintf(message,"<html><body>\r\n");

        sprintf(message,"%s<p>%d + %d = %d</p><br>\r\n",message,a,b,result);

        sprintf(message,"%s<p>welcome to the word of jialuhu</p><br>\r\n",message);

        sprintf(message,"%s</body></html>\r\n",message);

        

        sprintf(re_head,"HTTP/1.1 200 ok\r\n");

        sprintf(re_head,"%sContent-length: %d\r\n",re_head,(int)strlen(message));

        sprintf(re_head,"%scontent-type: text/html\r\n\r\n",re_head);

       // sprintf(re_head,"%sconection: close\r\n\r\n");

    }

    printf("%s",re_head);

    printf("%s",message);

    fflush(stdout);

    return 0;

}

當(dāng)然除了加減法,還有很多功能可以去實(shí)現(xiàn)。此處就簡(jiǎn)單實(shí)現(xiàn)了這些功能。還有一些HTML文件,因?yàn)閼邪┰?,所以隨便寫(xiě)了幾個(gè)。

 

五、總結(jié)

縱觀博客其實(shí)感覺(jué)涉及的知識(shí)有點(diǎn)雜亂,但是很綜合吧。首先滿(mǎn)足代碼上高性能的需求,利用了線(xiàn)城池和epoll多路復(fù)用,其中也包括同步機(jī)制的封裝。其次就是HTTP這塊的知識(shí)了,包括請(qǐng)求格式響應(yīng)格式和請(qǐng)求方法和響應(yīng)狀態(tài)碼,很多很多都是零零碎碎平湊一起的。而且感覺(jué)這個(gè)服務(wù)器的實(shí)現(xiàn),也終于明白了瀏覽器和后臺(tái)是怎么溝通交流的,有時(shí)候看不如動(dòng)手實(shí)現(xiàn)下,很多東西就會(huì)突然明白了。大體模塊就是epoll、線(xiàn)城池、同步機(jī)制、邏輯處理。代碼里肯定也有很多沒(méi)有測(cè)試出來(lái)的bug,但是實(shí)現(xiàn)大概三分之二后還是有丟丟開(kāi)心的吧。


分享:
評(píng)論:
你還沒(méi)有登錄,請(qǐng)先