如何在Python中判断套接字连接是否关闭?

15
我仍然不清楚如何了解我的套接字连接状态。这是我的操作。

我仍然不清楚如何了解我的套接字连接状态。这是我的操作。

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() 将抛出一个异常(但是当我停止我的服务器时没有异常)。但是,如果服务器本身有时会发送一个空字符串,那该怎么办?如果我尝试通过检查空字符串来重新连接它,它将抛出一个已连接的异常。这里的解释 (如何判断 Python 中的连接是否已死亡) 对我没有用。

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

5个回答

21

fileno()用于获取文件描述符,对于已关闭的套接字,返回值为-1。

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

输出

376
-1

从文档18.1 - 套接字中得知:

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

此外,套接字在技术上是一个列表,您可以在其中找到[closed]标记。

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

7
只有在文件结束时(即服务器端已关闭)client.recv 才会返回空字符串。
通过 SOCK_STREAM 连接不可能发送零长度的消息。如果服务器希望发送一个空字符串,则需要发送一些其他指示符(例如,C风格的空字符串实际上是一个只包含单个零字节的一字节字符串——空终止符)。
如果您的服务器真的停止了,那么您应该得到一个空字符串。您确定没有收到吗?您上面展示的循环没有检查这种情况。如果使用上述代码停止服务器,您将仅停留在循环中,接收一个零字节字符串,使用(空)字符串调用processNotificationMessage,睡眠一秒钟,然后返回接收另一个零字节字符串,无限期重复此过程。您需要检查这种情况并从循环中breakif not msg: break)。

正如我在解释中提到的那样,我需要一直保持接收状态。因此,如果服务器重新启动,我需要重新连接并再次开始接收数据。我该怎么做? - Vishal Yarabandi
两个循环,一个嵌套在另一个里面。 - Gil Hamilton

5
如何呢: s._closed 这会给出一个True/False答案。

1
socket对象没有名为'_closed'的属性。 - Vishal Yarabandi
s._closed运行得很好,也许你使用了旧版本的Python Vishal?我正在使用Python版本3.7.7,实际上s._closed是一个属性。说实话,s._closed应该被接受为正确答案。 - Adam
2
很好。但在我的情况下,它总是返回“false”。为什么会这样? - Rohit Nishad

1
首先,值得一提的是,在大多数情况下,最好在主处理程序循环中处理此问题,您可以简单地测试EOF(由socket.recv()接收到的空字符串)。然而,我遇到了需要测试这个的情况,以下方法似乎比传递另一个回调函数到线程中获取此状态更好,等等。
以下是我正在使用的内容,但我非常陌生,因此如果有更好或更有效的方法,请提供反馈,我将更新此答案。
似乎最好的方法是检查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检查,因为我正在测试可能为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

我想我应该回报一下,我通过编码摆脱了对这个测试的需求。也许需要这个测试是一种“代码异味”,但我还在学习中。 - Rafe

0

s_closed result pic s_closed 返回 true/false,就像 Wie Lin 所说的那样,它在那里(我已经使用和测试过了,可以在图片中看到)。或者你可以使用 s.___getstate__


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接