22-使用非阻塞IO-1
代码
def relay(sock):
socketEvents = select.POLLIN
poll = select.poll()
poll.register(sock, socketEvents)
poll.register(sys.stdin, select.POLLIN)
setNonBlocking(sock)
# setNonBlocking(sys.stdin)
# setNonBlocking(sys.stdout)
done = False
socketOutputBuffer = '' # 字符串,用以当作socket的输出缓冲区
while not done:
events = poll.poll(10000) # 10 seconds
for fileno, event in events:
if event & select.POLLIN: # 关注读事件
if fileno == sock.fileno(): # socket可读
data = sock.recv(8192)
if data: # 这里的sdtout不是非阻塞,只是为了代码简短一些
nw = sys.stdout.write(data) # stdout does support non-blocking write, though
else:
done = True
else:
assert fileno == sys.stdin.fileno() # stdin可读
data = os.read(fileno, 8192)
if data:
assert len(socketOutputBuffer) == 0 # 先判断socketOutputBuffer为空,否则出现乱序
nw = nonBlockingWrite(sock.fileno(), data) # 非阻塞写socket
if nw < len(data): # 未写完
if nw < 0:
nw = 0
socketOutputBuffer = data[nw:] # 将未发送完的数据,存起来,后面再发送
socketEvents |= select.POLLOUT # 继续关注socket的写事件
poll.register(sock, socketEvents)
poll.unregister(sys.stdin) #
else: # 读完
sock.shutdown(socket.SHUT_WR) # 关闭socket的写部分
poll.unregister(sys.stdin)
if event & select.POLLOUT: # 关注写事件
if fileno == sock.fileno():
assert len(socketOutputBuffer) > 0 # 缓存不空,有东西可以写
nw = nonBlockingWrite(sock.fileno(), socketOutputBuffer)
if nw < len(socketOutputBuffer): # 没有写完
assert nw > 0
socketOutputBuffer = socketOutputBuffer[nw:] # 记录未写完的数据
else:
socketOutputBuffer = ''
socketEvents &= ~select.POLLOUT # 停止关注可写事件,否则会Busy loop
poll.register(sock, socketEvents)
poll.register(sys.stdin, select.POLLIN)
测试
与之前相比,并不会停下来,因为此时采用的非阻塞IO,即使write操作失败,也不会阻塞read。

非阻塞IO小结
-
需要应用层缓存
-
非阻塞IO应该是网络库实现的,但是需要了解
是否多了IO复用后就可以不用阻塞IO,即每次读写之前先检查是否可读?
不行。
-
例子1:accept有连接到来时,socket fd变得可读。如果用阻塞IO,在变得可读到真正读期间,连接可能断开,此时再读,就会一直阻塞在读操作
-
例子2:select、epoll、poll即使报告fd可读,但也有可能是错误地报错可读
-

浙公网安备 33010602011771号