22-使用非阻塞IO-1


 

 

 

代码

recipes/netcat-nonblock.py at master · chenshuo/recipes (github.com)

 

 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。

image-20230218115400459

 

非阻塞IO小结

  • 需要应用层缓存

  • 非阻塞IO应该是网络库实现的,但是需要了解

 

是否多了IO复用后就可以不用阻塞IO,即每次读写之前先检查是否可读?

 

不行。

  • 例子1:accept有连接到来时,socket fd变得可读。如果用阻塞IO,在变得可读到真正读期间,连接可能断开,此时再读,就会一直阻塞在读操作

  • 例子2:select、epoll、poll即使报告fd可读,但也有可能是错误地报错可读

  •  
posted @ 2023-04-29 15:50  DavidJIAN  阅读(58)  评论(0)    收藏  举报