http://blog.csdn.net/fangjian1204/article/details/34415651
http服務器已經可以處理并發連接,支持多個客戶端并發訪問,每個連接可以持續讀寫數據,當然,這只是一個簡單的學習例子,還有很多bug,發表出來只是希望大家可以互相學習,我也在不斷的改進,希望大家有什么意見可以多多指點,謝謝
server.h
[cpp]?view plain?copy
- /*?
- ?*?server.h?
- ?*?
- ?*??Created?on:?Jun?23,?2014?
- ?*??????Author:?fangjian?
- ?*/??
- ??
- ??
- #ifndef?SERVER_H_??
- #define?SERVER_H_??
- ??
- #include?"epoll_event.h"??
- ??
- struct?web_event_t;??
- struct?web_connection_t??
- {??
- ????int?fd;??
- ??
- ????int?state;//當前處理到哪個階段??
- ????struct?web_event_t*?read_event;??
- ????struct?web_event_t*?write_event;??
- ????char*?querybuf;??
- ????int?query_start_index;//請求數據的當前指針??
- ????int?query_end_index;//請求數據的下一個位置??
- ????int?query_remain_len;//可用空間??
- ??
- ????char?method[8];??
- ????char?uri[128];??
- ????char?version[16];??
- ????char?host[128];??
- ????char?accept[128];??
- ????char?conn[20];??
- };??
- ??
- struct?server??
- {??
- ????int?epollfd;??
- };??
- ??
- void?web_epoll_ctl(int?epollfd,int?ctl,int?fd,int?flag);??
- int?setnonblocking(int?fd);??
- void?initConnection(web_connection_t*?&conn);??
- void?web_accept(struct?web_connection_t*?conn);??
- void?read_request(?struct?web_connection_t*?conn?);??
- void?process_request_line(struct?web_connection_t*?conn);??
- void?process_head(struct?web_connection_t*?conn);??
- void?process_body(struct?web_connection_t*?conn);??
- void?send_response(struct?web_connection_t*?conn);??
- void?try_to_enlarge_buffer(struct?web_connection_t&?conn);??
- void?empty_event_handler(struct?web_connection_t*?conn);??
- void?close_conn(?struct?web_connection_t*?conn?);??
- ??
- #endif?/*?SERVER_H_?*/??
server.cpp
[cpp]?view plain?copy
- /*?
- ?*?server.cpp?
- ?*?
- ?*??Created?on:?Jun?23,?2014?
- ?*??????Author:?fangjian?
- ?*/??
- ??
- #include?"server.h"??
- #include?"epoll_event.h"??
- ??
- #include?<stdio.h>??
- #include?<netinet/in.h>??
- #include?<arpa/inet.h>??
- #include?<string.h>??
- #include?<stdlib.h>??
- #include?<fcntl.h>??
- #include<signal.h>??
- #include?<sys/socket.h>??
- #include?<sys/epoll.h>??
- #include?<sys/stat.h>??
- #include?<sys/sendfile.h>??
- #include?<iostream>??
- using?namespace?std;??
- ??
- int?main(int?argc,char*?argv[])??
- {??
- ????const?char*?ip?=?"127.0.0.1";??
- ????int?port?=??8083;??
- ??
- ????signal(SIGPIPE,SIG_IGN);//原因:http://blog.sina.com.cn/s/blog_502d765f0100kopn.html??
- ??
- ????int?listenfd?=?socket(AF_INET,SOCK_STREAM,0);??
- ????struct?sockaddr_in?address;??
- ????bzero(&address,sizeof(address));??
- ????address.sin_family?=?AF_INET;??
- ????inet_pton(AF_INET,ip,&address.sin_addr);??
- ????address.sin_port?=?htons(port);??
- ????bind(listenfd,(struct?sockaddr*)&address,sizeof(address));??
- ????listen(listenfd,50);??
- ??
- ????web_connection_t*?conn?=?NULL;??
- ????epoll_init_event(conn);??
- ????initConnection(conn);//創建一個用于接受連接的結構體??
- ????if(conn?==?NULL){printf("---創建監聽結構體失敗---\n");return?-1;};//創建監聽結構體??
- ??
- ????conn->fd?=?listenfd;??
- ????conn->read_event->handler?=?web_accept;??
- ????epoll_add_event(conn,EPOLLIN?|?EPOLLERR);??
- ??
- ????setnonblocking(listenfd);??
- ??
- ????fork();??
- ??
- ????ngx_epoll_process_events();//進入事件循環,等待事件到達??
- }??
- void?initConnection(web_connection_t*?&conn)??
- {??
- ????conn?=?(web_connection_t*)malloc(sizeof(web_connection_t));??
- ????conn->read_event?=?(web_event_t*)malloc(sizeof(web_event_t));??
- ????conn->write_event?=?(web_event_t*)malloc(sizeof(web_event_t));??
- ????conn->state?=?ACCEPT;??
- ??
- ????conn->querybuf?=?(char*)malloc(QUERY_INIT_LEN);??
- ????if(!conn->querybuf)??
- ????{??
- ????????printf("?malloc?error\n");??
- ????????return;??
- ????}??
- ????conn->query_start_index?=?0;??
- ????conn->query_end_index?=?0;??
- ????conn->query_remain_len?=?QUERY_INIT_LEN;??
- }??
- ??
- 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?web_accept(web_connection_t*?conn)??
- {??
- ????printf("-----------accept-------\n");??
- ????struct?sockaddr?*?client_address;??
- ????socklen_t?client_addrlength?=?sizeof(client_address);??
- ????int?connfd?=?accept(conn->fd,(struct?sockaddr*)&(client_address),&client_addrlength);??
- ????if(connfd?==?-1)??
- ????{??
- ????????printf("accept?error\n");??
- ????????return;??
- ????}??
- ????web_connection_t*?new_conn?=?NULL;??
- ????initConnection(new_conn);//創建一個新的連接結構體??
- ????if(new_conn?==?NULL){printf("---創建連接結構體失敗---\n");return;};??
- ??
- ????new_conn->fd?=?connfd;??
- ????new_conn->state?=?READ;??
- ????new_conn->read_event->handler?=?read_request;??
- ????epoll_add_event(new_conn,EPOLLIN?|?EPOLLERR);??
- ??
- ????setnonblocking(connfd);??
- }??
- void?read_request(?struct?web_connection_t*?conn?)??
- {??
- ????printf("-----------read_begin-------\n");??
- ??
- ????int?len,fd?=?conn->fd;??
- ????while(true)??
- ????{??
- ????????/*?嘗試增加緩沖區空間?*/??
- ????????try_to_enlarge_buffer(*conn);??
- ????????len=?recv(fd,conn->querybuf?+?conn->query_end_index,conn->query_remain_len,0);??
- ????????if(len?<?0)??
- ????????{??
- ????????????printf("----數據讀取完畢-----\n");??
- ????????????break;//表示當前數據讀取完畢,不是出錯??
- ????????}??
- ????????else?if(len?>?0)??
- ????????{??
- ????????????conn->query_end_index?+=?len;??
- ????????????conn->query_remain_len-=?len;??
- ????????}??
- ????????else?if(len?==?0)??
- ????????{??
- ????????????printf("----連接關閉-----\n");??
- ????????????epoll_del_event(conn);??
- ????????????close_conn(conn?);??
- ????????????return?;??
- ????????}??
- ????}??
- ????cout?<<?"-----客戶端的內容是?"?<<?endl;??
- ??
- ????cout?<<?conn->querybuf?<<?endl;??
- ??
- ????process_request_line(conn);??
- ??
- ????return?;??
- }??
- void?process_request_line(struct?web_connection_t*?conn)??
- {??
- ????int?len;??
- ????char*?ptr?=?strpbrk(conn->querybuf?+?conn->query_start_index,"?\t");??
- ????if(?!ptr)??
- ????{??
- ????????printf("請求行解析失敗\n");??
- ????????return;??
- ????}??
- ????len?=?ptr?-?conn->querybuf?-?conn->query_start_index;??
- ????strncpy(conn->method,conn->querybuf?+?conn->query_start_index,len);??
- ????cout?<<"metnod="<<conn->method<<endl;??
- ??
- ????conn->query_start_index?+=?(len+1);??
- ????ptr?=?strpbrk(conn->querybuf?+?conn->query_start_index,"?\t");??
- ????if(?!ptr)??
- ????{??
- ????????printf("請求行解析失敗\n");??
- ????????return;??
- ????}??
- ????len?=?ptr?-?conn->querybuf?-?conn->query_start_index;??
- ????strncpy(conn->uri,conn->querybuf?+?conn->query_start_index,len);??
- ????cout?<<?"uri="<<conn->uri<<endl;??
- ??
- ????conn->query_start_index?+=?(len+1);??
- ????ptr?=?strpbrk(conn->querybuf,"\n");//先是回車\r,再是換行\n??
- ????if(!ptr)??
- ????{??
- ????????printf("請求行解析失敗\n");??
- ????????return;??
- ????}??
- ????len?=?ptr?-?conn->querybuf?-?conn->query_start_index;??
- ????strncpy(conn->version,conn->querybuf?+?conn->query_start_index,len);??
- ????cout?<<?"version="<<conn->version<<endl;??
- ????conn->query_start_index?+=?(len+1);??
- ??
- ????cout?<<"-----請求行解析完畢----------"<<endl;??
- ??
- ????process_head(conn);??
- }??
- ??
- void?process_head(struct?web_connection_t*?conn)??
- {??
- ????cout?<<?"-------開始解析首部------"?<<?endl;??
- ??
- ????char*?end_line;??
- ????int?len;??
- ??
- ????while(true)??
- ????{??
- ????????end_line?=?strpbrk(conn->querybuf?+?conn->query_start_index,"\n");??
- ????????len?=?end_line?-?conn->querybuf?-?conn->query_start_index;??
- ????????if(len?==?1)??
- ????????{??
- ????????????printf("解析完畢\n");??
- ????????????conn->query_start_index?+=?(len?+1);??
- ????????????cout?<<?conn->querybuf?+?conn->query_start_index?<<?endl;??
- ????????????break;??
- ????????}??
- ????????else??
- ????????{??
- ????????????if(strncasecmp(conn->querybuf+conn->query_start_index,"Host:",5)?==?0)??
- ????????????{??
- ????????????????strncpy(conn->host,conn->querybuf+conn->query_start_index?+?6,len-6);??
- ????????????????cout?<<?"host="<<conn->host<<endl;??
- ????????????}??
- ????????????else?if(strncasecmp(conn->querybuf+conn->query_start_index,"Accept:",7)?==?0)??
- ????????????{??
- ????????????????strncpy(conn->accept,conn->querybuf+conn->query_start_index?+?8,len-8);??
- ????????????????cout?<<"accept="<<conn->accept?<<endl;??
- ????????????}??
- ????????????else?if(strncasecmp(conn->querybuf+conn->query_start_index,"Connection:",11)?==?0)??
- ????????????{??
- ????????????????strncpy(conn->conn,conn->querybuf+conn->query_start_index?+?12,len-12);??
- ????????????????cout?<<"connection="<<conn->conn?<<endl;??
- ????????????}??
- ????????????else??
- ????????????{??
- ????????????}??
- ????????????conn->query_start_index?+=?(len?+1);??
- ????????}??
- ????}??
- ????process_body(conn);??
- ????printf("----首部解析完畢----------\n");??
- }??
- void?process_body(struct?web_connection_t*?conn)??
- {??
- ????if(conn->query_start_index?==?conn->query_end_index)??
- ????{??
- ????????printf("---包體為空----\n");??
- ????}??
- ????else??
- ????{??
- ????????printf("---丟體包體-----\n");??
- ????}??
- ????conn->query_start_index?=?conn->query_end_index?=?0;??
- ??
- ????conn->state?=?SEND_DATA;??
- ????conn->write_event->handler?=?send_response;??
- ????conn->read_event->handler?=?empty_event_handler;//讀事件回調函數設置為空??
- ????epoll_mod_event(conn,EPOLLOUT?|?EPOLLERR);??
- }??
- void?send_response(struct?web_connection_t*?conn)??
- {??
- ????char?path[128]?=?"http";//根目錄下的文件夾??
- ????int?len?=?strlen(conn->uri);??
- ????memcpy(path+4,conn->uri,len);??
- ????len?+=?4;??
- ????path[len]?=?'\0';//很重要??
- ??
- ????int?filefd?=?open(path,O_RDONLY);??
- ????if(filefd?<?0)??
- ????{??
- ????????cout?<<?"無法打開該文件"?<<endl;??
- ????????return?;??
- ????}??
- ????struct?stat?stat_buf;??
- ????fstat(filefd,&stat_buf);??
- ????sendfile(conn->fd,filefd,NULL,stat_buf.st_size);??
- ????close(filefd);??
- ??
- ????//close(conn->fd);//如果不關閉該連接socket,則瀏覽器一直在加載,如何解決,保持keep-alive???
- ??
- ????conn->state?=?READ;??
- ????conn->read_event->handler?=?read_request;??
- ????epoll_mod_event(conn,EPOLLIN?|?EPOLLERR);??
- ??
- ????//sleep(2);??
- }??
- ??
- void?try_to_enlarge_buffer(struct?web_connection_t&?conn)??
- {??
- ????if(conn.query_remain_len??<?REMAIN_BUFFER)??
- ????{??
- ????????int?new_size?=?strlen(conn.querybuf)?+?QUERY_INIT_LEN;??
- ????????conn.querybuf?=?(char*)realloc(conn.querybuf,new_size);??
- ????????conn.query_remain_len??=?new_size?-?conn.query_end_index;??
- ????}??
- }??
- void?empty_event_handler(struct?web_connection_t*?conn)??
- {??
- ??
- }??
- //關閉一個連接??
- void?close_conn(?struct?web_connection_t*?conn?)??
- {??
- ????static?int?count?=?0;??
- ????count?++;??
- ????printf("關閉第%d個連接\n",count);??
- ??
- ????close(?conn->fd);??
- ????free(conn->querybuf);??
- ????free(conn->read_event);??
- ????free(conn->write_event);??
- ????free(conn);??
- }??
epoll_event.h
[cpp]?view plain?copy
- /*?
- ?*?event.h?
- ?*?
- ?*??Created?on:?Jun?25,?2014?
- ?*??????Author:?fangjian?
- ?*/??
- ??
- #ifndef?EVENT_H_??
- #define?EVENT_H_??
- ??
- #include?<netinet/in.h>??
- #include?"server.h"??
- #define?MAX_EVENT_NUMBER?10000??
- #define?QUERY_INIT_LEN??1024??
- #define?REMAIN_BUFFER??512??
- ??
- /*?以下是處理機的狀態?*/??
- #define?ACCEPT?1??
- #define?READ?2??
- #define?QUERY_LINE?4??
- #define?QUERY_HEAD?8??
- #define?QUERY_BODY?16??
- #define?SEND_DATA?32??
- ??
- struct?web_connection_t;??
- ??
- typedef?void?(*event_handler_pt)(web_connection_t*?conn);??
- ??
- //每一個事件都由web_event_t結構體來表示??
- struct?web_event_t??
- {??
- ????/*為1時表示事件是可寫的,通常情況下,它表示對應的TCP連接目前狀態是可寫的,也就是連接處于可以發送網絡包的狀態*/??
- ????unsigned?????????write:1;??
- ????/*為1時表示此事件可以建立新的連接,通常情況下,在ngx_cycle_t中的listening動態數組中,每一個監聽對象ngx_listening_t對應的讀事件中?
- ????的accept標志位才會是1*/??
- ????unsigned?????????accept:1;??
- ????//為1時表示當前事件是活躍的,這個狀態對應著事件驅動模塊處理方式的不同,例如:在添加事件、刪除事件和處理事件時,該標志位的不同都會對應著不同的處理方式??
- ????unsigned?????????active:1;??
- ????unsigned?????????oneshot:1;??
- ????unsigned?????????eof:1;//為1時表示當前處理的字符流已經結束??
- ????unsigned?????????error:1;//為1時表示事件處理過程中出現了錯誤??
- ??
- ????event_handler_pt??handler;//事件處理方法,每個消費者模塊都是重新實現它??
- ????unsigned?????????closed:1;//為1時表示當前事件已經關閉??
- };??
- ??
- void?epoll_init_event(web_connection_t*?&conn);??
- void?epoll_add_event(web_connection_t*?conn,int?flag);??
- void?epoll_mod_event(web_connection_t*?conn,int?flag);??
- void?epoll_del_event(web_connection_t*?conn);??
- int?ngx_epoll_process_events();??
- #endif?/*?EVENT_H_?*/??
epoll_event.cpp
[cpp]?view plain?copy
- /*?
- ?*?event.cpp?
- ?*?
- ?*??Created?on:?Jun?25,?2014?
- ?*??????Author:?fangjian?
- ?*/??
- ??
- #include?"epoll_event.h"??
- #include?<sys/epoll.h>??
- #include?<unistd.h>??
- #include?<stdio.h>??
- #include?<stdlib.h>??
- ??
- static?int??ep?=?-1;//epoll對象的描述符,每個進程只有一個??
- ??
- void?epoll_init_event(web_connection_t*?&conn)??
- {??
- ????ep?=?epoll_create(1024);??
- }??
- ??
- /*?添加事件,conn已經設置好回調函數和fd了?*/??
- void?epoll_add_event(web_connection_t*?conn,int?flag)??
- {??
- ????epoll_event?ee;??
- ????int?fd?=?conn->fd;??
- ????ee.data.ptr?=?(void*)conn;??
- ????ee.events?=?flag;??
- ????epoll_ctl(ep,EPOLL_CTL_ADD,fd,&ee);??
- }??
- ??
- /*?修改事件,event已經設置好回調函數和fd了?*/??
- void?epoll_mod_event(web_connection_t*?conn,int?flag)??
- {??
- ????epoll_event?ee;??
- ????int?fd?=?conn->fd;??
- ????ee.data.ptr?=?(void*)conn;??
- ????ee.events?=?flag;??
- ????epoll_ctl(ep,EPOLL_CTL_MOD,fd,&ee);??
- }??
- ??
- //刪除該描述符上的所有事件,若想只刪除讀事件或寫事件,則把相應的事件設置為空函數??
- void?epoll_del_event(web_connection_t*?conn)??
- {??
- ????epoll_ctl(?ep,?EPOLL_CTL_DEL,?conn->fd,?0?);//刪除事件最后一個參數為0??
- }??
- //事件循環函數??
- int?ngx_epoll_process_events()??
- {??
- ????epoll_event?event_list[MAX_EVENT_NUMBER];??
- ????while(true)??
- ????{??
- ????????int?number?=?epoll_wait(ep,event_list,MAX_EVENT_NUMBER,-1);??
- ??
- ????????printf("number=%d\n",number);??
- ????????printf("當前進程ID為:?%d?\n",getpid());??
- ??
- ????????int?i;??
- ????????for(i?=?0;i?<?number;i++)??
- ????????{??
- ????????????web_connection_t*?conn?=?(web_connection_t*)(event_list[i].data.ptr);??
- ????????????int?socket?=?conn->fd;//當前觸發的fd??
- ????????????//讀事件??
- ????????????if?(?event_list[i].events?&?EPOLLIN?)??
- ????????????{??
- ????????????????conn->read_event->handler(conn);??
- ????????????}??
- ????????????//寫事件??
- ????????????else?if(?event_list[i].events?&?EPOLLOUT?)??
- ????????????{??
- ????????????????conn->write_event->handler(conn);??
- ????????????}??
- ????????????else?if(?event_list[i].events?&?EPOLLERR?)??
- ????????????{??
- ??
- ????????????}??
- ????????}??
- ????}??
- ????return?0;??
- }??
使用方法:
服務器使用方法:直接運行即可
客戶端使用方法:編譯客戶端代碼,然后 ./client 127.0.0.1 8083 5(最后一個代表客戶端進程數)
本程序在linux平臺下測試成功
免費代碼下載地址:http://download.csdn.net/detail/fangjian1204/7575477