C++网路编程(2)

来源:互联网 发布:白芨吉利知胃病中草药 编辑:程序博客网 时间:2024/06/05 11:56

三、ET、LT两种工作模式
3、工作模式
  epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:
  LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
  ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。
  ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

代码示例:

#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <assert.h>#include <stdio.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <fcntl.h>#include <stdlib.h>#include <sys/epoll.h>#include <pthread.h>#define MAX_EVENT_NUMBER 1024#define BUFFER_SIZE 10/*将文件描述符设置为非阻塞的*/int setnonblocking(int fd){    int old_option = fcntl(fd,F_GETFL);    int new_option = old_option | O_NONBLOCK;    fcntl(fd,F_SETFL,new_option);    return old_option;}/*typedef union epoll_data {  void *ptr;  int fd;  __uint32_t u32;  __uint64_t u64;} epoll_data_t;struct epoll_event {  __uint32_t events; // Epoll events  epoll_data_t data; //User data variable};*/void addfd(int epollfd,int fd,bool enable_et){    epoll_event event;    event.data.fd = fd;    event.events = EPOLLIN; //关心的事件是可读事件    if(enable_et)    {        event.events |= EPOLLET; //开启ET模式    }    //poll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);    setnonblocking(fd); //将这个文件描述符设成非阻塞}//lt工作模式//events从内核中得到的事件集合void lt(epoll_event *events,int number,int epollfd,int listenfd){    char buf[BUFFER_SIZE];    for(int i = 0; i < number; i++)    {        int sockfd = events[i].data.fd; //取出事件的文件描述符        if(sockfd == listenfd)  //监听套接字有响应,代表有客户要进行连接,需要用accept处理这个连接        {            struct sockaddr_in client_address;            socklen_t client_addrlength = sizeof(client_address);            int connfd = accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);            addfd(epollfd,connfd,false);        }        else if(events[i].events & EPOLLIN) //其他fd产生的事件        {            printf("event trigger once\n");            memset(buf,'\0',BUFFER_SIZE);            int ret = recv(sockfd,buf,BUFFER_SIZE - 1,0);            if(ret <= 0)            {                close(sockfd);                continue;            }            printf("get %d bytes of content: %s\n",ret,buf);        }        else        {            printf("something else happend \n");        }    }}void et(epoll_event *events,int number,int epollfd,int listenfd){    char buf[BUFFER_SIZE];    for(int i = 0; i < number; i++)    {        int sockfd = events[i].data.fd;        if(sockfd == listenfd) //第一次取出server的句柄        {            struct sockaddr_in client_address;            socklen_t client_addrlength = sizeof(client_address);            int connfd = accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);            addfd(epollfd,connfd,true);        }        else if (events[i].events & EPOLLIN)        {            printf( "event trigger once\n" );            while( 1 )            {                memset( buf, '\0', BUFFER_SIZE );                int ret = recv( sockfd, buf, BUFFER_SIZE-1, 0 );                if( ret < 0 )                {                    // EAGAIN 代表没有到数据不代表接收错误                    if( ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) )                    {                        printf( "read later\n" );                        break;                    }                    close( sockfd );                    break;                }                else if( ret == 0 )   // 返回值0 代表连接中断                {                    close( sockfd );                }                else                {                    printf( "get %d bytes of content: %s\n", ret, buf );                }            }        }        else        {            printf("something else happend \n");        }    }}int main(int argc,char * argv[]){    //在命令行输入ip port    if(argc <= 2)    {        printf("usage: %s ip_address port_number\n",basename(argv[0]));        return 0;    }    const char * ip = argv[1];    int port = atoi(argv[2]);    int ret = 0;    struct sockaddr_in address;    //初始化结构体    bzero(&address,sizeof(address));    address.sin_family = AF_INET;    inet_pton(AF_INET,ip,&address.sin_addr);    address.sin_port = htons(port);    //套接字    int listenfd = socket(PF_INET,SOCK_STREAM,0);    assert(listenfd > 0);    //绑定    ret = bind(listenfd,(struct sockaddr*)&address,sizeof(address));    assert(ret != -1);    //监听    ret = listen(listenfd,5);    assert(ret != -1);    epoll_event events[MAX_EVENT_NUMBER];    //创建epoll句柄    int epollfd = epoll_create(5);    assert(epollfd != -1);    //将监听套接字放到epoll的监听列表中    addfd(epollfd,listenfd,true);    while(1)    {        int ret = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);//等待事件的产生,ret为要处理的事件的数目        if(ret < 0)        {            printf("epoll failure\n");            break;        }        lt(events,ret,epollfd,listenfd);    //    et(events,ret,epollfd,listenfd);    }    close(listenfd);    return 0;}