epoll out事件觸發時機
當epoll_ctl設定了out事件(add 或 mod),如果核心發送緩衝區有空閑,則會觸發out事件,如果發送緩衝區滿,則不會不會觸發out事件,當發送緩衝區不再滿時會觸發out事件。如果在緩衝區由滿變為不滿之前不設定out事件(mod 修改out->in),在緩衝區不滿之後不會觸發out事件。
第一種情況,設定out,測試代碼如下:
#include <iostream>#include <sys/socket.h>#include <sys/epoll.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <errno.h>using namespace std;#define MAXLINE 5#define OPEN_MAX 100#define LISTENQ 20#define SERV_PORT 6002#define INFTIM 1000void setnonblocking(int sock){ int opts; opts=fcntl(sock,F_GETFL); if(opts<0) { perror("fcntl(sock,GETFL)"); exit(1); } opts = opts|O_NONBLOCK; if(fcntl(sock,F_SETFL,opts)<0) { perror("fcntl(sock,SETFL,opts)"); exit(1); } }int main(){ int i, maxi, listenfd, connfd, sockfd,epfd,nfds; ssize_t n; char line[MAXLINE]; socklen_t clilen; //聲明epoll_event結構體的變數,ev用於註冊事件,數組用於回傳要處理的事件 struct epoll_event ev,events[20]; //產生用於處理accept的epoll專用的檔案描述符 epfd=epoll_create(256); struct sockaddr_in clientaddr; struct sockaddr_in serveraddr; listenfd = socket(AF_INET, SOCK_STREAM, 0); int reuse = 1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); //把socket設定為非阻塞方式 //setnonblocking(listenfd); //設定與要處理的事件相關的檔案描述符 ev.data.fd=listenfd; //設定要處理的事件類型 ev.events=EPOLLIN|EPOLLET; //ev.events=EPOLLIN; //註冊epoll事件 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; char *local_addr="0.0.0.0"; inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT); serveraddr.sin_port=htons(SERV_PORT); bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr)); listen(listenfd, LISTENQ); maxi = 0; for ( ; ; ) { //等待epoll事件的發生 nfds=epoll_wait(epfd,events,20,-1); //處理所發生的所有事件 for(i=0;i<nfds;++i) { if(events[i].data.fd==listenfd) { connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); setnonblocking(connfd); if(connfd<0){ perror("connfd<0"); exit(1); } setnonblocking(connfd); char *str = inet_ntoa(clientaddr.sin_addr); cout << "accapt a connection from " << str << endl; //設定用於讀操作的檔案描述符 ev.data.fd=connfd; //設定用於注測的讀操作事件 ev.events=EPOLLOUT|EPOLLET; //ev.events=EPOLLIN; //註冊ev epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); char buf[1024] = "abc1234567890abc1234567890abc1234567890abc1234567890abc1234567890abc1234567890abc1234567890abc1234567890"; int i = 0; while(1) { if (send(connfd, buf, strlen(buf) + 1, 0) < 0) { cout << "error-->" << strerror(errno) << endl; break; } usleep(100000); cout << ++i << endl; } } else if(events[i].events&EPOLLIN) { cout << "EPOLLIN" << endl; sockfd = events[i].data.fd; char buf; int n = 0; while ( (n = read(sockfd, &buf, 1)) > 0) { printf("%c\n",buf); } printf("%d\n",n); if (n == 0) { ev.data.fd=sockfd; epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,&ev); } else { ev.data.fd=sockfd; ev.events=EPOLLET|EPOLLOUT; epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } } else if(events[i].events&EPOLLOUT) { cout << "EPOLLOUT" << endl; sockfd = events[i].data.fd; //write(sockfd, line, n); //設定用於讀操作的檔案描述符 // sleep(5); ev.data.fd=sockfd; //設定用於注測的讀操作事件 ev.events=EPOLLIN|EPOLLET; //修改sockfd上要處理的事件為EPOLIN epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } } } return 0;}
用戶端使用telnet 命令,telnet 192.168.0.123 6002,串連後會收到資料,使用加強工作管理員將telnet進程掛起,不再列印輸出,server一直發送資料,直到Resource temporarily unavailable,退出while迴圈,等待out事件,在加強任務管理中喚醒telnet ,會列印出EPOLLOUT,說明發送緩衝區由滿到不滿,會觸發out事件。
第二種情況:在緩衝區由滿變為不滿之前修改out 為 in,測試代碼如下:
#include <iostream>#include <sys/socket.h>#include <sys/epoll.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <errno.h>using namespace std;#define MAXLINE 5#define OPEN_MAX 100#define LISTENQ 20#define SERV_PORT 6002#define INFTIM 1000void setnonblocking(int sock){ int opts; opts=fcntl(sock,F_GETFL); if(opts<0) { perror("fcntl(sock,GETFL)"); exit(1); } opts = opts|O_NONBLOCK; if(fcntl(sock,F_SETFL,opts)<0) { perror("fcntl(sock,SETFL,opts)"); exit(1); } }int main(){ int i, maxi, listenfd, connfd, sockfd,epfd,nfds; ssize_t n; char line[MAXLINE]; socklen_t clilen; //聲明epoll_event結構體的變數,ev用於註冊事件,數組用於回傳要處理的事件 struct epoll_event ev,events[20]; //產生用於處理accept的epoll專用的檔案描述符 epfd=epoll_create(256); struct sockaddr_in clientaddr; struct sockaddr_in serveraddr; listenfd = socket(AF_INET, SOCK_STREAM, 0); int reuse = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); //把socket設定為非阻塞方式 //setnonblocking(listenfd); //設定與要處理的事件相關的檔案描述符 ev.data.fd=listenfd; //設定要處理的事件類型 ev.events=EPOLLIN|EPOLLET; //ev.events=EPOLLIN; //註冊epoll事件 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; char *local_addr="0.0.0.0"; inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT); serveraddr.sin_port=htons(SERV_PORT); bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr)); listen(listenfd, LISTENQ); maxi = 0; for ( ; ; ) { //等待epoll事件的發生 nfds=epoll_wait(epfd,events,20,-1); //處理所發生的所有事件 for(i=0;i<nfds;++i) { if(events[i].data.fd==listenfd) { connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); setnonblocking(connfd); if(connfd<0){ perror("connfd<0"); exit(1); } //setnonblocking(connfd); char *str = inet_ntoa(clientaddr.sin_addr); cout << "accapt a connection from " << str << endl; //設定用於讀操作的檔案描述符 ev.data.fd=connfd; //設定用於注測的讀操作事件 ev.events=EPOLLOUT|EPOLLET; //ev.events=EPOLLIN; //註冊ev epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); char buf[1024] = "abc1234567890abc1234567890abc1234567890abc1234567890abc1234567890abc1234567890abc1234567890abc1234567890"; int i = 0; while(1) { if (send(connfd, buf, strlen(buf) + 1, 0) < 0) { cout << "error-->" << strerror(errno) << endl; break; } usleep(100000); cout << ++i << endl; } //設定用於注測的讀操作事件 ev.events=EPOLLIN|EPOLLET; //ev.events=EPOLLIN; //註冊ev epoll_ctl(epfd,EPOLL_CTL_MOD,connfd,&ev); } else if(events[i].events&EPOLLIN) { cout << "EPOLLIN" << endl; sockfd = events[i].data.fd; char buf; int n = 0; while ( (n = read(sockfd, &buf, 1)) > 0) { printf("%c\n",buf); } printf("%d\n",n); if (n == 0) { ev.data.fd=sockfd; epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,&ev); } else { ev.data.fd=sockfd; ev.events=EPOLLET|EPOLLOUT; epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } } else if(events[i].events&EPOLLOUT) { cout << "EPOLLOUT" << endl; sockfd = events[i].data.fd; //write(sockfd, line, n); //設定用於讀操作的檔案描述符 // sleep(5); ev.data.fd=sockfd; //設定用於注測的讀操作事件 ev.events=EPOLLIN|EPOLLET; //修改sockfd上要處理的事件為EPOLIN epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } } } return 0;}
用戶端使用telnet 命令,telnet 192.168.0.123 6002,串連後會收到資料,使用加強工作管理員將telnet進程掛起,不再列印輸出,server一直發送資料,直到Resource temporarily unavailable,退出while迴圈,等待out事件,在加強任務管理中喚醒telnet ,不會有任何輸出。