229-epoll的LT和ET模式

epoll的LT和ET模式

epoll 对文件描述符有两种操作模式:LT(Level Trigger,电平触发)模式和 ET(EdgeTrigger,边沿触发)模式。LT 模式是默认的工作模式。当往 epoll 内核事件表中注册一个文件描述符上的 EPOLLET 事件时,epoll 将以高效的 ET 模式 来操作该文件描述符。
对于 LT 模式操作的文件描述符,当 epoll_wait 检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。这样,当应用程序下一次调用 epoll_wait 时,还会再次向应用程序通告此事件,直到该事件被处理。
对于 ET 模式操作的文件描述符,当 epoll_wait 检测到其上有事件发生并将此事件通知应用程序后,应用程序必须立即处理该事件,因为后续的epoll_wait 调用将不再向应用程序通知这一事件。所以 ET 模式在很大程度上降低了同一个 epoll 事件被重复触发的次数,因此效率比 LT 模式高。

select和poll读只能工作在LT模式下。epoll可以工作在LT和ET模式下。

LT代码演示

和上一章节的epoll代码一致。recv一次只读取一个字符
在这里插入图片描述
比如我发送“hello",而我recv一次只读取一个字符,当送过来的字符超过1个,服务器会怎么办?
在这里插入图片描述
我们送过来的数据量比我们recv一次接收的量要多,我们recv一次收不完,但是I/O函数会继续提醒我们,这个描述符上还有数据!我们会继续去读取数据,缓冲区有数据还没读完。一直循环,直到我们读完数据。然后select的返回值变为0。开始打印timeout。这种就是LT模式。但是它会触发不断的返回,一个描述符为了读取数据返回了好几次,其他描述符还有数据呢?如果都是一次读取一个字符,也不断的返回,导致处于不断的大循环不断的运转。
在这里插入图片描述

epoll的ET模式

我们现在的ET高效模式做法就是:如果检测到这个描述符上有数据,就只提醒应用程序1次,如果你没有处理完,I/O函数就不再提醒你了!!!要求程序员在接到I/O函数的通知后一次处理完,你要是没处理完或者压根没有处理,I/O函数就不会再提醒你,除非客户端又一次发送过来数据,就再提醒你一次。否则,不再提醒你。
我们先开启高效ET模式,看看情况
ET模式怎么开启?我们在添加描述符的时候,原本设置了一个事件:EPOLLIN,它还有一个事件:EPOLLET。
在这里插入图片描述
其他代码保持不变!一次只读取一个字符!
运行服务器端和客户端
在这里插入图片描述
在这里插入图片描述

它只读取到了一个h,没有读到ello\0。
是因为我们I/O函数收到以后,描述符上有数据,c=5,报告应用程序,这个描述符有数据,然后应用程序recv,一次读取一个字符,读取h,但是数据没有读完,但是I/O不再提醒我们。但是缓冲区上还有数据哦。容易丢数据,I/O函数只提醒1次,但是缓冲区有数据,没有提醒我们。
所以ET模式下,我们要在只提醒1次的情况下,把数据一次性全部读走!
我们通过命令查看
在这里插入图片描述
我们看到,接收缓冲区上还有5个字符。(ello\0)
在这里插入图片描述
我们在客户端上再敲下abc,服务器端会打印什么呢?是e还是a?
我们使用的都是TCP连接,TCP是可靠安全,面向连接的流式服务。不会丢数据,丢了会重发。未读取的数据仍然在接收缓冲区上!!!
回车一敲,客户端给服务器端再发送了一次数据,服务器端会触发一次读事件,提醒我们这个描述符上有读事件,但是我们再去读取的时候,还是只读取1个字符,应该读e,缓冲区放的是ello\0abc\0 所以这次输出来的是e
在这里插入图片描述
ET模式就是只提醒1次
在这里插入图片描述
我们的服务器端出了什么问题?为什么崩了?输出了2个l?
我们客户端关闭以后,对于我们的服务器端来讲,就会在这个描述符上产生一个读事件,因为我们每一次读都会读到数据,但是对方已经关闭描述符了,但是我们的缓冲区上有数据,所以读一下给客户端send一下,再读再send,这时候就有问题,引发异常,程序终止。
在这里插入图片描述

epoll的ET模式正确写法

如果我们的buff要是定义500个大小,我们一次收不完。我们是流式服,可以控制对方一次发不超过多少字符,但是不能控制对方连续发好多次,积攒在缓冲区上,我们一次收不完。
但是我们要解决这个问题!I/O提醒一次,我们就要读完。
但是问题又出来了,我们要越过I/O函数,I/O函数没有提醒我们,我们得循环去recv,直到读完。但是我们第一次读,不会被阻塞,因为提醒我们有数据,肯定读得到数据,但是如果我们第一次就把数据读完了,循环去recv第二次读取的话就被阻塞住了。以此类推。会被阻塞的。

如何解决呢???
把描述符设置为非阻塞 模式!
非阻塞模式就是我们在进行第一次recv的时候,循环去读取,有多少数据读多少,如果缓冲区没有数据,我们就直接返回-1(失败,读完了)。 不用0是因为0有特殊意义:recv返回值0,代表对方关闭了!!! 退出条件:返回-1的时候。
我们用这个设置非阻塞模式。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上代码!
在这里插入图片描述
在这里插入图片描述
可以在添加描述符之前设置或者在添加描述符之后设置。
内核事件表中,加不加描述符,都可以设置成非阻塞模式。
在这里插入图片描述
在这里插入图片描述
我们在recv中出错,也是return -1,直接移除关闭描述符。
当对方关闭描述符或者我们出错了,返回-1。
recv返回0代表正常运行。
recv的返回-1我们得分为两种情况,一种情况是真的出错了,一种是因为非阻塞模式,读完了,返回-1;
在这里插入图片描述
我们怎么知道是真的出错还是读完了返回-1?
我们查看一下recv,有个返回值
在这里插入图片描述
失败的情况有错误码,下面这个错误码就是,我们数据没准备好,返回错误,我们不认为这是错误!
在这里插入图片描述
下面这个错误码,我们认为是真的失败了。对方关闭了描述符我们还使用,未查到描述符,就是失效!
在这里插入图片描述
这个错误值在哪呢?
我们有个头文件。
在这里插入图片描述
这个变量是错误值,出错了,会把错误码置成其相应的值。
根据出错的错误类型,得到具体出错的原因。
在这里插入图片描述
在这里插入图片描述

运行程序!

在这里插入图片描述
完成了!!!

ET模式必须是非阻塞模式,LT是阻塞模式或非阻塞模式都可以

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

林林林ZEYU

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

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

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

打赏作者

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

抵扣说明:

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

余额充值