epoll一點思考(3)

來源:互聯網
上載者:User

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.