该httpserver已经能够处理并发连接,支持多个client并发訪问,每一个连接能够持续读写数据。当然。这仅仅是一个简单的学习样例。还有非常多bug,发表出来仅仅是希望大家能够互相学习。我也在不断的改进,希望大家有什么意见能够多多指点,谢谢
server.h
/* * 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
/* * server.cpp * * Created on: Jun 23, 2014 * Author: fangjian */#include "server.h"#include "epoll_event.h"#include#include #include #include #include #include #include #include #include #include #include #include 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="< method< 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="< uri< 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="< version< query_start_index += (len+1); cout <<"-----请求行解析完成----------"< 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="< host< querybuf+conn->query_start_index,"Accept:",7) == 0) { strncpy(conn->accept,conn->querybuf+conn->query_start_index + 8,len-8); cout <<"accept="< accept < querybuf+conn->query_start_index,"Connection:",11) == 0) { strncpy(conn->conn,conn->querybuf+conn->query_start_index + 12,len-12); cout <<"connection="< 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 << "无法打开该文件" < 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/* * event.h * * Created on: Jun 25, 2014 * Author: fangjian */#ifndef EVENT_H_#define EVENT_H_#includeepoll_event.cpp#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 32struct 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_ */
/* * event.cpp * * Created on: Jun 25, 2014 * Author: fangjian */#include "epoll_event.h"#include用法:#include #include #include 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;}
server用法:直接执行就可以
client用法:编译client代码。然后 ./client 127.0.0.1 8083 5(最后一个代表client进程数) 本程序在linux平台下測试成功免费代码下载地址: