如何判断连接是否在Python中死亡
我希望我的python应用程序能够分辨出另一端的套接字何时被丢弃。 有没有这样的方法?
这取决于你“丢弃”的意思。 对于TCP套接字,如果另一端通过close()或进程终止来closures连接,那么通过读取文件结尾或读取错误,通常会将errno设置为任何连接同行“是由您的操作系统。 对于python,你会读取一个零长度的string,或者当你尝试读取或写入套接字时,会抛出一个socket.error。
简短的回答:
使用一个非阻塞recv()或阻塞recv()/ select()与一个非常短的超时。
很长的回答:
处理套接字连接的方法是根据需要进行读取或写入,并准备处理连接错误。
TCP区分“丢弃”连接的三种forms:超时,重置,closures。
其中,超时不能真正被检测到,TCP可能只告诉你时间还没有到期。 但即使它告诉你,时间可能还会过期。
还要记住,使用shutdown()或者你或者你的peer(连接的另一端)可能只closures传入的字节stream,并且保持传出的字节stream正在运行,或者closures外出的stream并保持传入的stream正在运行。
所以严格来说,你想要检查读取stream是否closures,或者写入stream是closures还是closures。
即使连接被“丢弃”,您仍然应该能够读取仍在networking缓冲区中的任何数据。 只有在缓冲区为空后,您才会收到与recv()的断开连接。
检查连接是否被丢弃就像问“在读完当前缓冲的所有数据之后我会收到什么?” 要发现这一点,你只需要读取当前缓冲的所有数据。
我可以看到如何“读取所有缓冲的数据”,以达到其结束,可能是一些人的问题,仍然认为recv()作为一个阻塞函数。 通过阻塞recv(),当缓冲区已经为空时,“检查”读操作将被阻塞,这就违背了“检查”的目的。
在我看来,任何被logging的可能会无限期地阻止整个过程的函数是一个devise缺陷,但是我认为它仍然是出于历史原因,从像使用常规文件描述符那样使用套接字是一个很酷的想法。
你可以做的是:
- 请将套接字设置为非阻塞模式,但是您会收到系统依赖的错误,指示接收缓冲区为空,或者发送缓冲区已满
- 坚持阻塞模式,但设置一个非常短的套接字超时。 这将允许你使用recv()来“ping”或“检查”套接字,几乎你想要做的事情
- 使用select()调用或asyncore模块的超时时间很短。 错误报告仍然是系统特定的。
对于写入问题的一部分,保持读取缓冲区为空状态几乎覆盖了它。 在非阻塞式读取尝试之后,您将发现连接“断开”,您可以select在读取返回closures的通道后停止发送任何内容。
我想唯一的方法来确保您的发送数据已经到达另一端(而不是仍然在发送缓冲区)是:
- 在相同的套接字上接收到您发送的确切消息的正确响应。 基本上你使用更高级别的协议来提供确认。
- 在套接字上执行成功的closures()和closures()
python套接字howto说send()将返回0字节如果通道被closures写入。 你可以使用一个非阻塞的或超时的socket.send(),如果它返回0,你不能再在该套接字上发送数据。 但如果它返回非零,你已经发送了一些东西,祝你好运:)
在这里,我也没有考虑OOB(带外)套接字数据作为解决您的问题的方法,但我认为OOB不是您的意思。
从链接Jweede发布:
exceptionsocket.timeout:
This exception is raised when a timeout occurs on a socket which has had timeouts enabled via a prior call to settimeout(). The accompanying value is a string whose value is currently always “timed out”.
这里是python文档中的套接字模块的演示服务器和客户端程序
# Echo server program import socket HOST = '' # Symbolic name meaning all available interfaces PORT = 50007 # Arbitrary non-privileged port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((HOST, PORT)) s.listen(1) conn, addr = s.accept() print 'Connected by', addr while 1: data = conn.recv(1024) if not data: break conn.send(data) conn.close()
和客户:
# Echo client program import socket HOST = 'daring.cwi.nl' # The remote host PORT = 50007 # The same port as used by the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) s.send('Hello, world') data = s.recv(1024) s.close() print 'Received', repr(data)
在文档示例页面中,我将这些内容从其中抽取出来,但有更复杂的示例使用这个想法,但这里是简单的答案:
假设你正在编写客户端程序,只要把你的所有代码放在一个try块中就可能被丢弃。
try: s.connect((HOST, PORT)) s.send("Hello, World!") ... except socket.timeout: # whatever you need to do when the connection is dropped
如果我没有弄错,这通常是通过超时来处理的。
我把这个博客文章中的代码示例翻译成Python: 如何检测客户端何时closures连接? ,这对我来说很好:
from ctypes import ( CDLL, c_int, POINTER, Structure, c_void_p, c_size_t, c_short, c_ssize_t, c_char, ARRAY ) __all__ = 'is_remote_alive', class pollfd(Structure): _fields_ = ( ('fd', c_int), ('events', c_short), ('revents', c_short), ) MSG_DONTWAIT = 0x40 MSG_PEEK = 0x02 EPOLLIN = 0x001 EPOLLPRI = 0x002 EPOLLRDNORM = 0x040 libc = CDLL(None) recv = libc.recv recv.restype = c_ssize_t recv.argtypes = c_int, c_void_p, c_size_t, c_int poll = libc.poll poll.restype = c_int poll.argtypes = POINTER(pollfd), c_int, c_int class IsRemoteAlive: # not needed, only for debugging def __init__(self, alive, msg): self.alive = alive self.msg = msg def __str__(self): return self.msg def __repr__(self): return 'IsRemoteClosed(%r,%r)' % (self.alive, self.msg) def __bool__(self): return self.alive def is_remote_alive(fd): fileno = getattr(fd, 'fileno', None) if fileno is not None: if hasattr(fileno, '__call__'): fd = fileno() else: fd = fileno p = pollfd(fd=fd, events=EPOLLIN|EPOLLPRI|EPOLLRDNORM, revents=0) result = poll(p, 1, 0) if not result: return IsRemoteAlive(True, 'empty') buf = ARRAY(c_char, 1)() result = recv(fd, buf, len(buf), MSG_DONTWAIT|MSG_PEEK) if result > 0: return IsRemoteAlive(True, 'readable') elif result == 0: return IsRemoteAlive(False, 'closed') else: return IsRemoteAlive(False, 'errored')