EPOLLRDHUP vs EPOLLHUP

EPOLLRDHUP 表示读关闭。不是所有的内核版本都支持,没有查证。有两种场景:

1、对端发送 FIN (对端调用close 或者 shutdown(SHUT_WR)).

2、本端调用 shutdown(SHUT_RD). 当然,关闭 SHUT_RD 的场景很少。

测试环境为  Linux localhost.localdomain 3.10.0-957.el7.x86_64 #1 SMP Thu Nov 8 23:39:32 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux。

使用 python 的服务端测试过程,客户端仅仅建立 TCP 连接,不发送任何数据。第 10 条指令 poll 超时,返回一个空的列表。通过关闭本端的读取,再次 poll 可以看到,返回 hex(8193) = 0x2001 表示EPOLLRDHUP 和 EPOLLIN 事件。

In [1]: import socket                                                                                                                                                                                                            

In [2]: serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)                                                                                                                                             

In [3]: serv.bind(("0.0.0.0", 7777))                                                                                                                                                                                             

In [4]: serv.listen(5)                                                                                                                                                                                                           

In [5]: import select                                                                                                                                                                                                            

In [6]: epoll_fd = select.epoll()                                                                                                                                                                                                

In [7]: client,addr = serv.accept()                                                                                                                                                                                              

In [8]: client                                                                                                                                                                                                                   
Out[8]: 

In [9]: epoll_fd.register(client, select.EPOLLIN | select.EPOLLRDHUP)                                                                                                                                                            

In [10]: epoll_fd.poll(5)                                                                                                                                                                                                        
Out[10]: []

In [11]: client.shutdown(socket.SHUT_RD)                                                                                                                                                                                         

In [12]: epoll_fd.poll(5)                                                                                                                                                                                                        
Out[12]: [(15, 8193)]

In [13]: for i in dir(select): 
    ...:     if "EPOLL" in i: 
    ...:         print(i, hex(getattr(select, i))) 
    ...:                                                                                                                                                                                                                         
EPOLLERR 0x8
EPOLLET -0x80000000
EPOLLHUP 0x10
EPOLLIN 0x1
EPOLLMSG 0x400
EPOLLONESHOT 0x40000000
EPOLLOUT 0x4
EPOLLPRI 0x2
EPOLLRDBAND 0x80
EPOLLRDHUP 0x2000
EPOLLRDNORM 0x40
EPOLLWRBAND 0x200
EPOLLWRNORM 0x100
EPOLL_CLOEXEC 0x80000

本端不动,客户端 shutdown(SHUT_WR) 得到一样的结果。 

EPOLLRDHUP vs EPOLLHUP_第1张图片

EPOLLHUP 表示读写都关闭

1、本端调用shutdown(SHUT_RDWR)。 不能是close,close 之后,文件描述符已经失效。

In [9]: epoll_fd.register(client, select.EPOLLIN | select.EPOLLRDHUP)                                                                                                                                                            

In [10]: epoll_fd.poll(5)                                                                                                                                                                                                        
Out[10]: []

In [11]: client.shutdown(socket.SHUT_RDWR)                                                                                                                                                                                       

In [12]: epoll_fd.poll(5)                                                                                                                                                                                                        
Out[12]: [(15, 8209)]

In [13]: hex(8209)                                                                                                                                                                                                               
Out[13]: '0x2011'

0x2011 刚好对应 EPOLLIN | EPOLLRDHUP | EPOLLHUP.

2、本端调用 shutdown(SHUT_WR),对端调用 shutdown(SHUT_WR)。

 

EPOLLRDHUP vs EPOLLHUP_第2张图片

3、对端发送 RST. 1) 对端系统崩溃重启,四元组消失。2)设置 linger 参数,l_onoff 为 1 开启,但是 l_linger = 0 超时参数为0.此时如果本端发送缓冲区中还有数据,本端 close() 将直接发送 RST.

补上测试数据,测试环境 Linux localhost.localdomain 2.6.32-696.el6.x86_64 #1 SMP Tue Mar 21 19:29:05 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux.

In [1]: import socket

In [2]: serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)

In [3]: serv.bind(("0.0.0.0", 7777))

In [4]: serv.listen(5)

In [5]: import select

In [6]: epoll_fd = select.epoll()

In [7]: client,addr = serv.accept()

In [8]: client
Out[8]: 

In [10]: addr
Out[10]: ('192.168.1.237', 59834)

In [11]: client.fileno()
Out[11]: 10


In [13]: client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)

In [14]: client.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
Out[14]: 131072

In [21]: epoll_fd.register(client)

In [22]: epoll_fd.poll(3)
Out[22]: [(10, 4)]

In [23]: client.fileno()
Out[23]: 10

In [24]: epoll_fd.modify(client, select.EPOLLIN)

In [25]: epoll_fd.poll(3)
Out[25]: []


In [26]: epoll_fd.poll(3)
Out[26]: [(10, 1)]

In [27]: epoll_fd.poll(3)
Out[27]: [(10, 25)]

In [28]: hex(25)
Out[28]: '0x19'

客户端:

EPOLLRDHUP vs EPOLLHUP_第3张图片

服务器上获得的接收缓冲区大小为 131072,所以客户端这里发送的数据长度稍微大于 131072 的 131080 字节,填满服务器的接收缓冲区,使得客户端的发送缓冲区中仍然存有数据。这个时候制造发送RST的linger条件后,直接 close 客户端。

客户端发送数据之前,服务端 poll 返回的结果时空(第25条输出)。客户端发送数据之后, close 之前,服务端 poll 的结果是 EPOLLIN(第26条输出 1);一旦客户端 close 发送了 RST,服务器 poll 的结果变成 EPOLLIN | EPOLLERR | EPOLLHUP(第27条输出 25).

服务端的抓包结果截图:

EPOLLRDHUP vs EPOLLHUP_第4张图片

1、最后客户端的确有发送 RST。

2、一旦服务端的接收缓冲区满;服务端将一直给客户端发送零窗口通告,让客户端停止发送。

 

最后,如果仅仅关闭写(shutdown(SHUT_WR)),epoll 将不会有任何返回。

 

你可能感兴趣的:(TCPIP,Linux)