一、了解epoll
可以通过epoll实现io多路复用
二、完整代码
epoll水平触发(LT)和边沿触发(ET)概念较为重要
开发过程中,一定要注意sockfd要在epoll这个集合里面
使用epoll肯定会有一个 事件的主循环。
#include<sys/socket.h> #include<arpa/inet.h> #include<netinet/in.h> #include<sys/epoll.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<pthread.h> #define BUFFER_LENGTH 1024 #define EPOLL_SIZE 1024 int main(int argc,char** argv){ if(argc<2){ printf("Param Error\n"); return -1; } int port=atoi(argv[1]); int sockfd=socket(AF_INET,SOCK_STREAM,0); sockaddr_in addr; memset(&addr,0,sizeof(sockaddr_in)); addr.sin_family=AF_INET; addr.sin_port=htons(port); addr.sin_addr.s_addr=INADDR_ANY; if(bind(sockfd,(sockaddr*)&addr,sizeof(sockaddr_in))<0){ perror("bind"); return 2; } if(listen(sockfd,5)<0){ perror("listen"); return 3; } int epfd=epoll_create(1);//里面的参数只要大于0就ok,里面的参数只有0和1的区别 epoll_event events[EPOLL_SIZE]={0}; epoll_event ev; ev.events=EPOLLIN;//EPOLLIN表示有数据来了 ev.data.fd=sockfd; epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);//将 监听的sockfd(门卫)交给epollfd(快递员)管理 while(1){ int nready=epoll_wait(epfd,events,EPOLL_SIZE,0);//返回值为事件个数 ;第四个参数:-1表示永久阻塞,0表示立即返回,如果是n,那么n个时间间隔执行一次。 if(nready==-1) continue;//如果一个事件都没有就continue for(int i=0;i<nready;i++){//nready中包含了listenfd和clientfd,这两个要区别处理 if(events[i].data.fd==sockfd){//listenfd //有新的客户端连接 sockaddr_in client_addr; memset(&client_addr,0,sizeof(sockaddr_in)); socklen_t client_len=sizeof(client_addr); int clientfd=accept(sockfd,(sockaddr*)&client_addr,&client_len); ev.events=EPOLLIN|EPOLLET;//边沿触发 ev.data.fd=clientfd; epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&ev);//把新的的客户fd加入到epoll中 }else{ int clientfd=events[i].data.fd; char buffer[BUFFER_LENGTH]={0}; int len=recv(clientfd,buffer,BUFFER_LENGTH,0); if(len<0){ close(clientfd); ev.events=EPOLLIN; ev.data.fd=clientfd; epoll_ctl(epfd,EPOLL_CTL_DEL,clientfd,&ev); break; } else if(len==0){ close(clientfd); ev.events=EPOLLIN; ev.data.fd=clientfd; epoll_ctl(epfd,EPOLL_CTL_DEL,clientfd,&ev); break; } else{ printf("Recv:%s,%d bytes\n",buffer,len); } } } } return 0; }
三、补充:
1.在建立连接的时候
比如建立3个客户端连接(假设没有其他输入)
每次循环,nready都为1,表示listenfd接收到要连接的客户端。while(1)这个大循环,循环3次,加入3个客户端fd。
2.断开连接也会接受到信息
断开的时候clientfd会发出信息,也就是events[i].data.fd!=sockfd
的情况,此时len==0
,进行客户端断开
3.多个客户端同时发送消息时
比如有5个客户端同时发送信息(假设没有新得客户端连接)
那么此时的nready=5
4.水平触发(LT)和边沿触发(ET)
LT模式下,只要存在未读完的数据,就会进行重复读取。
ET模式下,只在数据发生变化时,才会进行一次读取,如果数据过长,可能有部分数据没有读取的风险。但是效率比水平触发高
5.EPOLLIN代表读取数据
如果没有设置读取数据,那么接受数据的时候,就没法得到对应的数据,如果都设置0,那么nready=0