如果在超时之前没有收到数据,那么Python的socket.recv()对于非阻塞套接字返回什么?

2024-03-28 15:57:44 发布

您现在位置:Python中文网/ 问答频道 /正文

基本上,我在几个地方读到过socket.recv()将返回它所能读取的任何内容,或者一个空字符串表示另一方已经关闭(官方文档甚至没有提到它在连接关闭时返回的内容。。。太好了!对于阻塞套接字来说,这一切都很好,因为我们知道recv()只在实际有东西要接收时返回,所以当它返回空字符串时,它必须表示另一方已经关闭了连接,对吧?

好吧,好吧,但是如果我的插座没有阻塞怎么办??我搜了一下(也许还不够,谁知道?)无法知道对方何时使用非阻塞套接字关闭了连接。似乎没有任何方法或属性告诉我们这一点,将recv()的返回值与空字符串进行比较似乎完全没有用处。。。只是我有这个问题吗?

作为一个简单的例子,假设我的套接字的超时设置为1.2342342秒(无论您喜欢哪个非负数),我调用socket.recv(1024),但另一方在1.2342342秒期间不发送任何内容。调用recv()将返回一个空字符串,我不知道连接是否仍处于正常状态。。。


Tags: 方法字符串文档内容属性官方状态地方
3条回答

当您使用recvselect连接时,如果套接字已准备好读取,但没有要读取的数据,则表示客户端已关闭连接。

下面是一些处理此问题的代码,还要注意在while循环中第二次调用recv时引发的异常。如果没有任何内容可供读取,则将引发此异常,但这并不意味着客户端已关闭连接:

def listenToSockets(self):

    while True:

        changed_sockets = self.currentSockets

        ready_to_read, ready_to_write, in_error = select.select(changed_sockets, [], [], 0.1)

        for s in ready_to_read:

            if s == self.serverSocket:
                self.acceptNewConnection(s)
            else:
                self.readDataFromSocket(s)

以及接收数据的函数:

def readDataFromSocket(self, socket):

    data = ''
    buffer = ''
    try:

        while True:
            data = socket.recv(4096)

            if not data: 
                break

            buffer += data

    except error, (errorCode,message): 
        # error 10035 is no data available, it is non-fatal
        if errorCode != 10035:
            print 'socket.error - ('+str(errorCode)+') ' + message


    if data:
        print 'received '+ buffer
    else:
        print 'disconnected'

很简单:如果recv()返回0个字节;you will not receive any more data on this connection. Ever.您仍然可以发送。

这意味着,如果没有可用的数据,但连接仍处于活动状态(另一端可能发送),则非阻塞套接字必须引发异常(它可能依赖于系统)。

对于没有可用数据的非阻塞套接字,recv将抛出socket.error异常,该异常的值将具有EAGAIN或ewooldblock的errno。示例:

import sys
import socket
import fcntl, os
import errno
from time import sleep

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',9999))
fcntl.fcntl(s, fcntl.F_SETFL, os.O_NONBLOCK)

while True:
    try:
        msg = s.recv(4096)
    except socket.error, e:
        err = e.args[0]
        if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
            sleep(1)
            print 'No data available'
            continue
        else:
            # a "real" error occurred
            print e
            sys.exit(1)
    else:
        # got a message, do something :)

当您通过使用^{}^{}超时来启用非阻塞行为时,情况略有不同。在这种情况下,会引发socket.error,但在超时的情况下,伴随的异常值始终是设置为“超时”的字符串。所以,要处理这个案子,你可以:

import sys
import socket
from time import sleep

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',9999))
s.settimeout(2)

while True:
    try:
        msg = s.recv(4096)
    except socket.timeout, e:
        err = e.args[0]
        # this next if/else is a bit redundant, but illustrates how the
        # timeout exception is setup
        if err == 'timed out':
            sleep(1)
            print 'recv timed out, retry later'
            continue
        else:
            print e
            sys.exit(1)
    except socket.error, e:
        # Something else happened, handle error, exit, etc.
        print e
        sys.exit(1)
    else:
        if len(msg) == 0:
            print 'orderly shutdown on server end'
            sys.exit(0)
        else:
            # got a message do something :)

如注释所示,这也是一个更可移植的解决方案,因为它不依赖于操作系统特定的功能来将套接字置于非阻塞模式。

有关详细信息,请参见recv(2)python socket

相关问题 更多 >