TinyWebServer项目笔记 ——03 http连接处理(上)

06-01 1308阅读

目录

1.介绍

2.基础知识

(1)epoll

1.epoll_create函数

2.epoll_ctl函数

3.eopll_wait函数

4.select/poll/epoll

5. LT/ET/EPOLLONESHOT

(2)HTTP报文格式

1.请求报文

2.响应报文

(3)HTTP状态码

(4)有限状态机

TinyWebServer项目笔记 ——03 http连接处理(上)
(图片来源网络,侵删)

3.http处理流程

(1)http报文处理流程

TinyWebServer项目笔记 ——03 http连接处理(上)
(图片来源网络,侵删)

(2)http类

(3)epoll相关代码

TinyWebServer项目笔记 ——03 http连接处理(上)
(图片来源网络,侵删)

(4)服务器接收http请求


1.介绍

        在服务器项目中,http请求的处理和响应至关重要,关系到用户界面的跳转和反馈。

        基础知识方面包括epoll、HTTP报文格式、状态码和有限状态机。

        代码分析方面,对服务器端处理http请求的全部流程进行简要介绍,然后结合代码对http类及请求接收进行详细分析。

2.基础知识

(1)epoll

        epoll涉及的知识比较多,仅对API和基础知识做介绍。更多资料可以查看——Linux高性能服务器编程这本书。

1.epoll_create函数
1    #include 
2    int epoll_create(int size)

        创建一个指示epoll内核事件表的文件描述符,该描述符将用作其他epoll系统调用的第一个参数,size不起作用。

2.epoll_ctl函数
1    #include 
2    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

        该函数用于操作内核事件表健康的文件描述符上的事件:注册、修改和删除。

  • epfd:为epoll_creat的句柄

  • op:表示动作,用3个宏来表示:

    • EPOLL_CTL_ADD (注册新的fd到epfd),

    • EPOLL_CTL_MOD (修改已经注册的fd的监听事件),

    • EPOLL_CTL_DEL (从epfd删除一个fd);

    • event:告诉内核需要监听的事件

              上述event是epoll_event结构体指针类型,表示内核所监听的事件,具体定义如下:

      • events描述事件类型,其中epoll事件类型有以下几种

        • EPOLLIN:表示对应的文件描述符可以读(包括对端SOCKET正常关闭)

        • EPOLLOUT:表示对应的文件描述符可以写

        • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)

        • EPOLLERR:表示对应的文件描述符发生错误

        • EPOLLHUP:表示对应的文件描述符被挂断;

        • EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)而言的

        • EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

          3.eopll_wait函数
          1    #include 
          2    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int             
                   timeout)

          该函数用于等待所监控文件描述符上事件的产生,返回就绪的文件描述符个数

          • events:用来存内核得到事件的集合,

          • maxevents:告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,

          • timeout:是超时时间

            • -1:阻塞

            • 0:立即返回,非阻塞

            • >0:指定毫秒

            • 返回值:成功返回有多少文件描述符就绪,时间到时返回0,出错返回-1

              4.select/poll/epoll
              • 调用函数

                • select和poll都是一个函数,epoll是一组函数

                • 文件描述符数量

                  • select通过线性表描述文件描述符集合,文件描述符有上限,一般是1024,但可以修改源码,重新编译内核,不推荐

                  • poll是链表描述,突破了文件描述符上限,最大可以打开文件的数目

                  • epoll通过红黑树描述,最大可以打开文件的数目,可以通过命令ulimit -n number修改,仅对当前终端有效

                  • 将文件描述符从用户传给内核

                    • select和poll通过将所有文件描述符拷贝到内核态,每次调用都需要拷贝

                    • epoll通过epoll_create建立一棵红黑树,通过epoll_ctl将要监听的文件描述符注册到红黑树上

                    • 内核判断就绪的文件描述符

                      • select和poll通过遍历文件描述符集合,判断哪个文件描述符上有事件发生

                      • epoll_create时,内核除了帮我们在epoll文件系统里建了个红黑树用于存储以后epoll_ctl传来的fd外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。

                      • epoll是根据每个fd上面的回调函数(中断函数)判断,只有发生了事件的socket才会主动的去调用 callback函数,其他空闲状态socket则不会,若是就绪事件,插入list

                      • 应用程序索引就绪文件描述符

                        • select/poll只返回发生了事件的文件描述符的个数,若知道是哪个发生了事件,同样需要遍历

                        • epoll返回的发生了事件的个数和结构体数组,结构体包含socket的信息,因此直接处理返回的数组即可

                        • 工作模式

                          • select和poll都只能工作在相对低效的LT模式下

                          • epoll则可以工作在ET高效模式,并且epoll还支持EPOLLONESHOT事件,该事件能进一步减少可读、可写和异常事件被触发的次数。 

                          • 应用场景

                            • 当所有的fd都是活跃连接,使用epoll,需要建立文件系统,红黑书和链表对于此来说,效率反而不高,不如selece和poll

                            • 当监测的fd数目较小,且各个fd都比较活跃,建议使用select或者poll

                            • 当监测的fd数目非常大,成千上万,且单位时间只有其中的一部分fd处于就绪状态,这个时候使用epoll能够明显提升性能。

                              5. LT/ET/EPOLLONESHOT
                              • LT水平触发模式

                                • epoll_wait检测到文件描述符有事件发生,则将其通知给应用程序,应用程序可以不立即处理该事件。

                                • 当下一次调用epoll_wait时,epoll_wait还会再次向应用程序报告此事件,直至被处理

                                • ET边缘触发模式

                                  • epoll_wait检测到文件描述符有事件发生,则将其通知给应用程序,应用程序必须立即处理该事件

                                  • 必须要一次性将数据读取完,使用非阻塞I/O,读取到出现eagain

                                  • EPOLLONESHOT

                                    • 一个线程读取某个socket上的数据后开始处理数据,在处理过程中该socket上又有新数据可读,此时另一个线程被唤醒读取,此时出现两个线程处理同一个socket

                                    • 我们期望的是一个socket连接在任一时刻都只被一个线程处理,通过epoll_ctl对该文件描述符注册epolloneshot事件,一个线程处理socket时,其他线程将无法处理,当该线程处理完后,需要通过epoll_ctl重置epolloneshot事件。

                                      (2)HTTP报文格式

                                              HTTP报文分为请求报文和响应报文两种,每种报文必须按照特有格式生成,才能被浏览器端识别。其中,浏览器端向服务器发送的为请求报文,服务器处理后返回给浏览器端的为响应报文。

                                      1.请求报文

                                               HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据四个部分组成。其中,请求分为两种,GET和POST,具体的:

                                              GET:

                                       1    GET /562f25980001b1b106000338.jpg HTTP/1.1
                                       2    Host:img.mukewang.com
                                       3    User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64)
                                       4    AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
                                       5    Accept:image/webp,image/*,*/*;q=0.8
                                       6    Referer:http://www.imooc.com/
                                       7    Accept-Encoding:gzip, deflate, sdch
                                       8    Accept-Language:zh-CN,zh;q=0.8
                                       9    空行
                                      10    请求数据为空

                                              POST:

                                      1    POST / HTTP1.1
                                      2    Host:www.wrox.com
                                      3    User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2 
                                           .0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
                                      4    Content-Type:application/x-www-form-urlencoded
                                      5    Content-Length:40
                                      6    Connection: Keep-Alive
                                      7    空行
                                      8    name=Professional%20Ajax&publisher=Wiley
                                      • 请求行,用来说明请求类型,要访问的资源以及所使用的HTTP版本。

                                        GET说明请求类型为GET,/562f25980001b1b106000338.jpg(URL)为要访问的资源,该行的最后一部分说明使用的是HTTP1.1版本。

                                      • 请求头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息。

                                        • HOST,给出请求资源所在服务器的域名。

                                        • User-Agent,HTTP客户端程序的信息,该信息由你发出请求使用的浏览器来定义,并且在每个请求中自动发送等。

                                        • Accept,说明用户代理可处理的媒体类型。

                                        • Accept-Encoding,说明用户代理支持的内容编码。

                                        • Accept-Language,说明用户代理能够处理的自然语言集。

                                        • Content-Type,说明实现主体的媒体类型。

                                        • Content-Length,说明实现主体的大小。

                                        • Connection,连接管理,可以是Keep-Alive或close。

                                        • 空行,请求头部后面的空行是必须的即使第四部分的请求数据为空,也必须有空行。

                                        • 请求数据也叫主体,可以添加任意的其他数据。

                                          2.响应报文

                                                   HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

                                          1HTTP/1.1 200 OK
                                           2Date: Fri, 22 May 2009 06:07:21 GMT
                                           3Content-Type: text/html; charset=UTF-8
                                           4空行
                                           5
                                           6      
                                           7      
                                           8            
                                           9      
                                          10
                                          • 状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。

                                            第一行为状态行,(HTTP/1.1)表明HTTP版本为1.1版本,状态码为200,状态消息为OK。

                                          • 消息报头,用来说明客户端要使用的一些附加信息。

                                            第二行和第三行为消息报头,Date:生成响应的日期和时间;Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8。

                                          • 空行,消息报头后面的空行是必须的。

                                          • 响应正文,服务器返回给客户端的文本信息。空行后面的html部分为响应正文。

                                            (3)HTTP状态码

                                                     HTTP有5种类型的状态码,具体的:

                                            • 1xx:指示信息--表示请求已接收,继续处理。

                                            • 2xx:成功--表示请求正常处理完毕。

                                              • 200 OK:客户端请求被正常处理。

                                              • 206 Partial content:客户端进行了范围请求。

                                              • 3xx:重定向--要完成请求必须进行更进一步的操作。

                                                • 301 Moved Permanently:永久重定向,该资源已被永久移动到新位置,将来任何对该资源的访问都要使用本响应返回的若干个URI之一。

                                                • 302 Found:临时重定向,请求的资源现在临时从不同的URI中获得。

                                                • 4xx:客户端错误--请求有语法错误,服务器无法处理请求。

                                                  • 400 Bad Request:请求报文存在语法错误。

                                                  • 403 Forbidden:请求被服务器拒绝。

                                                  • 404 Not Found:请求不存在,服务器上找不到请求的资源。

                                                  • 5xx:服务器端错误--服务器处理请求出错。

                                                    • 500 Internal Server Error:服务器在执行请求时出现错误。

                                                      (4)有限状态机

                                                              有限状态机,是一种抽象的理论模型,它能够把有限个变量描述的状态变化过程,以可构造可验证的方式呈现出来。比如,封闭的有向图。

                                                              有限状态机可以通过if-else,switch-case和函数指针来实现,从软件工程的角度看,主要是为了封装逻辑。

                                                              带有状态转移的有限状态机示例代码。

                                                       1    STATE_MACHINE(){
                                                       2    State cur_State = type_A;
                                                       3    while(cur_State != type_C){
                                                       4        Package _pack = getNewPackage();
                                                       5        switch(){
                                                       6            case type_A:
                                                       7                process_pkg_state_A(_pack);
                                                       8                cur_State = type_B;
                                                       9                break;
                                                      10            case type_B:
                                                      11                process_pkg_state_B(_pack);
                                                      12                cur_State = type_C;
                                                      13                break;
                                                      14        }
                                                      15    }
                                                      16}

                                                              该状态机包含三种状态:type_A,type_B和type_C。其中,type_A是初始状态,type_C是结束状态。

                                                              状态机的当前状态记录在cur_State变量中,逻辑处理时,状态机先通过getNewPackage获取数据包,然后根据当前状态对数据进行处理,处理完后,状态机通过改变cur_State完成状态转移。

                                                              有限状态机一种逻辑单元内部的一种高效编程方法,在服务器编程中,服务器可以根据不同状态或者消息类型进行相应的处理逻辑,使得程序逻辑清晰易懂。

                                                      3.http处理流程

                                                              首先对http报文处理的流程进行简要介绍,然后具体介绍http类的定义和服务器接收http请求的具体过程。

                                                      (1)http报文处理流程
                                                      • 浏览器端发出http连接请求,主线程创建http对象接收请求并将所有数据读入对应buffer,将该对象插入任务队列,工作线程从任务队列中取出一个任务进行处理。(本篇)

                                                      • 工作线程取出任务后,调用process_read函数,通过主、从状态机对请求报文进行解析。(中篇)

                                                      • 解析完之后,跳转do_request函数生成响应报文,通过process_write写入buffer,返回给浏览器端。(下篇)

                                                        (2)http类

                                                                这一部分代码在TinyWebServer/http/http_conn.h中,主要是http类的定义。

                                                         1class http_conn{
                                                          2    public:
                                                          3        //设置读取文件的名称m_real_file大小
                                                          4        static const int FILENAME_LEN=200;
                                                          5        //设置读缓冲区m_read_buf大小
                                                          6        static const int READ_BUFFER_SIZE=2048;
                                                          7        //设置写缓冲区m_write_buf大小
                                                          8        static const int WRITE_BUFFER_SIZE=1024;
                                                          9        //报文的请求方法,本项目只用到GET和POST
                                                         10        enum METHOD{GET=0,POST,HEAD,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATH};
                                                         11        //主状态机的状态
                                                         12        enum  CHECK_STATE{CHECK_STATE_REQUESTLINE=0,
                                                                            CHECK_STATE_HEADER,CHECK_STATE_CONTENT};
                                                         13        //报文解析的结果
                                                         14        enum HTTP_CODE{NO_REQUEST,GET_REQUEST,BAD_REQUEST,   
                                                                        NO_RESOURCE,FORBIDDEN_REQUEST,FILE_REQUEST,
                                                                        INTERNAL_ERROR,CLOSED_CONNECTION};
                                                         15        //从状态机的状态
                                                         16        enum LINE_STATUS{LINE_OK=0,LINE_BAD,LINE_OPEN};
                                                         17
                                                         18    public:
                                                         19        http_conn(){}
                                                         20        ~http_conn(){}
                                                         21
                                                         22    public:
                                                         23        //初始化套接字地址,函数内部会调用私有方法init
                                                         24        void init(int sockfd,const sockaddr_in &addr);
                                                         25        //关闭http连接
                                                         26        void close_conn(bool real_close=true);
                                                         27        void process();
                                                         28        //读取浏览器端发来的全部数据
                                                         29        bool read_once();
                                                         30        //响应报文写入函数
                                                         31        bool write();
                                                         32        sockaddr_in *get_address(){
                                                         33            return &m_address;  
                                                         34        }
                                                         35        //同步线程初始化数据库读取表
                                                         36        void initmysql_result();
                                                         37        //CGI使用线程池初始化数据库表
                                                         38        void initresultFile(connection_pool *connPool);
                                                         39
                                                         40    private:
                                                         41        void init();
                                                         42        //从m_read_buf读取,并处理请求报文
                                                         43        HTTP_CODE process_read();
                                                         44        //向m_write_buf写入响应报文数据
                                                         45        bool process_write(HTTP_CODE ret);
                                                         46        //主状态机解析报文中的请求行数据
                                                         47        HTTP_CODE parse_request_line(char *text);
                                                         48        //主状态机解析报文中的请求头数据
                                                         49        HTTP_CODE parse_headers(char *text);
                                                         50        //主状态机解析报文中的请求内容
                                                         51        HTTP_CODE parse_content(char *text);
                                                         52        //生成响应报文
                                                         53        HTTP_CODE do_request();
                                                         54
                                                         55        //m_start_line是已经解析的字符
                                                         56        //get_line用于将指针向后偏移,指向未处理的字符
                                                         57        char* get_line(){return m_read_buf+m_start_line;};
                                                         58
                                                         59        //从状态机读取一行,分析是请求报文的哪一部分
                                                         60        LINE_STATUS parse_line();
                                                         61
                                                         62        void unmap();
                                                         63
                                                         64        //根据响应报文格式,生成对应8个部分,以下函数均由do_request调用
                                                         65        bool add_response(const char* format,...);
                                                         66        bool add_content(const char* content);
                                                         67        bool add_status_line(int status,const char* title);
                                                         68        bool add_headers(int content_length);
                                                         69        bool add_content_type();
                                                         70        bool add_content_length(int content_length);
                                                         71        bool add_linger();
                                                         72        bool add_blank_line();
                                                         73
                                                         74    public:
                                                         75        static int m_epollfd;
                                                         76        static int m_user_count;
                                                         77        MYSQL *mysql;
                                                         78
                                                         79    private:
                                                         80        int m_sockfd;
                                                         81        sockaddr_in m_address;
                                                         82
                                                         83        //存储读取的请求报文数据
                                                         84        char m_read_buf[READ_BUFFER_SIZE];
                                                         85        //缓冲区中m_read_buf中数据的最后一个字节的下一个位置
                                                         86        int m_read_idx;
                                                         87        //m_read_buf读取的位置m_checked_idx
                                                         88        int m_checked_idx;
                                                         89        //m_read_buf中已经解析的字符个数
                                                         90        int m_start_line;
                                                         91
                                                         92        //存储发出的响应报文数据
                                                         93        char m_write_buf[WRITE_BUFFER_SIZE];
                                                         94        //指示buffer中的长度
                                                         95        int m_write_idx;
                                                         96
                                                         97        //主状态机的状态
                                                         98        CHECK_STATE m_check_state;
                                                         99        //请求方法
                                                        100        METHOD m_method;
                                                        101
                                                        102        //以下为解析请求报文中对应的6个变量
                                                        103        //存储读取文件的名称
                                                        104        char m_real_file[FILENAME_LEN];
                                                        105        char *m_url;
                                                        106        char *m_version;
                                                        107        char *m_host;
                                                        108        int m_content_length;
                                                        109        bool m_linger;
                                                        110
                                                        111        char *m_file_address;        //读取服务器上的文件地址
                                                        112        struct stat m_file_stat;
                                                        113        struct iovec m_iv[2];        //io向量机制iovec
                                                        114        int m_iv_count;
                                                        115        int cgi;                    //是否启用的POST
                                                        116        char *m_string;                //存储请求头数据
                                                        117        int bytes_to_send;          //剩余发送字节数
                                                        118        int bytes_have_send;        //已发送字节数
                                                        119};

                                                                在http请求接收部分,会涉及到init和read_once函数,但init仅仅是对私有成员变量进行初始化,不用过多讲解。

                                                                这里,对read_once进行介绍。read_once读取浏览器端发送来的请求报文,直到无数据可读或对方关闭连接,读取到m_read_buffer中,并更新m_read_idx。

                                                         1  //循环读取客户数据,直到无数据可读或对方关闭连接
                                                         2 bool http_conn::read_once()
                                                         3 {
                                                         4    if(m_read_idx>=READ_BUFFER_SIZE)
                                                         5    {
                                                         6        return false;
                                                         7    }
                                                         8    int bytes_read=0;
                                                         9    while(true)
                                                        10    {
                                                        11        //从套接字接收数据,存储在m_read_buf缓冲区
                                                        12        bytes_read=recv(m_sockfd,m_read_buf+m_read_idx,READ_BUFFER_SIZE- 
                                                                                   m_read_idx,0);
                                                        13        if(bytes_read==-1)
                                                        14        {    
                                                        15            //非阻塞ET模式下,需要一次性将数据读完
                                                        16            if(errno==EAGAIN||errno==EWOULDBLOCK)
                                                        17                break;
                                                        18            return false;
                                                        19        }
                                                        20        else if(bytes_read==0)
                                                        21        {
                                                        22            return false;
                                                        23        }
                                                        24        //修改m_read_idx的读取字节数
                                                        25        m_read_idx+=bytes_read;
                                                        26    }
                                                        27    return true;
                                                        28}
                                                        (3)epoll相关代码

                                                                项目中epoll相关代码部分包括非阻塞模式、内核事件表注册事件、删除事件、重置EPOLLONESHOT事件四种。

                                                                非阻塞模式

                                                        1 //对文件描述符设置非阻塞
                                                        2 int setnonblocking(int fd)
                                                        3 {
                                                        4    int old_option = fcntl(fd, F_GETFL);
                                                        5    int new_option = old_option | O_NONBLOCK;
                                                        6    fcntl(fd, F_SETFL, new_option);
                                                        7    return old_option;
                                                        8}

                                                                内核事件表注册新事件,开启EPOLLONESHOT,针对客户端连接的描述符,listenfd不用开启。

                                                        1  void addfd(int epollfd, int fd, bool one_shot)
                                                         2 {
                                                         3    epoll_event event;
                                                         4    event.data.fd = fd;
                                                         5
                                                         6  #ifdef ET
                                                         7    event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
                                                         8  #endif
                                                         9
                                                        10  #ifdef LT
                                                        11    event.events = EPOLLIN | EPOLLRDHUP;
                                                        12  #endif
                                                        13
                                                        14    if (one_shot)
                                                        15        event.events |= EPOLLONESHOT;
                                                        16    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
                                                        17    setnonblocking(fd);
                                                        18}

                                                                内核事件表删除事件

                                                        1  void removefd(int epollfd, int fd)
                                                        2 {
                                                        3    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, 0);
                                                        4    close(fd);
                                                        5  }

                                                                重置EPOLLONESHOT事件

                                                        1  void modfd(int epollfd, int fd, int ev)
                                                         2{
                                                         3    epoll_event event;
                                                         4    event.data.fd = fd;
                                                         5
                                                         6  #ifdef ET
                                                         7    event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;
                                                         8  #endif
                                                         9
                                                        10  #ifdef LT
                                                        11    event.events = ev | EPOLLONESHOT | EPOLLRDHUP;
                                                        12  #endif
                                                        13
                                                        14    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
                                                        15}
                                                        (4)服务器接收http请求

                                                                浏览器端发出http连接请求,主线程创建http对象接收请求并将所有数据读入对应buffer,将该对象插入任务队列,工作线程从任务队列中取出一个任务进行处理。

                                                                

                                                        1 //创建MAX_FD个http类对象
                                                         2 http_conn* users=new http_conn[MAX_FD];
                                                         3
                                                         4 //创建内核事件表
                                                         5 epoll_event events[MAX_EVENT_NUMBER];
                                                         6 epollfd = epoll_create(5);
                                                         7 assert(epollfd != -1);
                                                         8
                                                         9 //将listenfd放在epoll树上
                                                        10 addfd(epollfd, listenfd, false);
                                                        11
                                                        12 //将上述epollfd赋值给http类对象的m_epollfd属性
                                                        13 http_conn::m_epollfd = epollfd;
                                                        14
                                                        15 while (!stop_server)
                                                        16{
                                                        17    //等待所监控文件描述符上有事件的产生
                                                        18    int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
                                                        19    if (number = MAX_FD)
                                                        41            {
                                                        42                show_error(connfd, "Internal server busy");
                                                        43                continue;
                                                        44            }
                                                        45            users[connfd].init(connfd, client_address);
                                                        46#endif
                                                        47
                                                        48//ET非阻塞边缘触发
                                                        49#ifdef ET
                                                        50            //需要循环接收数据
                                                        51            while (1)
                                                        52            {
                                                        53                int connfd = accept(listenfd, (struct sockaddr *)&client_address, &client_addrlength);
                                                        54                if (connfd = MAX_FD)
                                                        59                {
                                                        60                    show_error(connfd, "Internal server busy");
                                                        61                    break;
                                                        62                }
                                                        63                users[connfd].init(connfd, client_address);
                                                        64            }
                                                        65            continue;
                                                        66#endif
                                                        67        }
                                                        68
                                                        69        //处理异常事件
                                                        70        else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
                                                        71        {
                                                        72            //服务器端关闭连接
                                                        73        }
                                                        74
                                                        75        //处理信号
                                                        76        else if ((sockfd == pipefd[0]) && (events[i].events & EPOLLIN))
                                                        77        {
                                                        78        }
                                                        79
                                                        80        //处理客户连接上接收到的数据
                                                        81        else if (events[i].events & EPOLLIN)
                                                        82        {
                                                        83            //读入对应缓冲区
                                                        84            if (users[sockfd].read_once())
                                                        85            {
                                                        86                //若监测到读事件,将该事件放入请求队列
                                                        87                pool->append(users + sockfd);
                                                        88            }
                                                        89            else
                                                        90            {
                                                        91               //服务器关闭连接
                                                        92            }
                                                        93        }
                                                        94
                                                        95    }
                                                        96}
                                                        
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码