基于epoll的简单的http服务器

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

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


server.h

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

server.cpp

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

epoll_event.h

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

epoll_event.cpp

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

使用方法:

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

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值