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

基本上,我已经读了几个地方, socket.recv()将返回任何它可以读取,或者一个空string,表明另一方已经closures(官方文档甚至没有提到什么时候连接是什么closures…太棒了!)。 因为我们知道recv()只有在实际有东西需要接收的时候才会返回,所以当它返回一个空string时,它必须意味着另一端已经closures了连接,对吗?

好的,好吧,但是当我的套接字是非阻塞时呢? 我已经search了一下(也许还不够,谁知道?),并不知道如何判断对方何时使用非阻塞套接字来closures连接。 似乎没有方法或属性告诉我们这一点,比较recv()的返回值与空string似乎是绝对无用的…是只有我有这个问题?

作为一个简单的例子,假设我的套接字的超时设置为1.2342342(无论你在这里是否定的非负数)秒,我打电话给socket.recv(1024) ,但另一方在1.2342342秒期间不发送任何东西。 recv()调用将返回一个空string,我不知道连接是否仍然存在或没有线索…

在没有可用数据的非阻塞套接字的情况下,recv将抛出socket.errorexception,并且exception的值将具有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引发的,但是在超时的情况下,exception的伴随值总是一个设置为“超时”的string。 所以,为了处理这种情况,你可以这样做:

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

如注释中所示,这也是一种更便携的解决scheme,因为它不依赖于操作系统特定的function来将套接字置于非blockng模式。

有关更多详细信息,请参阅recv(2)和python套接字 。

当你使用recv连接select如果套接字已经准备好被读取,但是没有数据要读取,这意味着客户端已经closures了连接。

下面是处理这个的一些代码,还要注意在while循环中第二次调用recv时引发的exception。 如果没有什么可读的,这个exception将被抛出,这并不意味着客户端已经closures了连接:

 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) 

和接收数据的function:

 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字节; 您将不会收到有关此连接的更多数据。 永远。 你仍然可以发送。

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

只是为了完成现有的答案,我build议使用select而不是非阻塞套接字 。 关键是非阻塞套接字使得东西变得复杂(除了可能发送),所以我会说没有理由使用它们。 如果您经常遇到阻止您的应用程序等待IO的问题,我还会考虑在后台单独的线程中执行IO。