网络 http服务器-v2-epoll版本
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络 http服务器-v2-epoll版本相关的知识,希望对你有一定的参考价值。
epoll的基本接口与建立tcp连接的流程 查看:
网络 使用epoll 实现TCP服务器 - 初出茅庐小菜鸟 - 51CTO技术博客
http://shaungqiran.blog.51cto.com/10532904/1784410
重点:
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 <sys/epoll.h> #include <malloc.h> #include <netdb.h> #include <sys/utsname.h> #include <net/if.h> #include <sys/ioctl.h> #define _MAXFD_ 10 #define ERRORIP -1 int 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<ret_num;++i){ if(event[i].data.fd==sock&&event[i].events&EPOLLIN){ int new_sock=-1; struct sockaddr_in client; socklen_t len=sizeof(client); while((new_sock=accept(sock,(struct sockaddr*)&client,&len))){ if(new_sock<0){ //perror("accept"); break; } if(set_non_block(new_sock)<0){ echo_error(new_sock,500); continue; } printf(" epoll :%d\n",new_sock); ev.data.fd=new_sock; ev.events=EPOLLIN|EPOLLET; if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,new_sock,&ev)<0){ perror("epoll_ctl"); echo_error(new_sock,503); continue; } } break; } else { if(event[i].events&EPOLLIN){ int fd=event[i].data.fd; pev_buf pev=(pev_buf)malloc(sizeof(ev_buf)); event[i].data.ptr=pev; pev->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 <stdio.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/epoll.h> #include <sys/sendfile.h> #define _SIZE_ 1024 typedef 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);//发送xiaoxi void 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,"<html>\n",strlen("<html>\n")); write(fd,"<head>",strlen("<head>")); write(fd,"<h1> HELLO PPH!!!</h1>\n",strlen("<h1> HELLO PPH!!!</h1>\n")); write(fd,"<h2>",strlen("<h2>")); write(fd,error,strlen(error)); write(fd,"</h2>\n",strlen("</h2>\n")); write(fd,"</head>\n",strlen("</head>\n")); write(fd,"</html>",strlen("</html>")); // 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; else return -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.1 while(index>0){//提取method url if(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; }
以上是关于网络 http服务器-v2-epoll版本的主要内容,如果未能解决你的问题,请参考以下文章
VSCode自定义代码片段14——Vue的axios网络请求封装
VSCode自定义代码片段14——Vue的axios网络请求封装
VSCode自定义代码片段14——Vue的axios网络请求封装
Flutter 报错 DioError [DioErrorType.DEFAULT]: Bad state: Insecure HTTP is not allowed by platform(代码片段