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]: <socket.socket fd=15, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('192.168.1.168', 7777), raddr=('192.168.1.120', 8790)>
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 可以作为一种读关闭的标志,注意 不能读的意思内核不能再往内核缓冲区中增加新的内容。已经在内核缓冲区中的内容,用户态依然能够读取到。

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)。

hex(17) 为 0x11,刚好是  EPOLLIN | EPOLLHUP .

3、对端发送 RST.

发送 RST 的常见场景。

1)  系统崩溃重启( 进程崩溃,只要内核是正常工作都还能兜底,发送的是FIN,不是这里讨论的RST ),四元组消失。此时收到任何数据,都会响应 RST.

2)设置 linger 参数,l_onoff 为 1 开启,但是 l_linger = 0 超时参数为0. 此时close() 将直接发送 RST.

3) 接收缓冲区 中还有数据,直接 close(), 接收缓冲区中的内容丢弃,直接发送 RST.

4)调用 close 时,close 会立马发送一个 FIN。注意:仅仅从 FIN 数据包上,无法断定对端是 close 还是仅仅 shutdown(SHUT_WR) 半关闭。往对端发送数据,若对端已经 close(),对端会回复 RST.

这里仅仅使用第 2 种场景来产生 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]: <socket._socketobject at 0x1cca0c0>
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'

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

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

服务端的抓包结果截图:

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

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

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

I want to use AutoLayout to size and layout a view in a manner that is reminiscent of UIImageView's 接触约束挺好玩的,对于页面设计帮助不是一星半点的,但是想设计出好的页面品质来,还是要对约束深入研究一番的。 技术成长就是从:不敢->就做了能咋地->没啥事儿->胆子变大->技术成长->遇到问题->解决问题。 可能你不相信,但是我的经历告诉我,技术就是勇于突破。 最原始,我仅能使用固定大小和方位... 在内核2.6.17(不含)以前版本,要想知道对端是否关闭socket,上层应用只能通过调用recv来进行判断,在2.6.17以后,这种场景上层只需要简单处理即可。 一、未使用 EPOLL RDHUP 服务端代码: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <s... epoll 事件之 EPOLL RDHUP | Solrex² - 杨文博的博客,记录我的生活、思想、技术和梦想You are here: Home » 开源 » Linux » epoll 事件之 EPOLL RDHUP epo... [1] 当客户端向一个没有在listen的服务器端口发送的connect的时候 服务器会返回一个RST 因为服务器根本不知道这个4元组的存在  [2] 当已经建立好连接的一对客户端和服务器,客户端突然操作系统崩溃,或者拔掉 问题场景是:客户端关闭了连接,然后会不断收到标题里的信号 问题原因是:收到 EPOLL RDUP事件时,没有执行close(events[i].data.fd),也就是没有客户端关闭连接后,服务端没有及时关闭对应的socket连接 可写触发: epoll out 对方主动关闭socket: epoll rdhup epoll in epoll out 自己方socket出现问题才会触发 epoll err epoll hup ,但是我在项目中发现也会同时触发 epoll in和 epoll rdhup epoll err和 epoll hup 是默认注册的所以我们在注册时间的时候不用注册了。 epoll rdh 1、listen fd,有新连接请求,对端发送普通数据 触发 EPOLL IN。 2、带外数据,只触发 EPOLL PRI。 3、对端正常关闭(程序里close(),shell下kill或ctr+c),触发 EPOLL IN和 EPOLL RDHUP ,但是不触发 EPOLL ERR 和 EPOLL HUP 。 再man epoll _ctl看下后两个事件的说明,这两个应该是本端(server端)出错才触发的 在对系统问题进行排查时,我发现了一个奇怪的现象:明明是对方断开连接,系统却报告一个查询失败的错误,但从用户角度来看请求的结果正常返回,没有任何问题。 对这个现象深入分析后发现,这是一个基于 epoll 的连接池实现上的问题,或者说是特性。 首先解释一下导致这个现象的原因。 在使用 epoll 时,对端正常断开连接(调用close()),在服务器端会触发一个 epoll 事件。在低于2.6.17版本的内核 EPOLL RDHUP 是从 Linux 内核2.6.17开始由GNU引入的事件。 当socket接收到对方关闭连接时的请求之后触发,有可能是TCP连接被对方关闭,也有可能是对方关闭了写操作。 如果不适用 EPOLL RDHUP 事件,我们也可以单纯的使用 EPOLL IN事件然后根据recv函数的返回值来判断socket上收到的是有效数据还是对方关闭连接的请求。 游戏服务器中很多平台都会要求进行数据上报,数据上报都是使用http协议。我们游戏服务器的 网络 都是自己实现的,没有使用第三方 网络 库,所以在进行数据上报的时候也就自己用c++实现简单的http请求。由于 网络 一直都是使用 EPOLL RDHUP 作为断开 网络 处理,该事件会比使用 EPOLL IN和 EPOLL OUT高效,但是在http协议中使用的时候可能会存在问题。 EPOLL RDHUP 表示的是对方读挂起,可能对方还... epoll 的使用 epoll 只有以下的三个系统函数调用: epoll _create, epoll _ctl和 epoll _wait: int epoll _create(int size);其中参数: (1)size指明了生成描述符的最大范围; 该函数返回一个 epoll 专用的描述符(fd) int epoll _ctl(int epfd,int op, int fd, struct epoll _event ...