[英]TCP non-blocking socket.connect() and socket.recv() Error questions. (Python or C)
[英]What does Python's socket.recv() return for non-blocking sockets if no data is received until a timeout occurs?
基本上,我已经在几个地方socket.recv()
将返回它可以读取的内容,或者一个空字符串表示另一方已关闭(官方文档甚至没有提到它在连接时返回的内容关闭......很棒!)。 这对于阻塞套接字来说都很好用,因为我们知道recv()
仅在实际接收到某些内容时返回,所以当它返回一个空字符串时,它必须意味着另一方已经关闭了连接,对吧?
好的,很好,但是当我的套接字没有阻塞时会发生什么? 我搜索了一下(也许还不够,谁知道?)并且无法弄清楚如何判断对方是否使用非阻塞套接字关闭了连接。 似乎没有方法或属性告诉我们这一点,并且将recv()
的返回值与空字符串进行比较似乎绝对没用......是不是我有这个问题?
举一个简单的例子,假设我的套接字超时设置为1.2342342(你喜欢这里的非负数)秒,我调用socket.recv(1024)
,但另一方在1.2342342秒期间没有发送任何内容。 recv()
调用将返回一个空字符串,我不知道连接是否仍然存在...
对于没有可用数据的非阻塞套接字,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()
返回0个字节; 您将不会再收到有关此连接的数据。 永远。 你仍然可以发送。
这意味着如果没有可用的数据但是连接仍然存在(另一端可能发送),则非阻塞套接字必须引发异常(可能与系统有关)。
当您使用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'
只是为了完成现有的答案,我建议使用select而不是非阻塞套接字 。 关键是非阻塞套接字使事情变得复杂(除了可能发送),所以我说根本没有理由使用它们。 如果您经常遇到应用程序被阻塞等待IO的问题,我还会考虑在后台单独的一个线程中执行IO。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.