El *_*dor 50 python sockets nonblocking recv
基本上,我已经阅读了几个socket.recv()会返回它可以读取的内容,或者一个空字符串表示另一方已关闭(官方文档甚至没有提到它在连接关闭时返回的内容...大!).这对于阻塞套接字来说都很好用,因为我们知道recv()只有当实际有东西要接收时才返回,所以当它返回一个空字符串时,它必须意味着另一方已经关闭了连接,对吧?
好的,很好,但是当我的套接字没有阻塞时会发生什么?我搜索了一下(也许还不够,谁知道?)并且无法弄清楚如何判断对方是否使用非阻塞套接字关闭了连接.似乎没有方法或属性告诉我们这一点,并且将recv()空字符串的返回值进行比较似乎绝对无用......这只是我有这个问题吗?
举一个简单的例子,假设我的套接字超时设置为1.2342342(你喜欢这里的非负数)秒和我打电话socket.recv(1024),但另一方在1.2342342秒期间没有发送任何内容.该recv()调用将返回一个空字符串,我不知道连接是否仍然存在...
msh*_*ldt 68
对于没有可用数据的非阻塞套接字,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 :)
Run Code Online (Sandbox Code Playgroud)
如果您通过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 :)
Run Code Online (Sandbox Code Playgroud)
如评论中所示,这也是一种更便携的解决方案,因为它不依赖于OS特定的功能来将套接字置于非阻塞模式.
有关更多详细信息,请参阅recv(2)和python套接字.
很简单:if recv()返回0字节; 您将不会再收到有关此连接的数据.永远.你仍然可以发送.
这意味着如果没有可用的数据但是连接仍然存在(另一端可能发送),则非阻塞套接字必须引发异常(可能与系统有关).
当您使用recv连接时,select如果已准备好读取套接字但没有要读取的数据,则意味着客户端已关闭连接.
下面是一些处理此问题的代码,还要注意recv在while循环中第二次调用时抛出的异常.如果没有任何东西可以读取此异常将被抛出它并不意味着客户端已关闭连接:
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)
Run Code Online (Sandbox Code Playgroud)
并且接收数据的功能:
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'
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
122971 次 |
| 最近记录: |