我希望我的python应用程序能够告诉另一端的套接字何时被删除.有这个方法吗?
简短回答:
使用非阻塞的recv(),或阻塞的recv()/ select(),超时很短.
答案很长:
处理套接字连接的方法是根据需要进行读取或写入,并准备好处理连接错误.
TCP区分3种"丢弃"连接形式:超时,重置,关闭.
其中,无法真正检测到超时,TCP可能只告诉您时间尚未到期.但即使它告诉你,时间可能仍然会过期.
还要记住,使用shutdown()您或您的对等方(连接的另一端)可能只关闭传入的字节流,并保持传出的字节流运行,或关闭传出流并保持传入的流运行.
严格来说,您要检查读取流是否已关闭,或者写入流是否已关闭,或两者是否都已关闭.
即使连接被"丢弃",您仍应该能够读取仍在网络缓冲区中的任何数据.只有在缓冲区为空后,才会收到与recv()的断开连接.
检查连接是否被丢弃就像问"在读取当前缓冲的所有数据后我将收到什么?" 要找到它,您只需要读取当前缓冲的所有数据.
我可以看到"读取所有缓冲的数据",到达它的末尾,对某些人来说可能是一个问题,仍然认为recv()是一个阻塞函数.使用阻塞recv(),当缓冲区已经为空时"检查"读取将阻塞,这违背了"检查"的目的.
在我看来,任何被记录为可能无限期地阻止整个过程的函数都是一个设计缺陷,但我想它仍然存在于历史原因,从使用套接字时就像常规文件描述符一样很酷.
你能做的是:
将套接字设置为非阻塞模式,但是您得到系统依赖的错误以指示接收缓冲区为空,或者发送缓冲区已满
坚持阻止模式,但设置一个非常短的套接字超时.这将允许您使用recv()"ping"或"检查"套接字,几乎就是您想要做的事情
使用select()调用或asyncore模块,超时很短.错误报告仍然是系统特定的.
对于问题的写入部分,保持读取缓冲区空白几乎涵盖了它.在非阻塞读取尝试后,您将发现"丢弃"的连接,并且您可以选择在读取返回已关闭的通道后停止发送任何内容.
我想唯一的方法是确保你发送的数据到达另一端(并且还没有在发送缓冲区中)是:
在同一个套接字上收到您发送的确切消息的正确响应.基本上您使用更高级别的协议来提供确认.
在套接字上执行成功的shutdow()和close()
python套接字如何说如果通道关闭,send()将返回写入的0字节.您可以使用非阻塞或超时socket.send(),如果它返回0,您将无法再在该套接字上发送数据.但如果它返回非零,你已经发送了一些东西,祝你好运:)
另外在这里我没有考虑过OOB(带外)套接字数据作为解决问题的方法,但我认为OOB不是你的意思.
这取决于你所说的"掉线".对于TCP套接字,如果另一端通过close()或进程终止关闭连接,您将通过读取文件结尾或获取读取错误来查找,通常将errno设置为任何'连接重置为peer'是您的操作系统.对于python,您将读取零长度字符串,或者当您尝试从套接字读取或写入时将抛出socket.error.
从链接Jweede发布:
异常socket.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 docs的套接字模块的演示服务器和客户端程序
# 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:如何检测客户端何时关闭连接?,对我来说效果很好:
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')