epoll的ET工作模式和LT工作模式

1.epoll的两种工作模式介绍

epoll的两种模式ET和LT:
LT模式(水平触发):是缺省的工作方式,并且同时支持block和non-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪,然后可以对这个就绪的fd进行io操作,如果你不做任何操作,内核还会继续通知你的。(只要缓冲区有数据就会一直通知)
ET模式(边沿触发):是高速工作方式,只支持no-block socket。在这种模式下,当文件描述符从未就绪变为就绪时,内核会通过epoll告诉你。然后,内核会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,知道你做了某些操作导致那个文件描述符不再为就绪状态了。(缓冲区有数据只会通知一次,之后再有数据才回通知)

2.测试ET模式和LT模式的区别

源代码(wrap相关文件在文章里):

#include"wrap.h"
#include<stdio.h>
#include<sys/epoll.h>
#include<errno.h>
#include<ctype.h>
int main(){
    int lfd;
    int ret;
    int n;
    int epfd;
    int i;
    int nready;
    int cfd;
    int sockfd;
    char buf[1024];
    socklen_t socklen;
    struct sockaddr_in svraddr;
    struct epoll_event ev;
    struct epoll_event events[1024];
    //创建socke
    lfd=socket(AF_INET,SOCK_STREAM,0);
    //设置端口复用
    int opt=1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
    //绑定
    bzero(&svraddr,sizeof(svraddr));
    svraddr.sin_addr.s_addr=htonl(INADDR_ANY);
    svraddr.sin_port=htons(8888);
    svraddr.sin_family=AF_INET;
    Bind(lfd,(struct sockaddr*)&svraddr, sizeof(svraddr));
    //设置监听
    Listen(lfd,128);
    //创建一个epoll树
    epfd=epoll_create(1024);
    if(epfd<0){
        perror("create epoll error");
        return -1;
    }
    //将监听文件描述符上epoll树
    ev.events=EPOLLIN;
    ev.data.fd=lfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
    while (1)
    {
        nready=epoll_wait(epfd,events,1024,-1);
        if(nready<0){
            perror("epoll_wait error");
            if(errno==EINTR){
                continue;
            }
            break;
        }
        for(i=0;i<nready;i++){
            sockfd=events[i].data.fd;
            //有客户端请求到来
            if(sockfd==lfd){
                cfd=Accept(lfd,NULL,NULL);
                //将cfd对应的事件上树
                ev.data.fd=cfd;
                ev.events=EPOLLIN;//可读事件
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
                continue;
            }
            //有客户端发送数据到来
            memset(buf,0x00,sizeof(buf));
            //n=Read(sockfd,buf,sizeof(buf));
            n=recv(sockfd,buf,2,0);
            if(n<=0){
                printf("n==%d, buf==%s\n",n,buf);
                Close(sockfd);
                //将sockfd对应的事件节点从epoll树上删除
                epoll_ctl(cfd,EPOLL_CTL_DEL,sockfd,NULL);
                perror("read error or client error");
                continue;
            }else{
                printf("n==%d, buf==%s\n",n,buf);
                for(int k=0;k<n;k++){
                    buf[k]=toupper(buf[k]);
                }
                //Write(sockfd,buf,n);
                send(sockfd,buf,n,0);
            }
        }
    }
    Close(epfd);
    Close(lfd);
    return 0;
}

在这里插入图片描述
在这里插入图片描述
可以发现执行了四次,epoll默认情况下为水平模式,在这种模式下,若数据一次性没有读完,缓冲区还有可读数据,则epoll_wait还会再次通知。如设置成ET模式:
源代码:

#include"wrap.h"
#include<stdio.h>
#include<sys/epoll.h>
#include<errno.h>
#include<ctype.h>
int main(){
    int lfd;
    int ret;
    int n;
    int epfd;
    int i;
    int nready;
    int cfd;
    int sockfd;
    char buf[1024];
    socklen_t socklen;
    struct sockaddr_in svraddr;
    struct epoll_event ev;
    struct epoll_event events[1024];
    //创建socke
    lfd=socket(AF_INET,SOCK_STREAM,0);
    //设置端口复用
    int opt=1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
    //绑定
    bzero(&svraddr,sizeof(svraddr));
    svraddr.sin_addr.s_addr=htonl(INADDR_ANY);
    svraddr.sin_port=htons(8888);
    svraddr.sin_family=AF_INET;
    Bind(lfd,(struct sockaddr*)&svraddr, sizeof(svraddr));
    //设置监听
    Listen(lfd,128);
    //创建一个epoll树
    epfd=epoll_create(1024);
    if(epfd<0){
        perror("create epoll error");
        return -1;
    }
    //将监听文件描述符上epoll树
    ev.events=EPOLLIN;
    ev.data.fd=lfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
    while (1)
    {
        nready=epoll_wait(epfd,events,1024,-1);
        if(nready<0){
            perror("epoll_wait error");
            if(errno==EINTR){
                continue;
            }
            break;
        }
        for(i=0;i<nready;i++){
            sockfd=events[i].data.fd;
            //有客户端请求到来
            if(sockfd==lfd){
                cfd=Accept(lfd,NULL,NULL);
                //将cfd对应的事件上树
                ev.data.fd=cfd;
                ev.events=EPOLLIN | EPOLLET;//可读事件
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
                continue;
            }
            //有客户端发送数据到来
            memset(buf,0x00,sizeof(buf));
            //n=Read(sockfd,buf,sizeof(buf));
            n=recv(sockfd,buf,2,0);
            if(n<=0){
                printf("n==%d, buf==%s\n",n,buf);
                Close(sockfd);
                //将sockfd对应的事件节点从epoll树上删除
                epoll_ctl(cfd,EPOLL_CTL_DEL,sockfd,NULL);
                perror("read error or client error");
                continue;
            }else{
                printf("n==%d, buf==%s\n",n,buf);
                for(int k=0;k<n;k++){
                    buf[k]=toupper(buf[k]);
                }
                //Write(sockfd,buf,n);
                send(sockfd,buf,n,0);
            }
        }
    }
    Close(epfd);
    Close(lfd);
    return 0;
}

在这里插入图片描述
在这里插入图片描述
若将epoll模式设置为ET模式,若读数据的时候一次性没有读完,则epoll_wait不再通知,知道下次有新的数据发来。

3.ET模式下的非阻塞模式

在ET模式下,需要循环读完数据,知道读完数据,但是读完数据会阻塞;若要一次性读完数据,还需要将通信文件描述符设置为非阻塞模式。
源代码:

#include"wrap.h"
#include<stdio.h>
#include<sys/epoll.h>
#include<errno.h>
#include<ctype.h>
#include<fcntl.h>
int main(){
    int lfd;
    int ret;
    int n;
    int epfd;
    int i;
    int nready;
    int cfd;
    int sockfd;
    char buf[1024];
    socklen_t socklen;
    struct sockaddr_in svraddr;
    struct epoll_event ev;
    struct epoll_event events[1024];
    //创建socke
    lfd=socket(AF_INET,SOCK_STREAM,0);
    //设置端口复用
    int opt=1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
    //绑定
    bzero(&svraddr,sizeof(svraddr));
    svraddr.sin_addr.s_addr=htonl(INADDR_ANY);
    svraddr.sin_port=htons(8888);
    svraddr.sin_family=AF_INET;
    Bind(lfd,(struct sockaddr*)&svraddr, sizeof(svraddr));
    //设置监听
    Listen(lfd,128);
    //创建一个epoll树
    epfd=epoll_create(1024);
    if(epfd<0){
        perror("create epoll error");
        return -1;
    }
    //将监听文件描述符上epoll树
    ev.events=EPOLLIN;
    ev.data.fd=lfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
    while (1)
    {
        nready=epoll_wait(epfd,events,1024,-1);
        if(nready<0){
            perror("epoll_wait error");
            if(errno==EINTR){
                continue;
            }
            break;
        }
        for(i=0;i<nready;i++){
            sockfd=events[i].data.fd;
            //有客户端请求到来
            if(sockfd==lfd){
                cfd=Accept(lfd,NULL,NULL);
                //将cfd对应的事件上树
                ev.data.fd=cfd;
                ev.events=EPOLLIN | EPOLLET;//可读事件
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
                //将cfd设置为非阻塞
                int flag =fcntl(cfd,F_GETFL);
                flag |=O_NONBLOCK;
                fcntl(cfd,F_SETFL,flag);
                continue;
            }
            //有客户端发送数据到来
            memset(buf,0x00,sizeof(buf));
            while(1){
                //n=Read(sockfd,buf,sizeof(buf));
                n=Read(sockfd,buf,2);
                //读完数据
                if(n==-1){
                    printf("read over, n=%d\n",n);
                    break;
                }
                //对方关闭连接或者异常
                if(n==0||(n<0&&n!=-1)){
                    printf("n==%d, buf==%s\n",n,buf);
                    Close(sockfd);
                    //将sockfd对应的事件节点从epoll树上删除
                    epoll_ctl(cfd,EPOLL_CTL_DEL,sockfd,NULL);
                    break;
                }else{
                    printf("n==%d, buf==%s\n",n,buf);
                    for(int k=0;k<n;k++){
                        buf[k]=toupper(buf[k]);
                    }
                    Write(sockfd,buf,n);
                }
            }
            
        }
    }
    Close(epfd);
    Close(lfd);
    return 0;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZhInen丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值