epoll(1)
#include<stdio.h> #include<fcntl.h> #include<unistd.h> #include<sys/epoll.h> #include<arpa/inet.h> #include<sys/socket.h> #include<errno.h> #include <netinet/tcp.h> #include <pthread.h> #include<string.h> #include<stdlib.h> #define MAX 500 int main(int argc,char* argv[]) { if(argc<2){return -1;} int port=atoi(argv[1]); int sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0){return -1;} struct sockaddr_in addr; memset(&addr,0,sizeof(struct sockaddr_in)); addr.sin_family=AF_INET; addr.sin_port=htons(port); addr.sin_addr.s_addr=INADDR_ANY; if(bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0) { printf("绑定端口失败!\n"); return -1; } if(listen(sockfd,5)<0) { return -1; } int epfd=epoll_create(1); struct epoll_event ev,events[MAX]={0}; ev.events=EPOLLIN; ev.data.fd=sockfd; epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev); while(1) { int nready=epoll_wait(epfd,events,500,-1); for(int i=0;i<nready;i++) { if(events[i].events&EPOLLIN) { if(events[i].data.fd==sockfd) { struct sockaddr_in client_addr; memset(&client_addr,0,sizeof(struct sockaddr_in)); socklen_t client_len=sizeof(client_addr); int clientfd=accept(sockfd,(struct sockaddr*)&client_addr,&client_len); char str[12]; printf("recv from %s at port %d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)), ntohs(client_addr.sin_port)); ev.events=EPOLLIN|EPOLLET; ev.data.fd=clientfd; epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&ev); } else { char buf[1024]={0}; int clientfd=events[i].data.fd; int n=recv(clientfd,buf,sizeof(buf),0); //非阻塞情况下,当缓冲区没有数据的时候,会返回-1 if(n<0) { if(errno==EAGAIN||errno==EWOULDBLOCK)//其他情况的异常,errno会有相应的标志,这是正常情况下的错误 { continue; } close(clientfd); ev.events=EPOLLIN; ev.data.fd=clientfd; epoll_ctl(epfd,EPOLL_CTL_DEL,clientfd,&ev); }else if(n==0) //客户端与服务器端断开的时候 { /*高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭连接。 这个场景下会出现大量socket处于TIME_WAIT状态。如果客户端的并发量持续 很高,此时部分客户端就会显示连接不上。 比如取一个web页面,1秒钟的http短连接处理完业务,在关闭连接之后, 这个业务用过的端口会停留在TIMEWAIT状态几分钟,而这几分钟, 其他HTTP请求来临的时候是无法占用此端口的(占着茅坑不拉翔)。 单用这个业务计算服务器的利用率会发现,服务器干正经事的时间 和端口(资源)被挂着无法被使用的时间的比例是 1:几百,服务器资源严重浪费。*/ printf("服务器与客户端断开连接\n"); //如果这里写了大量业务代码之后忘记关闭了clientfd,那么服务器端就会有大量close_wait状态 close(clientfd); ev.events=EPOLLIN; ev.data.fd=clientfd; epoll_ctl(epfd,EPOLL_CTL_DEL,clientfd,&ev); } else { printf("接收到的数据为:%s,总共%d个字节\n",buf,n); } } } } } return 0; }