Python中epoll如何检测客户端关闭?

5 python epoll

这是我的服务器

"""Server using epoll method"""

import os
import select
import socket
import time

from oodict import OODict

addr = ('localhost', 8989)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(8)
s.setblocking(0) # Non blocking socket server
epoll = select.epoll()
epoll.register(s.fileno(), select.EPOLLIN) # Level triggerred

cs = {}
data = ''
while True:
    time.sleep(1)
    events = epoll.poll(1) # Timeout 1 second
    print 'Polling %d events' % len(events)
    for fileno, event in events:
        if fileno == s.fileno():
            sk, addr = s.accept()
            sk.setblocking(0)
            print addr
            cs[sk.fileno()] = sk
            epoll.register(sk.fileno(), select.EPOLLIN)

        elif event & select.EPOLLIN:
            data = cs[fileno].recv(4)
            print 'recv ', data
            epoll.modify(fileno, select.EPOLLOUT)
        elif event & select.EPOLLOUT:
            print 'send ', data
            cs[fileno].send(data)
            data = ''
            epoll.modify(fileno, select.EPOLLIN)

        elif event & select.EPOLLERR:
            print 'err'
            epoll.unregister(fileno)
Run Code Online (Sandbox Code Playgroud)

客户端输入

ideer@ideer:/home/chenz/source/ideerfs$ telnet localhost 8989
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
123456
123456
^]

telnet> q
Connection closed.
Run Code Online (Sandbox Code Playgroud)

服务器端输出

ideer@ideer:/chenz/source/ideerfs$ python epoll.py 
Polling 0 events
Polling 0 events
Polling 1 events
('127.0.0.1', 53975)
Polling 0 events
Polling 1 events
recv  1234
Polling 1 events
send  1234
Polling 1 events
recv  56

Polling 1 events
send  56

Polling 0 events
Polling 0 events
Polling 0 events
Polling 1 events
recv  
Polling 1 events
send  
Polling 1 events
recv  
Polling 1 events
send  
Polling 1 events
recv  
Polling 1 events
send  
Polling 1 events
recv  
^CTraceback (most recent call last):
  File "epoll.py", line 23, in <module>
    time.sleep(1)
KeyboardInterrupt
Run Code Online (Sandbox Code Playgroud)

奇怪的是,客户端关闭连接后,epoll仍然可以轮询recv并发送事件!为什么 EPOLLERR 事件从未发生?如果使用 EPOLLHUP 也是一样的。

我注意到 EPOLLERR 事件仅在您尝试写入关闭的连接时发生。除此之外,还有其他方法可以判断连接是否已关闭吗?

如果在 EPOLLIN 事件中没有得到任何结果,将连接视为已关闭是否正确?

Kje*_*sen 5

EPOLLERR 和 EPOLLHUP 永远不会在帖子中粘贴的代码中发生,因为它们总是与 EPOLLIN 或 EPOLLOUT 一起发生(其中几个可以一次设置),因此 if/then/else 总是选择一个EPOLLIN 或 EPOLLOUT。

实验我发现EPOLLHUP仅与EPOLLERR一起发生,其原因可能是python与epoll和低级IO的接口方式,通常recv会返回-1并将errno设置为EAGAIN,当非非-上没有可用的时候阻塞recv,但是python使用''(没有返回)来表示EOF。

关闭 telnet 会话仅关闭 tcp 连接的那一端,因此在您这边调用 recv 仍然完全有效,tcp 接收缓冲区中可能有待处理的数据,您的应用程序尚未读取这些数据,因此不会触发错误条件。

似乎EPOLLIN和返回空字符串的recv表明另一端已关闭连接,但是,使用旧版本的python(在引入epoll之前)和管道上的普通选择,我经历过read 返回的 '' 并不表示 EOF 只是缺少可用数据。


Joh*_*ery 0

您是否只需将掩码组合在一起即可同时使用 EPOLLHUP 和 EPOLLIN:


epoll.register(sk.fileno(), select.EPOLLIN | select.EPOLLHUP)

虽然说实话我对 epoll 库不太熟悉,所以这只是一个建议......