http连接的封装 先前我们写的线程,数据库池或者底层的小工具如locker锁等东西是地基,是骨架,那么http解析部分就是主体,是血肉。这是这个项目最关键的部分。
下面是一张简易的框架图,首先说明一下这个WebServer的本质,WebServer的本质上是⼀个⾼性能⽹络框架 ,它提供了⼀个单服务端(当然也可以扩展为多服务端)与多客户端的⾼效连接框架,但是客户端与服务端连接上以后具体应该做些什么(也就是有哪些业务),这就可以由我们⾃由发挥了,这就是 WebServer 的功能扩展。⽬前⼤多数的WebServer都将从服务端获取MIME 作为主要功能。
 
 
由于变量和参数过多,先不介绍初始化了,先从设计讲起。
http_conn的设计 其中大部分设计都是参考了《Linux高性能服务器编程》
固定的一些方法和变量 定义http响应的一些状态信息
1 2 3 4 5 6 7 8 9 const  char  *ok_200_title = "OK" ;const  char  *error_400_title = "Bad Request" ;const  char  *error_400_form = "Your request has bad syntax or is inherently impossible to staisfy.\n" ;const  char  *error_403_title = "Forbidden" ;const  char  *error_403_form = "You do not have permission to get file form this server.\n" ;const  char  *error_404_title = "Not Found" ;const  char  *error_404_form = "The requested file was not found on this server.\n" ;const  char  *error_500_title = "Internal Error" ;const  char  *error_500_form = "There was an unusual problem serving the request file.\n" ;
 
类内的方法枚举,
1 2 3 4 5 6 7 8 9 10 11 12 enum  METHOD { 	GET = 0 , 	POST, 	HEAD, 	PUT, 	DELETE, 	TRACE, 	OPTIONS, 	CONNECT, 	PATCH }; 
 
分析状态,这些状态是解析请求的不同阶段,所有的完成后我们才能回应响应
1 2 3 4 5 6 enum  CHECK_STATE {     CHECK_STATE_REQUESTLINE = 0 ,      CHECK_STATE_HEADER,      CHECK_STATE_CONTENT  }; 
 
请求结果枚举
1 2 3 4 5 6 7 8 9 10 11 enum  HTTP_CODE {     NO_REQUEST,      GET_REQUEST,      BAD_REQUEST,      NO_RESOURCE,      FORBIDDEN_REQUEST,      FILE_REQUEST,      INTERNAL_ERROR,      CLOSED_CONNECTION  }; 
 
状态行
1 2 3 4 5 6 enum  LINE_STATUS {     LINE_OK = 0 ,     LINE_BAD,     LINE_OPEN }; 
 
一些固定常用方法,具体的实现就不展示了,也可以在《Linux高性能服务器编程》找到
1 2 3 4 5 int  setnonblocking (int  fd)  void  addfd (int  epollfd, int  fd, bool  one_shot, int  TRIGMode)  void  removefd (int  epollfd, int  fd)  void  modfd (int  epollfd, int  fd, int  ev, int  TRIGMode)  void  http_conn::unmap ()  ; 
 
read_once() 设置都有读写缓存区,分别是m_read_buf和m_write_buf
1 2 3 4 5 6 char  m_read_buf[READ_BUFFER_SIZE]{};int  m_read_idx{};int  m_check_idx{};char  m_write_buf[WRITE_BUFFER_SIZE]{};int  m_write_idx{};int  m_start_line{};
 
 
m_read_idx:代表从客户端接受的数据的末尾位置。 
m_check_idx:用来标记当前正在检查的字符的位置。 
m_start_line: 用来标记缓冲区中当前处理的行的起始位置。 
 
read_once这个函数主要完成将数据读到缓冲区,定义了一块缓冲区m_read_buf专门用来存放从浏览器发送来的请求报文,并用一个指针m_read_idx记录,这里分LT、ET模式,在LT模式下,epoll_wait会无数次地通知应用程序读事件的发生,直到应用程序去取。这里的应用程序是什么呢?很显然就是下面这个read_once()代码,在if(m_TRIGMode == 0)程序块里,应用程序用recv去将sockfd内的内容取到m_readbuf里面,如果没有取完,程序是无所谓的,它会继续往下执行,直到下一次epoll_wait再次通知,它便再进行recv操作。
而在ET模式下,epoll_wait只会通知应用程序一次,应用程序被要求在这一次就把sockfd中全部的数据取出,即read_once,可以看到在ET模式下,代码执行一个永不结束的循环while(true),唯有当数据全部取完(即recv返回-1并设置errno为EAGAIN或EWOULDBLOCK)时,程序才会退出,不然程序就一直循环下去。(其实当对方关闭了sock连接也会退出,但这就属于异常处理的流程,而非常规流程了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 bool  http_conn::read_once ()   {  if (m_read_idx>=READ_BUFFER_SIZE){     return  false ;   }   int  read_ret=0 ;   if (m_TRIGMode==0 ){      read_ret= recv (m_sock_fd,m_read_buf+m_read_idx,READ_BUFFER_SIZE-m_read_idx,0 );     if (read_ret<=0 ){       return  false ;     }     m_read_idx+=read_ret;     return  true ;   }   else {      while (true ){       read_ret= recv (m_sock_fd,m_read_buf+m_read_idx,READ_BUFFER_SIZE-m_read_idx,0 );       if (read_ret==-1 ){          if (errno==EAGAIN||errno==EWOULDBLOCK){           break ;         }         return  false ;       }       else  if (read_ret==0 ) return  false ;       m_read_idx+=read_ret;     }     return  true ;   } } 
 
process 这是http最为核心的函数了,这是线程池工作线程不断循环执行的逻辑了
 
主要执行process_read和process_write。
1 2 3 4 5 6 7 8 9 10 11 12 13 void  http_conn::process ()   {  HTTP_CODE read_ret = process_read ();   if (read_ret==NO_REQUEST){     modfd (m_epoll_fd,m_sock_fd,EPOLLIN,m_TRIGMode);     return ;   }   bool  write_ret= process_write (read_ret);   if (!write_ret){     close_conn ();   }   modfd (m_epoll_fd,m_sock_fd,EPOLLOUT,m_TRIGMode); } 
 
process_read process_read用来处理连接,我们设计了两个状态机主状态机/从状态机 来进行报文解析。下图是处理连接的拓扑图: 
从状态机负责读取一行的数据 
当从状态机成功读完一行后,就将这行数据交给主状态机 
主状态机会根据报文格式以及自身状态对该请求行进行解析 
解析完后主状态机的状态改变,切换到下一个状态再等待从状态机的数据循环、 
 
 
从上面的read_once()函数中我们获得了一个字符数组m_read_buf,和一个指针m_read_idx。我们开始读取m_read_buf至今为止保存的内容。这时我们需要创建一个新的指针m_checked_idx来记录每一行报文的结束地址。
下面是process_read流程图,
 
parse_line 因为http报文是按行来分开 不同的信息的,而发送过来的数据要按行处理分出一行的内容,因为报文的行都是以<CR><LF>结束,我们可以通过这个条件让指针m_checked_idx来记录每一行报文的结束地址。当m_checked_idx在一行报文的末尾时,那m_start_line到m_checked_idx就是一行的首尾了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 http_conn::LINE_STATUS http_conn::parse_line ()   {  char  temp;   for (;m_check_idx<m_read_idx;++m_check_idx){     temp=m_read_buf[m_check_idx];     if (temp=='\r' ){       if (m_check_idx+1 ==m_read_idx)         return  LINE_OPEN;       else  if (m_read_buf[m_check_idx+1 ]=='\n' ){         m_read_buf[m_check_idx++]='\0' ;         m_read_buf[m_check_idx++]='\0' ;         return  LINE_OK;       }       return  LINE_BAD;     }     else  if (temp=='\n' ){       if (m_check_idx>1 &&m_read_buf[m_check_idx-1 ]=='\r' ){         m_read_buf[m_check_idx-1 ]='\0' ;         m_read_buf[m_check_idx++]='\0' ;         return  LINE_OK;       }       return  LINE_BAD;     }   }   return  LINE_OPEN; } 
 
而get_line就是一行内容了,因为我们在末尾都赋值了\0
1 2 3 text = get_line (); char  *get_line ()   {return  m_read_buf+m_start_line;};ret = parse_request_line (text); 
 
parse_request_line 1 2 3 4 METHOD m_method;  char  *m_url{};  char  *m_version{}; int  cgi{}; 
 
这个函数主要用来解析请求行 比如GET http://www.baidu.com/index.html HTTP/1.0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 http_conn::HTTP_CODE http_conn::parse_request_line (char  *text)   {  m_url = strpbrk (text, " \t" );   if  (!m_url)   {     return  BAD_REQUEST;   }   *m_url++ = '\0' ;   char  *method = text;   if  (strcasecmp (method, "GET" ) == 0 )     m_method = GET;   else  if  (strcasecmp (method, "POST" ) == 0 )   {     m_method = POST;     cgi = 1 ;   }   else      return  BAD_REQUEST;   m_url += strspn (m_url, " \t" );    m_version = strpbrk (m_url, " \t" );   if  (!m_version)     return  BAD_REQUEST;   *m_version++ = '\0' ;   m_version += strspn (m_version, " \t" );   if  (strcasecmp (m_version, "HTTP/1.1" ) != 0 )     return  BAD_REQUEST;   if  (strncasecmp (m_url, "http://" , 7 ) == 0 )   {     m_url += 7 ;     m_url = strchr (m_url, '/' );   }   if  (strncasecmp (m_url, "https://" , 8 ) == 0 )   {     m_url += 8 ;     m_url = strchr (m_url, '/' );   }   if  (!m_url || m_url[0 ] != '/' )     return  BAD_REQUEST;      if  (strlen (m_url) == 1 )     strcat (m_url, "judge.html" );   m_check_state = CHECK_STATE_HEADER;   return  NO_REQUEST; } 
 
解析完请求行,我们再来解析请求头部,根据报文格式,以及头部字段名的种类,我们有下面的代码
1 2 3 bool  m_linger{}; long  m_content_length{};char  *m_host{};
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 http_conn::HTTP_CODE http_conn::parse_headers (char  *text)   {  if  (text[0 ] == '\0' )   {     if  (m_content_length != 0 )     {       m_check_state = CHECK_STATE_CONTENT;       return  NO_REQUEST;     }     return  GET_REQUEST;   }   else  if  (strncasecmp (text, "Connection:" , 11 ) == 0 )   {     text += 11 ;     text += strspn (text, " \t" );     if  (strcasecmp (text, "keep-alive" ) == 0 )     {       m_linger = true ;     }   }   else  if  (strncasecmp (text, "Content-length:" , 15 ) == 0 )   {     text += 15 ;     text += strspn (text, " \t" );     m_content_length = atol (text);   }   else  if  (strncasecmp (text, "Host:" , 5 ) == 0 )   {     text += 5 ;     text += strspn (text, " \t" );     m_host = text;   }   else    {     LOG_INFO ("oop!unknown header: %s" , text)   }   return  NO_REQUEST; 
 
parse_content 如果时post请求还要解析请求体
 
1 2 3 4 5 6 7 8 9 http_conn::HTTP_CODE http_conn::parse_content (char  *text)   {  if (m_read_idx>=(m_content_length+m_check_idx)){     text[m_content_length]='\0' ;          m_string=text;     return  GET_REQUEST;   }   return  NO_REQUEST; } 
 
process_read 在POST请求报文中,因为消息体结尾没有 \r\n ,所以不会触发parse_line()解析,所以我们只能根据主状态机进行条件判断进入循环。但这会有个问题,等POST请求报文全部解析完后,m_check_state依然是CHECK_STATE_CONTENT,还是不会退出循环。这不是我们所希望的,所以我们让它加上line_status == LINE_OK,这样当POST消息体全部解析完后,line_status会被赋值为LINE_OPEN,就不再进入主循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 http_conn::HTTP_CODE http_conn::process_read ()   {  LINE_STATUS line_status=LINE_OK;   HTTP_CODE ret=NO_REQUEST;   char  *text=nullptr ;   while  ((m_check_state == CHECK_STATE_CONTENT && line_status == LINE_OK) || ((line_status = parse_line ()) == LINE_OK))   {     text = get_line ();     m_start_line = m_check_idx;     LOG_INFO ("%s" , text)     switch  (m_check_state)     {       case  CHECK_STATE_REQUESTLINE:       {         ret = parse_request_line (text);         if  (ret == BAD_REQUEST)           return  BAD_REQUEST;         break ;       }       case  CHECK_STATE_HEADER:       {         ret = parse_headers (text);         if  (ret == BAD_REQUEST)           return  BAD_REQUEST;         else  if  (ret == GET_REQUEST)         {           return  do_request ();         }         break ;       }       case  CHECK_STATE_CONTENT:       {         ret = parse_content (text);         if  (ret == GET_REQUEST)           return  do_request ();          line_status = LINE_OPEN;         break ;       }       default :         return  INTERNAL_ERROR;     }   }   return  NO_REQUEST; } 
 
do_request 因为客户端主要从服务端获取MIME 作为主要功能,客户发起了一个图片的请求http://xxx/images/pic.jpg,在服务端来说这是一个链接,在服务端那边就是一个请求资源文件的路径,服务端根据请求的URL路径,去其文件系统中寻找对应的文件。如果文件存在,服务端将继续处理;如果文件不存在,则通常返回404错误(资源未找到)。
do_request()主要目的是解析HTTP请求URL,根据不同的路径(URL中的部分内容)来执行不同的操作,例如处理CGI请求(如登录、注册等),处理静态文件请求等。
1 2 3 char  m_real_file[FILENAME_LEN]{};char * doc_root{}; char  *m_file_address{}; 
 
其实本质上这个函数是为了得到m_real_file,资源文件的路径,得到m_real_file后映射文件内容到内存 ,这样操作系统可以利用虚拟内存系统来访问文件,文件的读取和写入就像访问普通内存一样高效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 http_conn::HTTP_CODE http_conn::do_request ()   {  strcpy (m_real_file,doc_root);   int  len= strlen (doc_root);   const  char  *p= strrchr (m_url,'/' );      if  (cgi == 1  && (*(p + 1 ) == '2'  || *(p + 1 ) == '3' ))    {          char  flag = m_url[1 ];      char  *m_url_real = (char  *)malloc (sizeof (char ) * 200 );     strcpy (m_url_real, "/" );     strcat (m_url_real, m_url + 2 );     strncpy (m_real_file + len, m_url_real, FILENAME_LEN - len - 1 );     free (m_url_real);               char  name[100 ], password[100 ];     int  i;     for  (i = 5 ; m_string[i] != '&' ; ++i)       name[i - 5 ] = m_string[i];     name[i - 5 ] = '\0' ;     int  j = 0 ;     for  (i = i + 10 ; m_string[i] != '\0' ; ++i, ++j)       password[j] = m_string[i];     password[j] = '\0' ;     if  (*(p + 1 ) == '3' )     {                     char  *sql_insert = (char  *)malloc (sizeof (char ) * 200 );       snprintf (sql_insert, strlen (sql_insert), "INSERT INTO user(username, passwd) VALUES('%s', '%s')" ,name,password);       m_lock.lock ();       if  (users.find (name) == users.end ())       {         int  res = mysql_query (mysql, sql_insert);         users.insert (pair <string, string>(name, password));         if  (!res)           strcpy (m_url, "/log.html" );         else            strcpy (m_url, "/registerError.html" );       }       else          strcpy (m_url, "/registerError.html" );       m_lock.unlock ();     }               else  if  (*(p + 1 ) == '2' )     {       if  (users.find (name) != users.end () && users[name] == password)         strcpy (m_url, "/welcome.html" );       else          strcpy (m_url, "/logError.html" );     }   }   if  (*(p + 1 ) == '0' )   {     char  *m_url_real = (char  *)malloc (sizeof (char ) * 200 );     strcpy (m_url_real, "/register.html" );     strncpy (m_real_file + len, m_url_real, strlen (m_url_real));     free (m_url_real);   }   else  if  (*(p + 1 ) == '1' )   {     char  *m_url_real = (char  *)malloc (sizeof (char ) * 200 );     strcpy (m_url_real, "/log.html" );     strncpy (m_real_file + len, m_url_real, strlen (m_url_real));     free (m_url_real);   }   else  if  (*(p + 1 ) == '5' )   {     char  *m_url_real = (char  *)malloc (sizeof (char ) * 200 );     strcpy (m_url_real, "/picture.html" );     strncpy (m_real_file + len, m_url_real, strlen (m_url_real));     free (m_url_real);   }   else  if  (*(p + 1 ) == '6' )   {     char  *m_url_real = (char  *)malloc (sizeof (char ) * 200 );     strcpy (m_url_real, "/video.html" );     strncpy (m_real_file + len, m_url_real, strlen (m_url_real));     free (m_url_real);   }   else  if  (*(p + 1 ) == '7' )   {     char  *m_url_real = (char  *)malloc (sizeof (char ) * 200 );     strcpy (m_url_real, "/fans.html" );     strncpy (m_real_file + len, m_url_real, strlen (m_url_real));     free (m_url_real);   }   else    {     strncpy (m_real_file + len, m_url, FILENAME_LEN - len - 1 );   }   if  (stat (m_real_file, &m_file_stat) < 0 )    {     return  NO_RESOURCE;   }   if  (!(m_file_stat.st_mode & S_IROTH))       return  FORBIDDEN_REQUEST;   if  (S_ISDIR (m_file_stat.st_mode))      return  BAD_REQUEST;   int  fd = open (m_real_file, O_RDONLY);   m_file_address = (char  *)mmap (nullptr , m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0 );   close (fd);   return  FILE_REQUEST; } 
 
process_write 这个process_write主要来根据处理HTTP请求的结果构建相应的HTTP响应并准备发送数据的。
HTTP应答的部分内容如下:
1 2 3 4 5 6 HTTP/1.0  200  OK Server:BWS/1.0  Content-Length:8024  Content-Type:text/html;charset=gbk Set-Cookie:BAIDUID=A5B6C72D68CF639CE8896FD79A03FBD8:FG=1 ;expires=Wed,04 -Jul-42  00 :10 :47  GMT;path=/;domain=.baidu.com Via:1.0  localhost(squid/3.0  STABLE18) 
 
add_response 这里定义一个基础的往HTTP响应的缓冲区 添加格式化的数据的函数,并且使用可变参数va_list增加它的可复用性。这样我们后面的各种写数据就可以直接调用add_response了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 bool  http_conn::add_response (const  char  *format, ...)   {  if  (m_write_idx >= WRITE_BUFFER_SIZE)     return  false ;   va_list arg_list;   va_start (arg_list, format);   int  len = vsnprintf (m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - 1  - m_write_idx, format, arg_list);   if  (len >= (WRITE_BUFFER_SIZE - 1  - m_write_idx))   {     va_end (arg_list);     return  false ;   }   m_write_idx += len;   va_end (arg_list);   LOG_INFO ("request:%s" , m_write_buf)   return  true ; } 
 
调用add_response的函数系列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 bool  http_conn::add_status_line (int  status, const  char  *title)   {    return  add_response ("%s %d %s\r\n" , "HTTP/1.1" , status, title); }   bool  http_conn::add_headers (int  content_len)   {    return  add_content_length (content_len) && add_linger () && add_blank_line (); } bool  http_conn::add_content_length (int  content_len)   {    return  add_response ("Content-Length:%d\r\n" , content_len); } bool  http_conn::add_content_type ()   {    return  add_response ("Content-Type:%s\r\n" , "text/html" ); } bool  http_conn::add_linger ()   {    return  add_response ("Connection:%s\r\n" ,(m_linger == true ) ? "keep-alive"  : "close" ); } bool  http_conn::add_blank_line ()   {    return  add_response ("%s" , "\r\n" ); } bool  http_conn::add_content (const  char  *content)   {    return  add_response ("%s" , content); } 
 
process_write 1 2 3 struct  stat  m_file_stat{}; struct  iovec  m_iv[2 ]{}; int  m_iv_count{}; 
 
如果是FILE_REQUEST,我们要发两段信息,用了iovec,因为后面write用writev,一段是用于写缓冲区的给客户端的响应,一段是文件内容,是客户端请求的资源文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 bool  http_conn::process_write (http_conn::HTTP_CODE ret)   {  switch  (ret)   {     case  INTERNAL_ERROR:     {       add_status_line (500 , error_500_title);       add_headers (strlen (error_500_form));       if  (!add_content (error_500_form))         return  false ;       break ;     }     case  BAD_REQUEST:     {       add_status_line (404 , error_404_title);       add_headers (strlen (error_404_form));       if  (!add_content (error_404_form))         return  false ;       break ;     }     case  FORBIDDEN_REQUEST:     {       add_status_line (403 , error_403_title);       add_headers (strlen (error_403_form));       if  (!add_content (error_403_form))         return  false ;       break ;     }     case  FILE_REQUEST:     {       add_status_line (200 , ok_200_title);       if  (m_file_stat.st_size != 0 )       {         add_headers (m_file_stat.st_size);         m_iv[0 ].iov_base = m_write_buf;         m_iv[0 ].iov_len = m_write_idx;         m_iv[1 ].iov_base = m_file_address;         m_iv[1 ].iov_len = m_file_stat.st_size;         m_iv_count = 2 ;         bytes_to_send = m_write_idx + m_file_stat.st_size;         return  true ;       }       else        {         const  char  *ok_string = "<html><body></body></html>" ;         add_headers (strlen (ok_string));         if  (!add_content (ok_string))           return  false ;       }     }     default :{       return  false ;     }   }   m_iv[0 ].iov_base = m_write_buf;   m_iv[0 ].iov_len = m_write_idx;   m_iv_count = 1 ;   bytes_to_send = m_write_idx;   return  true ; } 
 
write 在写缓冲区写满要发送的数据后,我们最后调用write将其发送给浏览器客户端,如果保持连接发送完了数据后要重置各参数不返回false
1 2 int  bytes_to_send{};int  bytes_has_send{};
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 bool  http_conn::write ()   {  int  temp = 0 ;   if  (bytes_to_send == 0 )    {     modfd (m_epoll_fd, m_sock_fd, EPOLLIN, m_TRIGMode);     reset ();     return  true ;   }   while  (true )   {     temp = writev (m_sock_fd, m_iv, m_iv_count);     if  (temp < 0 )     {       if  (errno == EAGAIN)        {         modfd (m_epoll_fd, m_sock_fd, EPOLLOUT, m_TRIGMode);         return  true ;       }       unmap ();       return  false ;     }     bytes_has_send += temp;     bytes_to_send -= temp;     if  (bytes_has_send >= m_iv[0 ].iov_len)     {       m_iv[0 ].iov_len = 0 ;       m_iv[1 ].iov_base = m_file_address + (bytes_has_send - m_write_idx);       m_iv[1 ].iov_len = bytes_to_send;     }     else      {       m_iv[0 ].iov_base = m_write_buf + bytes_has_send;       m_iv[0 ].iov_len = m_iv[0 ].iov_len - bytes_has_send;     }     if  (bytes_to_send <= 0 )     {       unmap ();       modfd (m_epoll_fd, m_sock_fd, EPOLLIN, m_TRIGMode);       if  (m_linger)       {         reset ();         return  true ;       }       else        {         return  false ;       }     }   } } 
 
初始化工作 现在列出类中的各种参数,这样就很清晰每个参数的使用途径了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public :  static  int  m_epoll_fd;   static  int  m_user_count;   MYSQL *mysql{};   int  m_state{};  private :  int  m_sock_fd{};   sockaddr_in m_address{};   int  m_TRIGMode{};    char  *m_file_address{};    char  m_read_buf[READ_BUFFER_SIZE]{};   int  m_read_idx{};   int  m_check_idx{};   char  m_write_buf[WRITE_BUFFER_SIZE]{};   int  m_write_idx{};   int  m_start_line{};   char  m_real_file[FILENAME_LEN]{};   char * doc_root{};    CHECK_STATE m_check_state;   METHOD m_method;   char  *m_url{};   char  *m_version{};   char  *m_host{};   struct  stat  m_file_stat{};    struct  iovec  m_iv[2 ]{};    int  m_iv_count{};    int  cgi{};    char * m_string{};    int  m_close_log{};   char  sql_user[100 ]{};   char  sql_password[100 ]{};   char  sql_name[100 ]{};   int  bytes_to_send{};   int  bytes_has_send{};      map<string,string>m_users;   bool  m_linger{};    long  m_content_length{}; 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void  http_conn::init (int  sockfd, const  sockaddr_in &addr, char  *root, int  TRIGMode,int  close_log, string user, string passwd, string sqlname)   {  m_sock_fd=sockfd;   m_TRIGMode=TRIGMode;   m_address=addr;   m_close_log=close_log;   addfd (m_epoll_fd,m_sock_fd,true ,TRIGMode);   m_user_count++;      doc_root=root;   strcpy (sql_user,user.c_str ());   strcpy (sql_password,passwd.c_str ());   strcpy (sql_name,sqlname.c_str ());   reset ();  } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 void  http_conn::reset ()   {   mysql= nullptr ;   m_state=0 ;   cgi=0 ;   m_file_address= nullptr ;   m_read_idx=0 ;   m_check_idx=0 ;   m_start_line=0 ;   m_write_idx=0 ;   m_check_state=CHECK_STATE_REQUESTLINE;   m_method=GET;   m_url= nullptr ;   m_version= nullptr ;   m_host= nullptr ;   m_string= nullptr ;   bytes_to_send=0 ;   bytes_has_send=0 ;   m_content_length=0 ;   m_linger=false ;   timer_flag=0 ;   improv=0 ;   memset (m_read_buf,'\0' ,READ_BUFFER_SIZE);   memset (m_write_buf,'\0' ,WRITE_BUFFER_SIZE);   memset (m_real_file,'\0' ,FILENAME_LEN); }