ET模式必须配非阻塞I/O,因ET仅在接收缓冲区由空变非空时触发一次EPOLLIN,若用阻塞socket且未读尽数据,后续epoll_wait将不再通知;监听套接字必须用LT,否则会丢连接。
et 模式必须配非阻塞 i/o,lt 模式可以混用阻塞/非阻塞,但混用会掩盖逻辑缺陷,不建议。
为什么 ET 模式下 recv 会卡住?
因为 ET 只在 socket 接收缓冲区从空变为非空时触发一次 EPOLLIN;如果用阻塞 socket 调用 recv,而缓冲区数据又没读完(比如只读了前 100 字节,还剩 50 字节),后续再调用 epoll_wait 就不会返回该 fd —— 内核认为“状态没变”,不会再次通知。
- 必须把监听套接字和已连接套接字都设为非阻塞:
fcntl(fd, F_SETFL, O_NONBLOCK) -
recv需循环读,直到返回-1且errno == EAGAIN或EWOULDBLOCK - 漏掉这个判断,就会永远等不到下一次事件,连接看似“卡死”
EPOLLET 和 EPOLLLT 的设置位置与常见误写
EPOLLET 是 flag,要跟 EPOLLIN 或 EPOLLOUT 一起用,通过 epoll_ctl 的 event.events 字段传入;它不是独立事件类型,也不能单独设置。
- 正确写法:
event.events = EPOLLIN | EPOLLET - 错误写法:
event.events = EPOLLET(没配EPOLLIN,等于没监听读事件) - 错误写法:
event.events = EPOLLIN | EPOLLLT(EPOLLLT不存在,应是EPOLLET的反向误拼) - LT 是默认行为,不加
EPOLLET就是 LT,无需显式写EPOLLLT
监听套接字(LFD)该用 LT 还是 ET?
必须用 LT。因为 accept 一次只能取一个连接,但内核的监听队列可能积压多个已完成三次握手的连接。ET 模式下,仅第一次有新连接到达时触发一次 EPOLLIN,若没把队列里所有连接 accept 完,剩下的就永远沉底。
- LT 模式:只要监听队列非空,
epoll_wait就持续返回 LFD 就绪,可反复accept直到返回-1+EAGAIN - ET 模式:哪怕队列里有 10 个待 accept 连接,也只通知一次,极易丢连接
- 实践中:LFD 固定用 LT;业务连接(CFD)按需选 ET(高并发小包)或 LT(兼容性优先)
ET 模式真正的复杂点不在设置,而在事件处理闭环:每次触发后必须穷尽缓冲区、必须检查 EAGAIN、必须确保 socket 始终非阻塞——少一个环节,问题就藏得极深,复现困难。


















