epoll in事件觸發時機
第一種情況,當設定了in,並且用戶端發送資料到達會觸發in事件
第二種情況,設定in,但是用戶端發送資料100位元組,伺服器讀取20位元組,不再觸發in直到再次設定in或有新資料到達。
第三種情況,設定in,如果client關閉(close socket或程式關閉),會觸發in事件,recv返回0,如果沒有del掉event,並且設定out事件,會觸發out事件,如果一直在設定(詢問)in事件,則會一直收到in事件。對於拔掉網線與斷電情況,recv返回-1,erron為ETIMEOUT,如果沒有設定keep-alive不會觸發in事件,如果設定了keep-alive會觸發一次in事件,但是在接收in事件後設定out事件後不會觸發out事件。
第三種情況,沒有設定keep-alive,用戶端close socket或程式關閉:
#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>#include <netinet/tcp.h>using namespace std;#define MAXLINE 5#define OPEN_MAX 100#define LISTENQ 20#define SERV_PORT 6002#define INFTIM 1000int socket_set_keepalive( int fd){ int ret, error, flag, alive, idle, cnt, intv; /* Set: use keepalive on fd */ alive = 1; if (setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, &alive, sizeof alive) != 0) { printf ("Set keepalive error: %s.\n" , strerror (errno)); return -1; } /* 10秒鐘無資料,觸發保活機制,發送保活包 */ idle = 10; if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE , &idle, sizeof idle) != 0) { printf ("Set keepalive idle error: %s.\n" , strerror (errno)); return -1; } /* 如果沒有收到回應,則5秒鐘後重發保活包 */ intv = 5; if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL , &intv, sizeof intv) != 0) { printf ("Set keepalive intv error: %s.\n", strerror (errno)); return -1; } /* 連續3次沒收到保活包,視為串連失效 */ cnt = 3; if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT , &cnt, sizeof cnt) != 0) { printf ("Set keepalive cnt error: %s.\n", strerror (errno)); return -1; } return 0;}void 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); //socket_set_keepalive(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; //static int i = 0; //if (i == 0) //{ 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 { cout << "error-222--->" << strerror(errno) << endl; } 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; //cout << "EPOLLOUT END" << 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;}
輸出如下:
EPOLLIN
0
EPOLLOUT
EPOLLIN
第三種情況,設定keep-alive,用戶端斷電或拔掉網線:
#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>#include <netinet/tcp.h>using namespace std;#define MAXLINE 5#define OPEN_MAX 100#define LISTENQ 20#define SERV_PORT 6002#define INFTIM 1000int socket_set_keepalive( int fd){ int ret, error, flag, alive, idle, cnt, intv; /* Set: use keepalive on fd */ alive = 1; if (setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, &alive, sizeof alive) != 0) { printf ("Set keepalive error: %s.\n" , strerror (errno)); return -1; } /* 10秒鐘無資料,觸發保活機制,發送保活包 */ idle = 10; if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE , &idle, sizeof idle) != 0) { printf ("Set keepalive idle error: %s.\n" , strerror (errno)); return -1; } /* 如果沒有收到回應,則5秒鐘後重發保活包 */ intv = 5; if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL , &intv, sizeof intv) != 0) { printf ("Set keepalive intv error: %s.\n", strerror (errno)); return -1; } /* 連續3次沒收到保活包,視為串連失效 */ cnt = 3; if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT , &cnt, sizeof cnt) != 0) { printf ("Set keepalive cnt error: %s.\n", strerror (errno)); return -1; } return 0;}void 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); socket_set_keepalive(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; //static int i = 0; //if (i == 0) //{ 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 { cout << "error-222--->" << strerror(errno) << endl; } 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; //cout << "EPOLLOUT END" << 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;}
拔掉網線或斷電,輸出如下:
error-->Resource temporarily unavailable
EPOLLOUT
EPOLLIN
-1
error-222--->Connection timed out