epoll的基本接口与建立tcp连接的流程 查看:

网络 使用epoll 实现TCP服务器 - 初出茅庐小菜鸟 - 51CTO技术博客

重点:

        epoll 遵循的是多路复用的 I/O模型其内部只实现了对 关注I/O事件的监听,而没有实现具体的操作。因此我们必须自己选择恰当的时机去 读  或者 写。而对于内核而言,对一个socket 文件描述符 的读 、写 事件的监听 是分离的(可读,未必可写)。因此 ,要想达到最高的效率。用户 做的 I/O操作相应的也应该读、写分离。

        而如果读写分离的话,对于HTTP服务器而言,server 端 提供的服务有时是需要 客户端 提交的信息的。读、写分离会造成无法有效的 做出响应。所以 我们必须自己维护一段 缓冲区,去将每个socket fd 的有效内容都报春起来。直到进行 写事件 时用到,之后 回收该内存。

        每个事件句柄中都有 一个 不长使用的 指针(void*)。正好我们可以用它来指向 我们的缓冲区、

typedef union epoll_data {  

    void *ptr; //我们可用的 ptr  用来存指向缓冲区 

    int fd;  

    __uint32_t u32;  

    __uint64_t u64;  

} epoll_data_t;  

 //感兴趣的事件和被触发的事件  

struct epoll_event {  

    __uint32_t events; /* Epoll events */  

    epoll_data_t data; /* User data variable */  

};  

epoll.c

#include "httpd.h"#include 
#include 
#include 
#include 
#include 
#include 
#define _MAXFD_ 10 #define ERRORIP -1int set_non_block(int fd){ int old_flag=fcntl(fd,F_GETFL); if(old_flag<0){ perror("fcntl"); // exit(-4); return -1; } if(fcntl(fd,F_SETFL,old_flag|O_NONBLOCK)<0){ perror("fcntl"); return -1; } return 0;}int startup(char* ip,int port){ int sock=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in server; server.sin_family=AF_INET; server.sin_addr.s_addr=inet_addr(ip); server.sin_port=htons(port); int flag=0;// printf("port  %d  %d",port,(htons(port))); if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0){ perror("bind"); exit(-2); } if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1)      {       perror("setsockopt");       exit(1);        }   if(listen(sock,50)<0){ perror("listen"); exit(-3); } return sock;}void usage(char* arg){ printf("usage %s [ip] [port]\n",arg);}void epollup(int sock){ int epoll_fd=epoll_create(256); if(epoll_fd<0){ perror("epoll"); return; } int timeout_num=0; int done=0; int timeout=10000; int i=0; int ret_num=-1; struct epoll_event ev; struct epoll_event event[100]; ev.data.fd=sock; ev.events=EPOLLIN|EPOLLET;// fd_num=1;// printf("listen sock%d\n",sock); if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock,&ev)<0){ perror("epoll_ctl"); return ; } while(!done){ switch(ret_num=epoll_wait(epoll_fd,event,256,timeout)){ case -1:{ perror("epoll_wait"); break; } case 0 :{ if( timeout_num++>20) done=1; printf("time out...\n"); break; } default:{ for(i=0;i
fd=fd; if(epoll_recv_http(&event[i])<0){ free(pev); if(epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,&ev)<0) perror("read EPOLL_CTL_DEL"); close(fd); continue; } else event[i].events=EPOLLOUT; } if(event[i].events&EPOLLOUT){ int fd=((pev_buf)(event[i].data.ptr))->fd; epoll_echo_http(event+i); if(epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,&ev)<0) perror("out_ctl_del"); free((pev_buf)event[i].data.ptr); printf("epoll close :%d\n",fd); close(fd); } } } break; } } }}int main(int argc,char* argv[]){ if(argc!=3){ usage(argv[0]); exit(-1); } int port=atoi(argv[2]); int listen_sock; char* ip=NULL; if(strcmp(argv[1],"any")==0){ int sfd, intr; struct ifreq buf[16];     struct ifconf ifc;     sfd = socket (AF_INET, SOCK_DGRAM, 0);      if (sfd < 0) return ERRORIP; ifc.ifc_len = sizeof(buf); ifc.ifc_buf = (caddr_t)buf; if (ioctl(sfd, SIOCGIFCONF, (char *)&ifc)) return ERRORIP; intr = ifc.ifc_len / sizeof(struct ifreq); while (intr-- > 0 && ioctl(sfd, SIOCGIFADDR, (char *)&buf[intr])); close(sfd); ip= inet_ntoa(((struct sockaddr_in*)(&buf[intr].ifr_addr))-> sin_addr); printf("%s\n",ip); listen_sock=startup(ip,port); } else  listen_sock=startup( argv[1],port);// printf("port %s %d",argv[2],port);     set_non_block(listen_sock); epollup(listen_sock); close(listen_sock); return 0;}

 httpd.h

#ifndef __MYHTTP__#define __MYHTTP__#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define _SIZE_ 1024typedef struct event_buf{//自己维护的结构体 int fd; char method[10];//http请求方法 char url[1024];//请求参数 char parameter[1024];//get方法 参数 int cgi;//是否需要用到CGI 方法}ev_buf,*pev_buf;//重命名char* get_text(int fd,char* buf);//获取消息正文int epoll_recv_http(struct epoll_event *ev);//接受消息int epoll_echo_http(struct epoll_event * ev);//发送xiaoxivoid cgi_action(int fd,char* method,char* url,char* parameter);//CGI接口int get_line(int sock_fd,char * buf);//从socket fd 读取一行信息void* http_action(void* client_sock);//void echo_error(int fd,int _errno);//回显错误信息void error_all(int fd,int err,char* reason);//所有错误信息void echo_html(int fd,const char* url,int size );//回显请求网页#endif

httpd.c

#include "httpd.h"#define DEFAULT "/default.html"#define IMG "src_html"#define CGI "src_cgi"int times=0;void echo_error(int fd,int _errno){printf("join err\n");switch(_errno){case 400://Bad Request  //客户端请求有语法错误,不能被服务器所理解break;case 404:请求资源不存在,eg:输入了错误的URL//printf("*******404\n");error_all(fd,404,"NOT_FIND!!");break;case 401://请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 break;case 403://服务器收到请求,但是拒绝提供服务 break;case 500:// Internal Server Error //服务器发生不可预期的错误break;case 503://Server Unavailable  //服务器当前不能处理客户端的请求,一段时间后可能恢复正常break;default:break;}}void error_all(int fd,int err,char* reason){char buf[_SIZE_]="";char error[_SIZE_]="";sprintf(buf,"HTTP/1.0 %d %s\r\n\r\n",err,reason);sprintf(error," %d %s",err,reason);printf("err buf:%s\n error:%s",buf,error);write(fd,buf,strlen(buf));write(fd,"\n",strlen("\n"));write(fd,"",strlen(""));write(fd,"

 HELLO PPH!!!

\n",strlen("

 HELLO PPH!!!

\n"));write(fd,"

",strlen("

"));write(fd,error,strlen(error));write(fd,"

\n",strlen("\n"));write(fd,"\n",strlen("\n"));write(fd,"",strlen(""));// echo_html(fd,"src_html/1.html",102400000);}void echo_html(int fd,const char* url,int fd_size ){char buf[_SIZE_]="HTTP/1.1 200 OK\r\n\r\n";int rfd=-1;off_t set=0;ssize_t size=1;printf("url:%s\n",url);if((rfd=open(url, O_RDONLY))<0){echo_error(fd,500);printf("e-html %s",strerror(errno));}printf("open success\n");if(write(fd,buf,strlen(buf))<0){perror(" echo_html_write");close(rfd);return;}printf("write head success\n");int i=0;while(set!=fd_size){size=sendfile(fd,rfd,&set,fd_size);if(errno!=EAGAIN&&size<0){printf("sendfile error %s %d\n",strerror(errno),errno);break;}// printf("\nsend: size %d all size  %d time %d \n",set,fd_size,++i);}close(rfd);return;}int get_line(int sock_fd,char * line){int index=0;ssize_t size=1;char ch=0;printf("getline start\n");while(ch!='\n'){if((size=read(sock_fd,&ch,1))<0){perror("getline__");if(errno==EAGAIN)continue;elsereturn -1;}if(ch=='\r'){char tmp=0;if(recv(sock_fd,&tmp,1,MSG_PEEK)>0){if(tmp=='\n'){line[index++]=tmp;read(sock_fd,&ch,1);continue;}}}//printf("index %d\n",index);if(index==1024){printf("httpd line full exit\n");line[1023]=0;return -2;}line[index++]=ch;}line[index]=0;if(strcmp(line,"\n")==0){return 0;}printf("getline  success\n");return 1;}//获取post正文 参数char* get_length(int fd,char* content_length){int size=1;int tag=0;int index=0;while(size!=0){//通过持续读取一行 直到读到空行结束size=get_line(fd,content_length);if(size==-2)continue;if(strncasecmp(content_length,"content-length: ",16)==0){printf(" length success\n");break;}if(size==-1){printf("get line出错\n");return NULL;}}content_length[strlen(content_length)-1]=0;strcpy(content_length,content_length+16);printf("con end: %s\n",content_length);return content_length;}void cgi_action(int fd,char* method,char* url ,char* parameter){char env[20]="METHOD=";char par[_SIZE_]="PARAMETER=";int pwrite[2];if((pipe(pwrite)<0)){perror("pipe");return;}strcat(env,method);strcat(par,parameter);printf(" act url:%s\n",url);printf("parameter:%s\n",par);if(putenv(env)<0){perror("putenv");return;}if(putenv(par)<0){perror("putenv par");return;}// printf("fork qian\n");pid_t id=fork();if(id<0){perror("fork");return;}else if(id==0){//子进程close(pwrite[0]);//printf("child\n");if(dup2(pwrite[1],1)<0){perror("dup2.1");return;}if(dup2(fd,0)<0){perror("dup2.2");return;}if(execl(url,NULL)<0){perror("execl");printf("exit url:\n",url);exit(-2);} }else{//父进程close(pwrite[1]);char buf[_SIZE_]="";int count=0;int i=0;ssize_t size=1;while(size>0){size=read(pwrite[0],buf,_SIZE_);if(size<0){echo_error(fd,500);break;}if(size==0)break;write(fd,buf,strlen(buf));}waitpid(-1,NULL,0);close(pwrite[0]);}}int epoll_echo_http(struct epoll_event * ev){char* method=((pev_buf)ev->data.ptr)->method;char* url=((pev_buf)ev->data.ptr)->url;char* parameter=((pev_buf)ev->data.ptr)->parameter;//get方法 参数int fd=((pev_buf)ev->data.ptr)->fd;int cgi=((pev_buf)ev->data.ptr)->cgi;struct stat stat_buf; if(cgi==0)if(stat(url, &stat_buf)<0){printf("stat <0 \n");echo_error(fd,404);return 0;}if(strcasecmp("POST",method)==0){//printf("already cgi\n");cgi_action(fd,method,url,parameter);}else if(strcasecmp("GET",method)==0){if(cgi==1){//cgi// printf("rev_http: parameter:%s\n",parameter);cgi_action(fd,method,url,parameter);printf("ret cgi\n");}else{echo_html(fd,url,stat_buf.st_size);}}if(strcasecmp(method,"POST")==0)clear_buf(fd);return 0;}int epoll_recv_http(struct epoll_event *ev){if(ev==NULL){printf("ev error\n");return -1;}int fd=((pev_buf)ev->data.ptr)->fd;char real_url[128]="src_html";char line[_SIZE_];char* method=NULL;char* version=NULL;char* url=NULL;char parameter[_SIZE_]="";//get方法 参数char  content_length[_SIZE_]="";if(get_line(fd,line)==-2){printf("it's a cache request! so can't process!\n");return 0;}int index=strlen(line)-1;//GET / HTTP/1.1while(index>0){//提取method urlif(line[index]==' '&&version==NULL){version=((char*)line)+index+1;line[index]=0;}if(line[index]==' '&&url==NULL){url=line+index+1;line[index]=0;}--index;}method=line;((pev_buf)ev->data.ptr)->cgi=0;if(strcasecmp("GET",method)==0){index=0;while(url[index]){if(url[index]=='?'){((pev_buf)ev->data.ptr)->cgi=1;strcpy(parameter,url+index+1);url[index]=0;((pev_buf)ev->data.ptr)->cgi=1;break;}++index;}}else if(strcasecmp("POST",method)==0){((pev_buf)ev->data.ptr)->cgi=1;if(get_length(fd,content_length)==NULL){echo_error(fd,503);printf("get len err\n");clear_buf(fd);return -1;}strcpy(parameter,content_length);}if(strcmp(url,"/")==0){strcat(real_url,DEFAULT);}else{if(((pev_buf)ev->data.ptr)->cgi==1){strcpy(real_url,CGI);}strcat(real_url,url);}printf("real_url :%s\n",real_url);strcpy(((pev_buf)ev->data.ptr)->method,method);strcpy(((pev_buf)ev->data.ptr)->url,real_url);strcpy(((pev_buf)ev->data.ptr)->parameter,parameter);printf(" get connect :%d times !\n",++times);if(strcasecmp(method,"get")==0)clear_buf(fd);return 1;}int  clear_buf(int fd){char buf[_SIZE_]="";ssize_t size=0;size=read(fd,buf,_SIZE_);if(size<0){perror("clear_buf_read");return -1;}buf[size]=0;return 0;}