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

时间:2013-05-25 00:53:49

标签: python sockets nonblocking recv

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

好的,但是当我的套接字没有阻塞时会发生什么?我搜索了一下(也许还不够,谁知道?)并且无法弄清楚如何判断对方是否使用非阻塞套接字关闭了连接。似乎没有方法或属性告诉我们这一点,并且将recv()的返回值与空字符串进行比较似乎绝对没用......是不是我遇到了这个问题?

举一个简单的例子,假设我的套接字超时设置为1.2342342(你喜欢这里的非负数)秒,我呼叫socket.recv(1024),但另一方在1.2342342秒期间没有发送任何内容期。 recv()调用将返回一个空字符串,我不知道连接是否仍然存在...

4 个答案:

答案 0 :(得分:61)

如果非阻塞套接字没有可用数据,则recv将抛出socket.error异常,并且异常的值将具有EAGAIN或EWOULDBLOCK的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.settimeout(n)socket.setblocking(False)超时启用了非阻止行为,则情况会略有不同。在这种情况下,socket.error是stil引发的,但是在超时的情况下,异常的伴随值总是设置为'timed out'的字符串。所以,要处理这种情况,你可以这样做:

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 :)

正如评论中所指出的,这也是一种更便携的解决方案,因为它不依赖于OS特定的功能来将套接字置于非阻塞模式。

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

答案 1 :(得分:6)

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

这意味着如果没有可用的数据但是连接仍然存在(另一端可能发送),则非阻塞套接字必须引发异常(可能与系统有关)。

答案 2 :(得分:5)

recv连接select时,如果已准备好读取套接字,但没有要读取的数据,则意味着客户端已关闭连接。

以下是一些处理此问题的代码,还要注意在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'

答案 3 :(得分:2)

为了完成现有答案,我建议using select instead of nonblocking sockets。关键是非阻塞套接字使事情变得复杂(除了可能发送),所以我说根本没有理由使用它们。如果您经常遇到应用程序被阻止等待IO的问题,我还会考虑在后台单独的一个线程中执行IO。