基本上,我已经在几个地方读过socket.recv()
将返回它可以读取的内容,或者一个空字符串表示另一方已关闭(官方文档甚至没有提到它返回时返回的内容)连接关闭......很棒!)。这对于阻塞套接字来说都很好用,因为我们知道recv()
只在实际有东西要接收时返回,所以当它返回一个空字符串时,必须意味着另一面关闭了连接,对吧?
好的,但是当我的套接字没有阻塞时会发生什么?我搜索了一下(也许还不够,谁知道?)并且无法弄清楚如何判断对方是否使用非阻塞套接字关闭了连接。似乎没有方法或属性告诉我们这一点,并且将recv()
的返回值与空字符串进行比较似乎绝对没用......是不是我遇到了这个问题?
举一个简单的例子,假设我的套接字超时设置为1.2342342(你喜欢这里的非负数)秒,我呼叫socket.recv(1024)
,但另一方在1.2342342秒期间没有发送任何内容期。 recv()
调用将返回一个空字符串,我不知道连接是否仍然存在...
答案 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。