经常有个疑问:客户端需要发送数据给服务端,直接调用send(fd, ...)发送即可,看着和EPOLLOUT事件没有啥关系(说明:这种一般情况下可以,但是当缓存区满就GG了)。所以本文下面以边缘触发下的场景为例,重点说下什么时候会触发EPOLLOUT。 下面总结了三种场景下的EPOLLOUT并分别说明:
- 客户端连接场景 触发条件:客户端connect上服务端后,得到fd,这时候把fd添加到epoll 事件池里面后,因为连接可写,会触发EPOLLOUT事件
- 客户端发包场景 触发条件:缓冲区从满到不满,会触发EPOLLOUT事件
典型应用场景(数据包发送问题): 数据包发送逻辑:将数据包发完内核缓冲区–>进而由内核再将缓冲区的内容发送出去;这边send只是做了第一部分的工作,如果缓存区满的话send将会得到已发送的数据大小(成功放到缓冲区的),而不是整个数据包大小。
这种情况我们可以借助EPOLLOUT事件加以解决:如果send部分成功,则表示缓存区满了,那么把剩下的部分交给epoll,当检测到EPOLLOUT事件后,再将剩余的包发送出去。
- 重新注册EPOLLOUT事件 触发条件:如果当连接可用后,且缓存区不满的情况下,调用epoll_ctl将fd重新注册到epoll事件池(使用EPOLL_CTL_MOD),这时也会触发EPOLLOUT时间。
典型应用场景: send或write发包函数会涉及系统调用,存在一定开销,如果能将数据包聚合起来,然后调用writev将多个数据包一并发送,则可以减少系统调用次数,提高效率。
这时EPOLLOUT事件就派上用场了:当发包时,可以将先数据包发到数据buffer(用户缓存区)中存放,然后通过重新注册EPOLLOUT事件,从而触发EPOLLOUT事件时,再将数据包一起通过writev发送出去。