我有一个使用select()的简单服务器,如下所示:
#!/usr/bin/env python2
import select, socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)
server.bind(('localhost', 50000))
server.listen(5)
# TCP Keepalive Options
#server.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
#server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
#server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3)
#server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)
inputs = [server]
print "Listening on port 50000"
while True:
readable, writable, exceptional = select.select(inputs, [], inputs)
for s in readable:
if s is server:
connection, client_address = s.accept()
print "New client connected: %s" % (client_address,)
connection.setblocking(0)
inputs.append(connection)
else:
data = s.recv(1024)
if data:
print "Data from %s: %s" % (s.getpeername(), data.replace('\n', ''))
else:
print "%s disconnected" % (s.getpeername(),)
inputs.remove(s)
s.close()
for s in exceptional:
print "Client at %s dropped out" % (s.getpeername(),)
inputs.remove(s)
s.close()
我可以使用telnet客户端连接到它,并且效果很好。它不响应客户,但是对于这个简单的示例,就可以了。
我看到的问题是这样的:如果客户端在没有发送TCP FIN或TCP RST的情况下断开连接,则服务器似乎永远无法确定客户端已消失。
我这样做是模拟客户端消失:
据我所知,通常的解决方案是打开TCP Keepalive,我通过取消注释TCP Keepalive部分来做到这一点。 当我这样做时,并遵循相同的测试过程,使客户端在连接的会话中间消失,似乎当套接字 超时,select()停止阻止,并在“可读”列表(与例外列表相反)中返回客户端。这个 使我的服务器尝试使用s.recv(1024)从该套接字读取数据,这使服务器崩溃(s.recv()抛出socket.error异常)。
我知道我可能可以捕获该异常并进行处理,但是我很好奇为什么:
这是预期的吗?有没有办法让select()将消失的客户端放到例外列表中呢?或者重要的是,不要仅仅因为select()表示套接字已准备好读取而已,recv()不会失败?
编辑:该问题与我先前here提出的问题并不重复,因为该问题专门处理select()及其如何处理异常。这实际上包括我从另一个问题中学到的代码。
答案 0 :(得分:1)
当您使用keepalive并检测到故障时,select()
应报告套接字为可读写状态。然后,当您尝试执行这些操作之一时,您将得到一个错误。在try/except
调用周围使用s.recv()
来检测socket.error
。
您可能天真地认为这将被报告为“例外情况”,但事实并非如此。这用于有效套接字上的正常异常,例如带外数据。