LT模式适合监听fd和简单业务逻辑,listenfd必须用LT以防连接丢失;ET模式需配非阻塞fd并循环读写至EAGAIN,适合高并发小包场景;混合使用(listenfd用LT、connfd用ET)是典型且合理方案。

LT模式适合监听fd和简单业务逻辑
监听 socket(listenfd)几乎必须用 LT 模式。因为内核 accept 队列可能一次入队多个连接请求,而 accept() 每次只取一个。如果用 ET,一次 epoll_wait 返回后没把队列清空,后续新连接就可能被漏掉——这不是 bug,是 ET 的设计行为。
LT 对初学者友好:不读完数据、不写满缓冲区都不要紧,下次 epoll_wait 还会提醒你。适合调试阶段、低并发服务或协议解析逻辑较重(比如 HTTP 头未收全就暂停处理)的场景。
- 默认启用,无需额外设置
EPOLLET - 可搭配阻塞或非阻塞 fd,但推荐统一用非阻塞,避免意外卡住
- 注意性能隐患:大量就绪但暂不处理的 fd 会反复触发,拖慢事件循环
ET模式必须配非阻塞 fd + 循环读写
ET 的核心约束不是“快”,而是“不重复通知”。一旦 epoll_wait 返回 EPOLLIN,你就得用 recv() 一直读到 EAGAIN 或 EWOULDBLOCK,否则剩余数据就沉底了——下一次触发只等新数据到来,而不是等你补读。
常见错误是只调一次 recv() 就返回,结果连接里还剩几百字节没处理,业务层完全感知不到。
- 注册时必须显式加
EPOLLET:例如events = EPOLLIN | EPOLLET -
fcntl(fd, F_SETFL, O_NONBLOCK)必须在epoll_ctl(ADD)前完成 - 读写都要 while 循环,不能依赖单次系统调用
- 对端关闭时
recv()返回 0,这个要单独判断,别和EAGAIN混淆
混合使用是常见且合理的方案
一个典型 WebServer 的做法是:listenfd 用 LT,所有 connfd(已建立的客户端连接)用 ET。这样既保证连接不丢,又让数据收发路径保持高性能。
有些项目还会按业务分层:管理类连接(如监控端口、配置热更)用 LT 保稳;主流量通道(如 HTTP/HTTPS worker)用 ET 拼吞吐。
- 不要全局一刀切选 LT 或 ET
- 同一个 epoll 实例里混用 LT 和 ET fd 完全合法,内核分别处理
- 注意:ET fd 如果误设为阻塞,
recv()可能直接阻塞住整个事件循环
ET 的性能优势只在高并发+小包场景明显
ET 减少的是 epoll_wait 的唤醒次数,不是单次系统调用开销。如果你的连接平均每次收 10KB 以上,或者并发量长期低于 1000,LT 和 ET 的实际差异微乎其微。
真正容易翻车的是 ET 下的错误处理逻辑:比如 send() 返回部分成功(EAGAIN 前已发若干字节),这时你要维护发送缓冲区偏移,而不是直接丢弃未发完的数据。
- 压测前务必验证 ET 路径是否真能收完整包(尤其含粘包、半包的协议)
- 日志里打上
recv()返回值和errno,ET 下EAGAIN是常态,EINTR也要 continue - 别为了“听起来高级”强行上 ET,LT 在多数内部服务中更省心
复杂点在于 ET 没有容错余地——它把“是否读完”的责任完全交给你,连内核都不会提醒。哪怕只漏一次 EAGAIN 判断,就可能让一条连接静默卡死。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











