首页 > 代码库 > 基于epoll的简单的http服务器
基于epoll的简单的http服务器
本人用epoll写了一个简单的http服务器,该服务器在客户端第一次发送数据时可以正确处理,但是当客户端不关闭继续发送数据时,服务器无法读取,请求大家帮忙看看哪里有问题,谢谢
</pre></p><p>server.h</p><p><pre name="code" class="cpp">/* * server.h * * Created on: Jun 23, 2014 * Author: fangjian */ #include <netinet/in.h> #ifndef SERVER_H_ #define SERVER_H_ #define QUERY_INIT_LEN 10 #define REMAIN_BUFFER 5 /* 以下是处理机的状态 */ #define ACCEPT 1 #define READ 2 #define QUERY_LINE 4 #define QUERY_HEAD 8 #define QUERY_BODY 16 #define SEND_DATA 32 struct connection { int fd; struct sockaddr_in client_address; int state;//当前处理到哪个阶段 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); struct connection* initConnection(int fd); void state_machine(struct connection& conn); void web_accept(struct connection& conn); void read_request(struct connection& conn); void process_request_line(struct connection& conn); void process_head(struct connection& conn); void process_body(struct connection& conn); void send_response(struct connection& conn); void try_to_enlarge_buffer(struct connection& conn); void close_connection(int fd); #endif /* SERVER_H_ */
server.cpp:
/* * server.cpp * * Created on: Jun 23, 2014 * Author: fangjian */ #include "server.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> #include <map> using namespace std; #define MAX_EVENT_NUMBER 10000 map<int,connection> map_conn; struct server server; int main(int argc,char* argv[]) { const char* ip = "172.16.55.67"; int port = 8083; signal(SIGPIPE,SIG_IGN); 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); epoll_event events[MAX_EVENT_NUMBER]; server.epollfd = epoll_create(1024); web_epoll_ctl(server.epollfd,EPOLL_CTL_ADD,listenfd,EPOLLIN);//添加读连接事件 setnonblocking(listenfd); //fork(); while(true) { int number = epoll_wait(server.epollfd,events,MAX_EVENT_NUMBER,-1); printf("number=%d\n",number); printf("当前进程ID为: %d \n",getpid()); int i; for(i = 0;i < number;i++) { int socket = events[i].data.fd;//当前触发的fd //有新连接到达 if(socket == listenfd) { printf("有新连接到达\n"); //创建一个连接结构体 struct connection* conn = initConnection(socket); //进入状态机处理请求 state_machine(*conn); } //读事件到达 else if(events[i].events & EPOLLIN) { printf("--------开始处理请求行------------\n"); state_machine(map_conn[socket]); printf("--------处理请求行结束--------------\n");//sleep(2); web_epoll_ctl(server.epollfd,EPOLL_CTL_MOD,socket,EPOLLIN); } //有写事件到达 else if(events[i].events & EPOLLOUT) { printf("--------开始发送数据--------------\n"); state_machine(map_conn[socket]); printf("---------发送数据结束--------------\n"); } //异常 else { } } } } void state_machine(struct connection& conn) { switch (conn.state) { case ACCEPT:{web_accept(conn);break;} case READ:{read_request(conn);break;} case QUERY_LINE:{process_request_line(conn);break;} case SEND_DATA:{send_response(conn);break;} } } /* 调用epoll_ctl处理 */ void web_epoll_ctl(int epollfd,int ctl,int fd,int flag) { epoll_event event; event.data.fd =fd; event.events = flag; epoll_ctl(epollfd,ctl,fd,&event); } 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; } struct connection* initConnection(int fd) { struct connection* conn = (struct connection*)malloc(sizeof(struct connection)); conn->fd = fd; conn->state = ACCEPT; conn->querybuf = (char*)malloc(QUERY_INIT_LEN); if(!conn->querybuf) { printf(" malloc error\n"); return NULL; } conn->query_start_index = 0; conn->query_end_index = 0; conn->query_remain_len = QUERY_INIT_LEN; return conn; } void web_accept(struct connection& conn) { socklen_t client_addrlength = sizeof(conn.client_address); int connfd = accept(conn.fd,(struct sockaddr*)&(conn.client_address),&client_addrlength); if(connfd == -1) { printf("accept error\n"); return; } web_epoll_ctl(server.epollfd,EPOLL_CTL_DEL,conn.fd,EPOLLIN);//删除监听事件 close(conn.fd);//关闭监听描述符,因为keep_alive是保持连接描述符不关闭 conn.fd = connfd; conn.state = READ; map_conn[connfd] = conn; web_epoll_ctl(server.epollfd,EPOLL_CTL_ADD,connfd,EPOLLIN); setnonblocking(connfd); } void read_request(struct connection& conn) { int len,fd = conn.fd; while(true) { /* 尝试增加缓冲区空间 */ try_to_enlarge_buffer(conn); len= read(fd,conn.querybuf+conn.query_end_index,conn.query_remain_len); if(len == -1) { printf("----数据读完-----\n"); conn.state = QUERY_LINE;//进入解析阶段 web_epoll_ctl(server.epollfd,EPOLL_CTL_DEL,conn.fd,EPOLLIN);//删除该连接上的读事件 break; } else if(len == 0) { printf("----客户端关闭连接------\n"); conn.state = QUERY_LINE;//进入解析阶段 web_epoll_ctl(server.epollfd,EPOLL_CTL_DEL,conn.fd,EPOLLIN);//删除该连接上的读事件 break; } else if(len > 0) { conn.query_end_index += len; conn.query_remain_len -= len; } } cout << "-----客户端的内容是 " << endl; cout << conn.querybuf << endl; process_request_line(conn); } void process_request_line(struct connection& 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 connection& 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 connection& conn) { if(conn.query_start_index == conn.query_end_index) { printf("---包体为空----\n"); } else { printf("---丢体包体-----\n"); } conn.query_start_index = conn.query_end_index = 0; send_response(conn); } void send_response(struct connection& 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? //web_epoll_ctl(server.epollfd,EPOLL_CTL_MOD,conn.fd,EPOLLIN); conn.state = READ; } void close_connection(int fd) { map_conn.erase(fd); close(fd); } void try_to_enlarge_buffer(struct connection& 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; } }
客户端代码:
#include <stdlib.h> #include <stdio.h> #include <assert.h> #include <unistd.h> #include <sys/types.h> #include <sys/epoll.h> #include <fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> static const char* request = "GET /index.html HTTP/1.1\r\nConnection: keep-alive\r\n\r\nxxxxxxxxxxxx"; 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 addfd( int epoll_fd, int fd ) { epoll_event event; event.data.fd = fd; event.events = EPOLLOUT | EPOLLET | EPOLLERR; epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event ); setnonblocking( fd ); } bool write_nbytes( int sockfd, const char* buffer, int len ) { int bytes_write = 0; printf( "write out %d bytes to socket %d\n", len, sockfd ); while( 1 ) { bytes_write = send( sockfd, buffer, len, 0 ); if ( bytes_write == -1 ) { return false; } else if ( bytes_write == 0 ) { return false; } len -= bytes_write; buffer = buffer + bytes_write; if ( len <= 0 ) { return true; } } } bool read_once( int sockfd, char* buffer, int len ) { int bytes_read = 0; memset( buffer, '\0', len ); bytes_read = recv( sockfd, buffer, len, 0 ); if ( bytes_read == -1 ) { return false; } else if ( bytes_read == 0 ) { return false; } printf( "read in %d bytes from socket %d with content: %s\n", bytes_read, sockfd, buffer ); return true; } void start_conn( int epoll_fd, int num, const char* ip, int port ) { int ret = 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 ); for ( int i = 0; i < num; ++i ) { sleep( 1 ); int sockfd = socket( PF_INET, SOCK_STREAM, 0 ); printf( "create 1 sock\n" ); if( sockfd < 0 ) { continue; } if ( connect( sockfd, ( struct sockaddr* )&address, sizeof( address ) ) == 0 ) { printf( "build connection %d\n", i ); addfd( epoll_fd, sockfd ); } } } void close_conn( int epoll_fd, int sockfd ) { epoll_ctl( epoll_fd, EPOLL_CTL_DEL, sockfd, 0 ); close( sockfd ); } int main( int argc, char* argv[] ) { assert( argc == 4 ); int epoll_fd = epoll_create( 100 ); start_conn( epoll_fd, atoi( argv[ 3 ] ), argv[1], atoi( argv[2] ) ); epoll_event events[ 10000 ]; char buffer[ 2048 ]; while ( 1 ) { int fds = epoll_wait( epoll_fd, events, 10000, 2000 ); for ( int i = 0; i < fds; i++ ) { int sockfd = events[i].data.fd; if ( events[i].events & EPOLLIN ) { printf("----服务器发来数据-----\n"); if ( ! read_once( sockfd, buffer, 2048 ) ) { close_conn( epoll_fd, sockfd ); } struct epoll_event event; event.events = EPOLLOUT | EPOLLET | EPOLLERR; event.data.fd = sockfd; epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event ); } else if( events[i].events & EPOLLOUT ) {printf("---------向服务器发送数据-----------\n"); if ( ! write_nbytes( sockfd, request, strlen( request ) ) ) { close_conn( epoll_fd, sockfd ); } printf("--------数据发送完毕-------------\n"); struct epoll_event event; event.events = EPOLLIN | EPOLLET | EPOLLERR; event.data.fd = sockfd; epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event ); } else if( events[i].events & EPOLLERR ) { close_conn( epoll_fd, sockfd ); } } } }
测试方法,在服务器项目中建一个名字为"http"的文件夹,里面放一个index.html静态文件,如:
<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href=http://www.mamicode.com/"http://nginx.org/">nginx.org.
>然后将服务器的IP改为自己的IP,即可运行服务器;客户端直接运行 ./main IP 8083 1 即可,谢谢大家的帮忙
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。