EPOLLERR和EPOLLHUP,不需要在epoll_event时针对fd作设置,一样也会触发;EPOLLRDHUP实测在对端关闭时会触发,需要注意的是:
EPOLLRDHUP的处理应该放在EPOLLIN和EPOLLOUT前面,处理方式应该 是close掉相应的fd后,作其他应用层的清理动作;EPOLLRDHUP会持续被触发;EPOLLRDHUP想要被触发,需要显式地在epoll_ctl调用时设置在events中;EPOLLOUT:
epoll_ctl添加相应fd,不然在LT模式下会频繁触发;write来发送数据,直到返回 EAGAIN后再使能EPOLLOUT,待触发后再继续write。EPOLLIN, 这里可以循环多次调用accept, 直至返回 EAGAIN, 同时适用于LT和ET。EPOLLRDHUP;EPOLLIN, 然后调用read, 此时返回的ssize_t类型结果为0;这部分有些内容上面已阐述过,这里统一归纳一下。
FIN 包,此时本端会触发事件如下:
EPOLLRDHUP (需要主动在epoll_ctal时加入events) EPOLLIN EPOLLOUT
EPOLLRDHUP,它明确表明对端已经关闭,处理时close相应fd后,无需再继续处理其他事件;EPOLLRDHUP的话,也可以处理EPOLLIN事件,此时read返回0, 同样表明对端已经关闭;EPOLLOUT事件里又向fd写了数据,数据只是写入到本地tcp发送缓冲区,此时write调用会返回成功,但是紧接着epoll_wait又会返回如下事件组合:
EPOLLERR EPOLLHUP EPOLLIN EPOLLOUT POLLRDHUP (需要主动在epoll_ctal时加入events)
可以看到相比之前多了EPOLLERR和EPOLLHUP,是因为之前收到了对端close时发送的FIN 包,此时再给对端发送数据,对端会返回RST包。
如果在收到RST包后,又向对端发送数据,会收到sigpipe异常,其默认处理是终止当前进程,此时可通过忽略此异常解决,忽略后write会返回-1, erron =32, Broken pipe:
signal(SIGPIPE, SIG_IGN);
Broker pipie这个异常,说到底是应用层没有对相应的fd在收到对端关闭通知时,作正确的处理所致,它并不是tcp/ip通讯层面的问题。
FIN包
tcp_fin.png
FIN包,而是发送RST,此时本端:1. 收到`RST`后的第一次写操作,写失败,errno = 104, Connection reset by peer; 之后将触发下列事件:
```
EPOLLIN
EPOLLOUT
EPOLLHUP
EPOLLRDHUP(需要主动在epoll_ctal时加入events)
```
2. 收到`RST`后的第二次及后序的写操作,写失败,在忽略了`SIGPIPE`后,erron =32, Broken pipe;
3. 收到`RST`后的读操作:errno = 104, Connection reset by peer
4. 下面可以看到发送了`RST`包:
tcp_rst.png
LT模式,socket fd可以设置成阻塞也可以设置成非阻塞;ET模式,socket fd只能设置成非阻塞; read时还有没有数据,一旦没有数据,又没有用非阻塞方式,则将一直阻塞在read调用上;LT模式下也每次循环读取,也有类似的问题;ET的触发,直接针对保存的fd继续其读操作。RST包,这个前面已经介绍过;FIN包;SO_LINGER改变close默认行为:
通过struct linger设置
linger.l_onoff linger.l_linger close行为 kernel行为 备注 0 为 disable 忽略 立即返回,同close的默认行为 尽力将发送缓存区中数据发送到对端,然后发送FIN包,四次挥手 > 0 为enable 0 立即返回 不走正常四次挥手,直接发送RST包,没有TIME_WAIT状态 > 0 为enbale 大于0 不管socket是否为blocking或noblocking, 都会阻塞直数据发送完成并收到对端的ACK, 或者linger.l_linger超时 如超时不走正常四次挥手,直接发送RST包,没有TIME_WAIT状态