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

标签 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)

客户端输入

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.

服务器端输出

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

奇怪的是,客户端关闭连接后,epoll还能轮询recv和发送事件!为什么 EPOLLERR 事件永远不会发生?使用 EPOLLHUP 也是一样。

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

如果您在 EPOLLIN 事件中什么也得不到,则将连接视为已关闭是否正确?

最佳答案

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

实验我发现 EPOLLHUP 只与 EPOLLERR 一起发生,原因可能是 python 与 epoll 和低级 IO 的接口(interface)方式,通常 recv 会返回 -1 并将 errno 设置为 EAGAIN 当没有可用时一个非阻塞的 recv,然而 python 使用 ''(没有返回)来发出 EOF 信号。

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

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

关于python - epoll 如何在 Python 中检测客户端关闭?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/793646/

相关文章:

Linux:Recv 随机返回零

python - 在 jinja2 中是否有直接格式化数字的方法?

python - 为什么装饰类会破坏描述符协议(protocol),从而阻止 staticmethod 对象按预期运行?

c++ - timerfd 和读取

epoll - 为什么 ePoll 的扩展性比 Poll 更好?

c++ - epoll等待修改文件描述符集

Python Django 错误 : version GLIBC_PRIVATE not defined

python - 如何使用 pycogent 在 python 2.7 中创建祖先序列?

python - Discord.py 安装后不工作

c - 关于 epoll_ctl()