基于epoll的简单的http服务器

http://blog.csdn.net/fangjian1204/article/details/34415651

该http服务器已经可以处理并发连接,支持多个客户端并发访问,每个连接可以持续读写数据,当然,这只是一个简单的学习例子,还有很多bug,发表出来只是希望大家可以互相学习,我也在不断的改进,希望大家有什么意见可以多多指点,谢谢

server.h

[cpp] view plaincopy

  1. /*
  2.  * server.h
  3.  *
  4.  *  Created on: Jun 23, 2014
  5.  *      Author: fangjian
  6.  */
  7. #ifndef SERVER_H_
  8. #define SERVER_H_
  9. #include "epoll_event.h"
  10. struct web_event_t;  
  11. struct web_connection_t  
  12. {  
  13. int fd;  
  14. int state;//当前处理到哪个阶段
  15. struct web_event_t* read_event;  
  16. struct web_event_t* write_event;  
  17. char* querybuf;  
  18. int query_start_index;//请求数据的当前指针
  19. int query_end_index;//请求数据的下一个位置
  20. int query_remain_len;//可用空间
  21. char method[8];  
  22. char uri[128];  
  23. char version[16];  
  24. char host[128];  
  25. char accept[128];  
  26. char conn[20];  
  27. };  
  28. struct server  
  29. {  
  30. int epollfd;  
  31. };  
  32. void web_epoll_ctl(int epollfd,int ctl,int fd,int flag);  
  33. int setnonblocking(int fd);  
  34. void initConnection(web_connection_t* &conn);  
  35. void web_accept(struct web_connection_t* conn);  
  36. void read_request( struct web_connection_t* conn );  
  37. void process_request_line(struct web_connection_t* conn);  
  38. void process_head(struct web_connection_t* conn);  
  39. void process_body(struct web_connection_t* conn);  
  40. void send_response(struct web_connection_t* conn);  
  41. void try_to_enlarge_buffer(struct web_connection_t& conn);  
  42. void empty_event_handler(struct web_connection_t* conn);  
  43. void close_conn( struct web_connection_t* conn );  
  44. #endif /* SERVER_H_ */

server.cpp

[cpp] view plaincopy

  1. /*
  2.  * server.cpp
  3.  *
  4.  *  Created on: Jun 23, 2014
  5.  *      Author: fangjian
  6.  */
  7. #include "server.h"
  8. #include "epoll_event.h"
  9. #include <stdio.h>
  10. #include <netinet/in.h>
  11. #include <arpa/inet.h>
  12. #include <string.h>
  13. #include <stdlib.h>
  14. #include <fcntl.h>
  15. #include<signal.h>
  16. #include <sys/socket.h>
  17. #include <sys/epoll.h>
  18. #include <sys/stat.h>
  19. #include <sys/sendfile.h>
  20. #include <iostream>
  21. using namespace std;  
  22. int main(int argc,char* argv[])  
  23. {  
  24. const char* ip = "127.0.0.1";  
  25. int port =  8083;  
  26.     signal(SIGPIPE,SIG_IGN);//原因:http://blog.sina.com.cn/s/blog_502d765f0100kopn.html
  27. int listenfd = socket(AF_INET,SOCK_STREAM,0);  
  28. struct sockaddr_in address;  
  29.     bzero(&address,sizeof(address));  
  30.     address.sin_family = AF_INET;  
  31.     inet_pton(AF_INET,ip,&address.sin_addr);  
  32.     address.sin_port = htons(port);  
  33.     bind(listenfd,(struct sockaddr*)&address,sizeof(address));  
  34.     listen(listenfd,50);  
  35.     web_connection_t* conn = NULL;  
  36.     epoll_init_event(conn);  
  37.     initConnection(conn);//创建一个用于接受连接的结构体
  38. if(conn == NULL){printf("---创建监听结构体失败---\n");return -1;};//创建监听结构体
  39.     conn->fd = listenfd;  
  40.     conn->read_event->handler = web_accept;  
  41.     epoll_add_event(conn,EPOLLIN | EPOLLERR);  
  42.     setnonblocking(listenfd);  
  43.     fork();  
  44.     ngx_epoll_process_events();//进入事件循环,等待事件到达
  45. }  
  46. void initConnection(web_connection_t* &conn)  
  47. {  
  48.     conn = (web_connection_t*)malloc(sizeof(web_connection_t));  
  49.     conn->read_event = (web_event_t*)malloc(sizeof(web_event_t));  
  50.     conn->write_event = (web_event_t*)malloc(sizeof(web_event_t));  
  51.     conn->state = ACCEPT;  
  52.     conn->querybuf = (char*)malloc(QUERY_INIT_LEN);  
  53. if(!conn->querybuf)  
  54.     {  
  55.         printf(" malloc error\n");  
  56. return;  
  57.     }  
  58.     conn->query_start_index = 0;  
  59.     conn->query_end_index = 0;  
  60.     conn->query_remain_len = QUERY_INIT_LEN;  
  61. }  
  62. int setnonblocking(int fd)  
  63. {  
  64. int old_option = fcntl(fd,F_GETFL);  
  65. int new_option = old_option | O_NONBLOCK;  
  66.     fcntl(fd,F_SETFL,new_option);  
  67. return old_option;  
  68. }  
  69. void web_accept(web_connection_t* conn)  
  70. {  
  71.     printf("-----------accept-------\n");  
  72. struct sockaddr * client_address;  
  73.     socklen_t client_addrlength = sizeof(client_address);  
  74. int connfd = accept(conn->fd,(struct sockaddr*)&(client_address),&client_addrlength);  
  75. if(connfd == -1)  
  76.     {  
  77.         printf("accept error\n");  
  78. return;  
  79.     }  
  80.     web_connection_t* new_conn = NULL;  
  81.     initConnection(new_conn);//创建一个新的连接结构体
  82. if(new_conn == NULL){printf("---创建连接结构体失败---\n");return;};  
  83.     new_conn->fd = connfd;  
  84.     new_conn->state = READ;  
  85.     new_conn->read_event->handler = read_request;  
  86.     epoll_add_event(new_conn,EPOLLIN | EPOLLERR);  
  87.     setnonblocking(connfd);  
  88. }  
  89. void read_request( struct web_connection_t* conn )  
  90. {  
  91.     printf("-----------read_begin-------\n");  
  92. int len,fd = conn->fd;  
  93. while(true)  
  94.     {  
  95. /* 尝试增加缓冲区空间 */
  96.         try_to_enlarge_buffer(*conn);  
  97.         len= recv(fd,conn->querybuf + conn->query_end_index,conn->query_remain_len,0);  
  98. if(len < 0)  
  99.         {  
  100.             printf("----数据读取完毕-----\n");  
  101. break;//表示当前数据读取完毕,不是出错
  102.         }  
  103. else if(len > 0)  
  104.         {  
  105.             conn->query_end_index += len;  
  106.             conn->query_remain_len-= len;  
  107.         }  
  108. else if(len == 0)  
  109.         {  
  110.             printf("----连接关闭-----\n");  
  111.             epoll_del_event(conn);  
  112.             close_conn(conn );  
  113. return ;  
  114.         }  
  115.     }  
  116.     cout << "-----客户端的内容是 " << endl;  
  117.     cout << conn->querybuf << endl;  
  118.     process_request_line(conn);  
  119. return ;  
  120. }  
  121. void process_request_line(struct web_connection_t* conn)  
  122. {  
  123. int len;  
  124. char* ptr = strpbrk(conn->querybuf + conn->query_start_index," \t");  
  125. if( !ptr)  
  126.     {  
  127.         printf("请求行解析失败\n");  
  128. return;  
  129.     }  
  130.     len = ptr - conn->querybuf - conn->query_start_index;  
  131.     strncpy(conn->method,conn->querybuf + conn->query_start_index,len);  
  132.     cout <<"metnod="<<conn->method<<endl;  
  133.     conn->query_start_index += (len+1);  
  134.     ptr = strpbrk(conn->querybuf + conn->query_start_index," \t");  
  135. if( !ptr)  
  136.     {  
  137.         printf("请求行解析失败\n");  
  138. return;  
  139.     }  
  140.     len = ptr - conn->querybuf - conn->query_start_index;  
  141.     strncpy(conn->uri,conn->querybuf + conn->query_start_index,len);  
  142.     cout << "uri="<<conn->uri<<endl;  
  143.     conn->query_start_index += (len+1);  
  144.     ptr = strpbrk(conn->querybuf,"\n");//先是回车\r,再是换行\n
  145. if(!ptr)  
  146.     {  
  147.         printf("请求行解析失败\n");  
  148. return;  
  149.     }  
  150.     len = ptr - conn->querybuf - conn->query_start_index;  
  151.     strncpy(conn->version,conn->querybuf + conn->query_start_index,len);  
  152.     cout << "version="<<conn->version<<endl;  
  153.     conn->query_start_index += (len+1);  
  154.     cout <<"-----请求行解析完毕----------"<<endl;  
  155.     process_head(conn);  
  156. }  
  157. void process_head(struct web_connection_t* conn)  
  158. {  
  159.     cout << "-------开始解析首部------" << endl;  
  160. char* end_line;  
  161. int len;  
  162. while(true)  
  163.     {  
  164.         end_line = strpbrk(conn->querybuf + conn->query_start_index,"\n");  
  165.         len = end_line - conn->querybuf - conn->query_start_index;  
  166. if(len == 1)  
  167.         {  
  168.             printf("解析完毕\n");  
  169.             conn->query_start_index += (len +1);  
  170.             cout << conn->querybuf + conn->query_start_index << endl;  
  171. break;  
  172.         }  
  173. else
  174.         {  
  175. if(strncasecmp(conn->querybuf+conn->query_start_index,"Host:",5) == 0)  
  176.             {  
  177.                 strncpy(conn->host,conn->querybuf+conn->query_start_index + 6,len-6);  
  178.                 cout << "host="<<conn->host<<endl;  
  179.             }  
  180. else if(strncasecmp(conn->querybuf+conn->query_start_index,"Accept:",7) == 0)  
  181.             {  
  182.                 strncpy(conn->accept,conn->querybuf+conn->query_start_index + 8,len-8);  
  183.                 cout <<"accept="<<conn->accept <<endl;  
  184.             }  
  185. else if(strncasecmp(conn->querybuf+conn->query_start_index,"Connection:",11) == 0)  
  186.             {  
  187.                 strncpy(conn->conn,conn->querybuf+conn->query_start_index + 12,len-12);  
  188.                 cout <<"connection="<<conn->conn <<endl;  
  189.             }  
  190. else
  191.             {  
  192.             }  
  193.             conn->query_start_index += (len +1);  
  194.         }  
  195.     }  
  196.     process_body(conn);  
  197.     printf("----首部解析完毕----------\n");  
  198. }  
  199. void process_body(struct web_connection_t* conn)  
  200. {  
  201. if(conn->query_start_index == conn->query_end_index)  
  202.     {  
  203.         printf("---包体为空----\n");  
  204.     }  
  205. else
  206.     {  
  207.         printf("---丢体包体-----\n");  
  208.     }  
  209.     conn->query_start_index = conn->query_end_index = 0;  
  210.     conn->state = SEND_DATA;  
  211.     conn->write_event->handler = send_response;  
  212.     conn->read_event->handler = empty_event_handler;//读事件回调函数设置为空
  213.     epoll_mod_event(conn,EPOLLOUT | EPOLLERR);  
  214. }  
  215. void send_response(struct web_connection_t* conn)  
  216. {  
  217. char path[128] = "http";//根目录下的文件夹
  218. int len = strlen(conn->uri);  
  219.     memcpy(path+4,conn->uri,len);  
  220.     len += 4;  
  221.     path[len] = '\0';//很重要
  222. int filefd = open(path,O_RDONLY);  
  223. if(filefd < 0)  
  224.     {  
  225.         cout << "无法打开该文件" <<endl;  
  226. return ;  
  227.     }  
  228. struct stat stat_buf;  
  229.     fstat(filefd,&stat_buf);  
  230.     sendfile(conn->fd,filefd,NULL,stat_buf.st_size);  
  231.     close(filefd);  
  232. //close(conn->fd);//如果不关闭该连接socket,则浏览器一直在加载,如何解决,保持keep-alive?
  233.     conn->state = READ;  
  234.     conn->read_event->handler = read_request;  
  235.     epoll_mod_event(conn,EPOLLIN | EPOLLERR);  
  236. //sleep(2);
  237. }  
  238. void try_to_enlarge_buffer(struct web_connection_t& conn)  
  239. {  
  240. if(conn.query_remain_len  < REMAIN_BUFFER)  
  241.     {  
  242. int new_size = strlen(conn.querybuf) + QUERY_INIT_LEN;  
  243.         conn.querybuf = (char*)realloc(conn.querybuf,new_size);  
  244.         conn.query_remain_len  = new_size - conn.query_end_index;  
  245.     }  
  246. }  
  247. void empty_event_handler(struct web_connection_t* conn)  
  248. {  
  249. }  
  250. //关闭一个连接
  251. void close_conn( struct web_connection_t* conn )  
  252. {  
  253. static int count = 0;  
  254.     count ++;  
  255.     printf("关闭第%d个连接\n",count);  
  256.     close( conn->fd);  
  257.     free(conn->querybuf);  
  258.     free(conn->read_event);  
  259.     free(conn->write_event);  
  260.     free(conn);  
  261. }  

epoll_event.h

[cpp] view plaincopy

  1. /*
  2.  * event.h
  3.  *
  4.  *  Created on: Jun 25, 2014
  5.  *      Author: fangjian
  6.  */
  7. #ifndef EVENT_H_
  8. #define EVENT_H_
  9. #include <netinet/in.h>
  10. #include "server.h"
  11. #define MAX_EVENT_NUMBER 10000
  12. #define QUERY_INIT_LEN  1024
  13. #define REMAIN_BUFFER  512
  14. /* 以下是处理机的状态 */
  15. #define ACCEPT 1
  16. #define READ 2
  17. #define QUERY_LINE 4
  18. #define QUERY_HEAD 8
  19. #define QUERY_BODY 16
  20. #define SEND_DATA 32
  21. struct web_connection_t;  
  22. typedef void (*event_handler_pt)(web_connection_t* conn);  
  23. //每一个事件都由web_event_t结构体来表示
  24. struct web_event_t  
  25. {  
  26. /*为1时表示事件是可写的,通常情况下,它表示对应的TCP连接目前状态是可写的,也就是连接处于可以发送网络包的状态*/
  27.     unsigned         write:1;  
  28. /*为1时表示此事件可以建立新的连接,通常情况下,在ngx_cycle_t中的listening动态数组中,每一个监听对象ngx_listening_t对应的读事件中
  29.     的accept标志位才会是1*/
  30.     unsigned         accept:1;  
  31. //为1时表示当前事件是活跃的,这个状态对应着事件驱动模块处理方式的不同,例如:在添加事件、删除事件和处理事件时,该标志位的不同都会对应着不同的处理方式
  32.     unsigned         active:1;  
  33.     unsigned         oneshot:1;  
  34.     unsigned         eof:1;//为1时表示当前处理的字符流已经结束
  35.     unsigned         error:1;//为1时表示事件处理过程中出现了错误
  36.     event_handler_pt  handler;//事件处理方法,每个消费者模块都是重新实现它
  37.     unsigned         closed:1;//为1时表示当前事件已经关闭
  38. };  
  39. void epoll_init_event(web_connection_t* &conn);  
  40. void epoll_add_event(web_connection_t* conn,int flag);  
  41. void epoll_mod_event(web_connection_t* conn,int flag);  
  42. void epoll_del_event(web_connection_t* conn);  
  43. int ngx_epoll_process_events();  
  44. #endif /* EVENT_H_ */

epoll_event.cpp

[cpp] view plaincopy

  1. /*
  2.  * event.cpp
  3.  *
  4.  *  Created on: Jun 25, 2014
  5.  *      Author: fangjian
  6.  */
  7. #include "epoll_event.h"
  8. #include <sys/epoll.h>
  9. #include <unistd.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. static int  ep = -1;//epoll对象的描述符,每个进程只有一个
  13. void epoll_init_event(web_connection_t* &conn)  
  14. {  
  15.     ep = epoll_create(1024);  
  16. }  
  17. /* 添加事件,conn已经设置好回调函数和fd了 */
  18. void epoll_add_event(web_connection_t* conn,int flag)  
  19. {  
  20.     epoll_event ee;  
  21. int fd = conn->fd;  
  22.     ee.data.ptr = (void*)conn;  
  23.     ee.events = flag;  
  24.     epoll_ctl(ep,EPOLL_CTL_ADD,fd,&ee);  
  25. }  
  26. /* 修改事件,event已经设置好回调函数和fd了 */
  27. void epoll_mod_event(web_connection_t* conn,int flag)  
  28. {  
  29.     epoll_event ee;  
  30. int fd = conn->fd;  
  31.     ee.data.ptr = (void*)conn;  
  32.     ee.events = flag;  
  33.     epoll_ctl(ep,EPOLL_CTL_MOD,fd,&ee);  
  34. }  
  35. //删除该描述符上的所有事件,若想只删除读事件或写事件,则把相应的事件设置为空函数
  36. void epoll_del_event(web_connection_t* conn)  
  37. {  
  38.     epoll_ctl( ep, EPOLL_CTL_DEL, conn->fd, 0 );//删除事件最后一个参数为0
  39. }  
  40. //事件循环函数
  41. int ngx_epoll_process_events()  
  42. {  
  43.     epoll_event event_list[MAX_EVENT_NUMBER];  
  44. while(true)  
  45.     {  
  46. int number = epoll_wait(ep,event_list,MAX_EVENT_NUMBER,-1);  
  47.         printf("number=%d\n",number);  
  48.         printf("当前进程ID为: %d \n",getpid());  
  49. int i;  
  50. for(i = 0;i < number;i++)  
  51.         {  
  52.             web_connection_t* conn = (web_connection_t*)(event_list[i].data.ptr);  
  53. int socket = conn->fd;//当前触发的fd
  54. //读事件
  55. if ( event_list[i].events & EPOLLIN )  
  56.             {  
  57.                 conn->read_event->handler(conn);  
  58.             }  
  59. //写事件
  60. else if( event_list[i].events & EPOLLOUT )  
  61.             {  
  62.                 conn->write_event->handler(conn);  
  63.             }  
  64. else if( event_list[i].events & EPOLLERR )  
  65.             {  
  66.             }  
  67.         }  
  68.     }  
  69. return 0;  
  70. }  

使用方法:

服务器使用方法:直接运行即可 客户端使用方法:编译客户端代码,然后 ./client 127.0.0.1 8083 5(最后一个代表客户端进程数) 本程序在linux平台下测试成功

免费代码下载地址:http://download.csdn.net/detail/fangjian1204/7575477

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据库新发现

思科CEO钱伯斯:我非常尊敬华为

--------------------------------------------------------------------------------...

32110
来自专栏黄成甲

怎样成为解决问题的高手(连载一)

什么是问题?一言以蔽之,问题来源于现实与目标的差距。因此,问题产生的原因可能是不清楚目标是什么;还可能是不知道差距产生的原因是什么;或者虽然知道差距产生的原因,...

35340
来自专栏進无尽的文章

什么才是优秀的网站用户界面设计

16720
来自专栏计算机编程

2018-03-14 致敬霍金

  在黑洞领域,没有人知道这个怪兽是个什么东西,自从霍金给出“霍金辐射”与“奇点定理”过后,人们终于逐渐揭开了这宇宙中最为神秘的天体。在这里再次缅怀一下虽然坐着...

15320
来自专栏一个会写诗的程序员的博客

服务网格 Pattern: Service Mesh

自从几十年前首次引入以来,我们了解到分布式系统能够实现我们之前甚至无法思考的用例,但它们也会引入各种新问题。

14520
来自专栏数据库新发现

第十三届搞笑诺贝尔奖(IgNobel)新鲜出炉

第十三届搞笑诺贝尔奖(IgNobel)新鲜出炉 中广网 10月04日 09:48

19330
来自专栏進无尽的文章

益思维-早期苹果员工胸牌背面写的11条“成功法则”

早期苹果员工胸牌背面写的11条“成功法则”这个胸牌的背面写着11条成功法则,其中每一条文字都充满了正能量。从中我们可以看到一些触动人心的感觉。

9820
来自专栏黄成甲

怎样科学地和人相处?

按照约定俗成的说法,一般跟人打交道有两个规则,一个是“黄金法则”,一个是“白银法则”。黄金法则并不适合现代社会对陌生人使用。因为你觉得好的东西别人不一定觉得好。...

16020
来自专栏知道一点点

20种新颖的按钮风格和效果【附源码】

Codrops 给我们分享了一组新鲜的按钮样式和效果的集合。它们中的大部分效果都使用了 CSS3 过渡和伪元素,他们都有一个共同点,那就是都具有简单性,没有太多...

16910
来自专栏Leetcode名企之路

【Leetcode】59. 螺旋矩阵 II

给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

16030

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励