【问题标题】:Python 3 - Sockets with select.select() - detecting loss of connectionPython 3 - 带有 select.select() 的套接字 - 检测连接丢失
【发布时间】:2017-10-29 00:04:30
【问题描述】:

我有一个 Python 3 服务器脚本,它运行 TCP 套接字服务器,使用 select.select() 检测和响应传入数据

我使用 select.select() 来处理没有线程的多个连接,并且服务器主要是反应式的(只等待数据并对其做出响应)。它为另一端设备的每个连接和参数保留一个字典;每个设备的条目都会在其连接关闭时被删除。

我的问题是我的客户端有时会在没有实际关闭 TCP 套接字的情况下失去连接​​,我不知道如何捕获或创建超时以关闭套接字并从字典中删除旧连接。

有什么好办法吗?

这是脚本的简化副本:

host = '192.168.0.252'
port = 9989
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((host,port))
server.listen(16)
socks = [server]
devices = {}

while True:
  readable,writable,exceptionavailable = select.select(socks,[],[])
  for s in readable:
    if(s == server):
      client, address = server.accept()
      socks.append(client)
    else:
      try: data = s.recv(1024)
      except ConnectionResetError: data = 0

      if data:
        print(data) # Would append device to "devices" dictionary
      else:
        s.close()
        socks.remove(s)
        del(devices[did]) # did is the ID that needs deleting from dictionary

任何帮助将不胜感激。

【问题讨论】:

  • 我建议查看SO_KEEPALIVE 选项和相关选项。在套接字上使用这些选项将允许使用保持活动探针来检查连接是否在没有警告的情况下关闭。

标签: python sockets python-3.x tcp


【解决方案1】:

编辑:根据@Daniel 的评论更新了更好的代码。

假设您想关闭一个连接,如果您在 X 秒内没有读取它。然后你必须:

  1. 对于每个套接字,记录您上次读取它的时间。
  2. 每次选择返回时更新上次读取时间并关闭已超时的连接。

在此代码中,连接超时设置为 300 秒。

lastread = {} # a dictionary with sockets as keys
...

readable,_,_ = select.select(socks,[],[], 60)
now = time()
for s in readable:
  ... read from the socket s and process input ...
  lastread[s] = now
closed = []
for s in lastread:
  if s not in readable and now - lastread[s] > 300:
    ... close connection ...
    closed.append(s)
for s in closed: del lastread[s]

注意事项:

  1. 传递给select 的超时(在本例中为60)与连接超时没有太大关系。它只是说您希望最多在 60 秒后将控制权交还给您。
  2. 请务必在创建套接字s时初始化lastread[s],并在关闭连接时删除密钥。

更多资源:

  • 使用select 和超时(link) 的教程
  • 一篇讨论 TCP 连接断开问题和其他一些解决方案的文章:(link)

【讨论】:

  • 效果很好!谢谢。我添加了一个小的修改以使用now = int(time.perf_counter()),以便使时间独立于系统时钟的更改(NTP 更新或手动时间更新等),并在最终切断连接之前尝试发送“ping”几次以防万一提示另一端发送一些东西(我会添加给可能需要它的客户)。
  • 这是如何工作的?您不能从当前正在迭代的列表中删除一个键?
  • 你是对的。它之所以有效,是因为该代码的最后一部分实际上是在一个循环中,所以最终lastread 的所有键都被处理了。我会用更好的解决方案更新代码。
猜你喜欢
  • 2011-05-19
  • 2012-08-18
  • 1970-01-01
  • 2018-09-26
  • 1970-01-01
  • 1970-01-01
  • 2016-09-04
  • 2011-06-13
  • 1970-01-01
相关资源
最近更新 更多