【问题标题】:How to know the if the socket connection is closed in python?如何知道套接字连接是否在python中关闭?
【发布时间】:2019-11-14 21:50:47
【问题描述】:

我仍然对知道我的套接字连接的状态感到困惑。这就是我正在做的事情。

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",9979))

while True:
    msg = client.recv(4096)
    processNotificationMessage(msg)
    time.sleep(0.1)

我想连接到服务器并一直接收数据。但是如何知道我的客户端连接是否由于某种原因而关闭?

通过谷歌搜索,我发现 client.recv() 将返回一个空字符串或 client.recv() 将引发异常(但有当我停止服务器时也不例外)。但是如果服务器本身有时会发送一个空字符串呢?如果我尝试通过检查空字符串来重新连接,则会引发异常,说明已连接。这里的解释(How to tell if a connection is dead in python)对我不起作用。

当我重新启动服务器时,客户端没有收到任何数据。

【问题讨论】:

    标签: python sockets


    【解决方案1】:

    fileno() 将为关闭的套接字返回 -1。

    cli, addr = self.sock.accept()
    print(cli.fileno())
    cli.close()
    print(cli.fileno())
    

    输出

    376
    -1
    

    来自文档18.1 - Socket

    socket.fileno() 返回套接字的文件描述符(一个小整数), 或 -1 失败。这对 select.select() 很有用。

    套接字在技术上也是一个列表,通过它你可以找到[关闭]标签

    cli, addr = self.sock.accept()
    for i in str(cli).split():
        print(i)
    
    <socket.socket
    fd=488,
    family=AddressFamily.AF_INET,
    type=SocketKind.SOCK_STREAM,
    proto=0,
    laddr=('10.1.1.2',
    80),
    raddr=('10.1.1.2',
    16709)>
    488
    
    -1
    
    cli.close()
    for i in str(cli).split():
        print(i)
    
    <socket.socket
    fd=504,
    family=AddressFamily.AF_INET,
    type=SocketKind.SOCK_STREAM,
    proto=0,
    laddr=('10.1.1.2',
    80),
    raddr=('10.1.1.2',
    16833)>
    504
    <socket.socket
    [closed]   <---
    fd=-1,
    family=AddressFamily.AF_INET,
    type=SocketKind.SOCK_STREAM,
    proto=0>
    -1
    

    【讨论】:

      【解决方案2】:

      client.recv 可以返回空字符串的唯一方法是在文件末尾(即服务器端消失了)。

      不可能通过SOCK_STREAM 连接发送零长度消息。如果服务器想要发送一个空字符串,它就需要发送一些其他的指示符(例如,一个 C 风格的空字符串实际上是一个仅包含一个零字节的单字节字符串——空终止符)。

      如果你的服务器真的停止了,你应该得到一个空字符串。你确定你没有?上面显示的循环没有检查该条件。如果服务器使用显示的代码停止,您将简单地坐在循环中,接收一个零字节字符串,使用(空)字符串调用processNotificationMessage,休眠十分之一秒,然后返回接收另一个零字节串,无穷无尽。您需要检查循环中的条件和break (if not msg: break)。

      【讨论】:

      • 正如我在解释中提到的,我需要永远接收。因此,如果服务器重新启动,我需要重新连接它并继续接收数据。我该怎么做??
      • 两个循环,一个嵌套在另一个循环中。
      【解决方案3】:

      怎么样: s._closed

      这将给出真/假答案。

      【讨论】:

      • 套接字对象没有名为'_closed'的属性
      • s._closed 完美运行,也许你有旧版本的 python Vishal?我在 python 版本 3.7.7 和 s._closed 实际上是一个属性。老实说 s._closed 应该是公认的答案。
      • 不错。但就我而言,它总是会返回false。为什么会发生这种情况?
      【解决方案4】:

      首先,值得一提的是,在大多数情况下,最好在主处理程序循环中处理此问题,您可以在其中简单地测试 EOF(socket.recv() 收到的空字符串。但是,我遇到了测试的需要this 和 following 似乎比将另一个回调传递给线程以获取此状态等更好)

      以下是我正在使用的,但我对此很陌生,所以如果有更好或更有效的方法,请提供反馈,我会更新这个答案。

      似乎最好的方法是检查getpeername()(在这里测试socket.recv()很糟糕,因为我们的测试会改变缓冲区,例如。)

      返回远程端点的地址。对于 IP 套接字,地址信息是一对 (hostaddr, port)。

      如果失败,则会引发 socket.error [Errno 9] Bad file descriptor.

      def is_socket_valid(socket_instance):
          """ Return True if this socket is connected. """
          if not socket_instance:
              return False
      
          try:
              socket_instance.getsockname()
          except socket.error as err:
              err_type = err.args[0]
              if err_type == errno.EBADF:  # 9: Bad file descriptor
                  return False
      
          try:
              socket_instance.getpeername()
          except socket.error as err:
              err_type = err.args[0]
          if err_type in [errno.EBADF, errno.ENOTCONN]:  #   9: Bad file descriptor.
              return False                               # 107: Transport endpoint is not connected
      
          return True
      

      注意:我在那里进行了第一个 if-check,因为我正在测试一个也可能为 None 的属性。

      1. 这并不像我想要的那样明确,但在我的测试中它似乎是准确的。
      2. 原始问题提到想知道未连接的原因。这并没有解决这个问题。

      此外,另一个答案提到如果套接字的本地端关闭,fileno() 返回一个负数。我得到相同的错误 9。无论哪种方式,这都不会测试另一方是否有效。

      >>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      >>> s.fileno()
      10
      >>> s.close()
      >>> s.fileno()
      ---------------------------------------------------------------------------
      error                                     Traceback (most recent call last)
      ----> 1 s.fileno()
      
      /tools/package/python/2.7.13/lib/python2.7/socket.pyc in meth(name, self, *args)
          226
          227 def meth(name,self,*args):
      --> 228     return getattr(self._sock,name)(*args)
          229
          230 for _m in _socketmethods:
      
      /tools/package/python/2.7.13/lib/python2.7/socket.pyc in _dummy(*args)
          172     __slots__ = []
          173     def _dummy(*args):
      --> 174         raise error(EBADF, 'Bad file descriptor')
          175     # All _delegate_methods must also be initialized here.
          176     send = recv = recv_into = sendto = recvfrom = recvfrom_into = _dummy
      
      error: [Errno 9] Bad file descriptor
      

      【讨论】:

      • 我想我应该报告一下,我编写了自己的代码是为了不需要这个测试。需要这个可能是“代码气味”,但我仍然在这里学习。
      【解决方案5】:

      s_closed result pic s_closed 如wie lin所说,返回true/false,它在那里(我已经使用并测试过它,可以在图片中看到)。或者你可以使用 s.___getstate__

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-15
        • 1970-01-01
        • 1970-01-01
        • 2011-04-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多