一:引入
Unix/Linux上有五种IO模型:阻塞(blocking)、非阻塞(non-blocking)、IO复用(IO multiplexing)、信号驱动(signal-driven)、异步(asynchronous)
当IO复用搭配阻塞IO可能会出现什么问题呢?应该如何尽量避免呢?

二:测试工具
chargen:服务端accept连接之后,不停地发送测试数据。
linux的nc及python程序netcat_nonblock.py,netcat.py

三:测试实验
(1)开启服务端chargen,用不同的nc连接观察异同

(2)使用linux自带nc连接。
1)输入缓冲区无输入时
2)有输入时

(3)netcat.py连接chargen时
1)输入缓冲区无输入时

2)有输入时

使用strace追踪代码

strace python netcat.py localhost 5001 </dev/zero > /dev/null

发现程序阻塞在发送上

sendto(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 8192, 0, NULL, 0

使用netstate -tpn观察tcp读写缓冲区接收与发送数据大小

netstate -tpn | grep 5001

(4)netcat_nonblock.py连接chargen时
1)输入缓冲区无输入时
2)有输入时

四:实验分析
(1)netcat.py代码如下:

#!/usr/bin/pythonimport os
import select
import socket
import sys#发送方与接收方数据传输不是独立的,阻塞io可能导致阻塞
#io复用,检查那个描述符准备好
def relay(sock):poll = select.poll()#注册两个读事件,读sock和stdinpoll.register(sock, select.POLLIN)   poll.register(sys.stdin, select.POLLIN)done = Falsewhile not done:events = poll.poll(10000)  # 10 secondsfor fileno, event in events:if event & select.POLLIN:if fileno == sock.fileno():data = sock.recv(8192)if data:sys.stdout.write(data)else:done = Trueelse:assert fileno == sys.stdin.fileno()data = os.read(fileno, 8192)if data:sock.sendall(data)else:sock.shutdown(socket.SHUT_WR) #将socket数据读完再关闭连接,防止tcp发生rst分解,使数据接收不完整poll.unregister(sys.stdin)def main(argv):if len(argv) < 3:binary = argv[0]print "Usage:\n  %s -l port\n  %s host port" % (argv[0], argv[0])returnport = int(argv[2])if argv[1] == "-l":# serverserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)server_socket.bind(('', port))server_socket.listen(5)(client_socket, client_address) = server_socket.accept()server_socket.close()relay(client_socket)else:# clientsock = socket.create_connection((argv[1], port))relay(sock)if __name__ == "__main__":main(sys.argv)

结合实验(3)第(2)个实验看到,阻塞io下当从输入缓冲区输入数据时,tcp缓冲区chargen及python都在写数据,进而导致两者都不能读数据而永久阻塞在写数据上。故说明发送方与接收方数据传输不是独立的,阻塞io可能会阻塞。
注意点:
sock.shutdown(socket.SHUT_WR)
当发送完数据之后再进行关闭写端,否则会导致数据接收不完整。

netcat_nonblock.py

#!/usr/bin/pythonimport errno
import fcntl
import os
import select
import socket
import sysdef setNonBlocking(fd):flags = fcntl.fcntl(fd, fcntl.F_GETFL)fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)def nonBlockingWrite(fd, data):try:nw = os.write(fd, data)return nwexcept OSError as e:if e.errno == errno.EWOULDBLOCK:return -1def relay(sock):socketEvents = select.POLLINpoll = select.poll()poll.register(sock, socketEvents)poll.register(sys.stdin, select.POLLIN)setNonBlocking(sock)  //sock用非阻塞 //stdout用阻塞# setNonBlocking(sys.stdin)# setNonBlocking(sys.stdout)done = FalsesocketOutputBuffer = ''while not done:events = poll.poll(10000)  # 10 secondsfor fileno, event in events:if event & select.POLLIN:if fileno == sock.fileno():data = sock.recv(8192)if data:nw = sys.stdout.write(data)  # stdout does support non-blocking write, thoughelse:done = Trueelse:assert fileno == sys.stdin.fileno()data = os.read(fileno, 8192)if data:assert len(socketOutputBuffer) == 0nw = nonBlockingWrite(sock.fileno(), data)if nw < len(data): //发生shortWriteif nw < 0:nw = 0socketOutputBuffer = data[nw:] //用socketOutputBuffer保存剩余数据socketEvents |= select.POLLOUTpoll.register(sock, socketEvents)  //sock登记写事件       前三行通用poll.unregister(sys.stdin)  //因netcat与stdin故采用此行,不通用else://数据读完sock.shutdown(socket.SHUT_WR)poll.unregister(sys.stdin)if event & select.POLLOUT:if fileno == sock.fileno():assert len(socketOutputBuffer) > 0nw = nonBlockingWrite(sock.fileno(), socketOutputBuffer)if nw < len(socketOutputBuffer):  //也可能发生shortWriteassert nw > 0socketOutputBuffer = socketOutputBuffer[nw:]  //保留未发送数据else:socketOutputBuffer = ''socketEvents &= ~select.POLLOUT  //停止观察pollOut事件,否则缓冲区无数据写但sock可写引起电平触发使cpu占满poll.register(sock, socketEvents)  //与前面对应poll.register(sys.stdin, select.POLLIN)def main(argv):if len(argv) < 3:binary = argv[0]print "Usage:\n  %s -l port\n  %s host port" % (argv[0], argv[0])print (sys.stdout.write)returnport = int(argv[2])if argv[1] == "-l":
0        # serverserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)server_socket.bind(('', port))server_socket.listen(5)(client_socket, client_address) = server_socket.accept()server_socket.close()relay(client_socket)else:# clientsock = socket.create_connection((argv[1], port))relay(sock)if __name__ == "__main__":main(sys.argv)

根据实验(4)可知使用非阻塞IO可解决阻塞问题。
关于写事件的通用形式:

if nw < len(data): //发生shortWriteif nw < 0:nw = 0socketOutputBuffer = data[nw:] //用socketOutputBuffer保存剩余数据socketEvents |= select.POLLOUTpoll.register(sock, socketEvents)

当发生shortWrite时,应将剩余数据保存在outBuffer中,然后注册POLLOUT事件,一旦socket变得可写就立刻发送数据。下次写时如果还有剩余,应该继续关注POLLOUT事件。

socketOutputBuffer = ''
socketEvents &= ~select.POLLOUT
poll.register(sock, socketEvents)

数据写完后停止观察pollOut事件,否则缓冲区无数据写但sock可写引起电平触发使cpu占满

四:实验结论
IO multiplexing和blocking IO用在一起可能会阻塞,在Linux多线程服务端编程中因为blocking IO中read()/write()/accept()/connect()都有可能阻塞当前线程,这样线程就没办法处理其他socket上的IO事件了,故一般不将IO multiplexing和blocking IO用在一起。

五:参考
1《Linux多线程服务端编程使用muduo C++网络库》 --陈硕
2陈硕老师的程序chargen,netcat.py 及netcat_nonblock.py源于https://github.com/chenshuo/recipes.git

IO复用在阻塞IO下的坑及解决相关推荐

  1. 同步IO、异步IO、阻塞IO、非阻塞IO、复用IO

    参考:同步IO 异步IO 作者:今天天气眞好 发布时间: 2021-04-19 09:42:29 网址:https://blog.csdn.net/qq_51118175/article/detail ...

  2. 分不清楚阻塞IO,非阻塞IO,IO复用?用最贴近生活的例子带你理解这三者的区别!

    文章目录 前言 一.什么是IO 二.阻塞IO模型 三.非阻塞 IO模型 四.IO复用模型 总结 前言 在<Unix网络编程>一书中提到了五种IO模型,分别是:阻塞IO.非阻塞IO.IO复用 ...

  3. 【死磕NIO】— 阻塞IO,非阻塞IO,IO复用,信号驱动IO,异步IO,这你真的分的清楚吗?

    通过上篇文章([死磕NIO]- 阻塞.非阻塞.同步.异步,傻傻分不清楚),我想你应该能够区分了什么是阻塞.非阻塞.异步.非异步了,这篇文章我们来彻底弄清楚什么是阻塞IO,非阻塞IO,IO复用,信号驱动 ...

  4. 【多线程】0.理解一下5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO

    5种IO模型.阻塞IO和非阻塞IO.同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度.环境不一样.所以,我们先说明基本的IO操作及环境. 本文是在<UNIX网络编 ...

  5. 5种网络IO模型:阻塞IO、非阻塞IO、异步IO、多路复用IO、信号驱动IO

    目录 前言 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) 模型间的区别 ...

  6. IO模型 :阻塞IO、非阻塞IO、信号驱动IO、异步IO、多路复用IO

    文章目录 IO模型 阻塞IO 非阻塞IO 信号驱动IO 多路复用IO 异步IO IO模型 根据各自的特性不同,IO模型被分为阻塞IO.非阻塞IO.信号驱动IO.异步IO.多路复用IO五类. 最主要的两 ...

  7. 阻塞IO、非阻塞IO的区别

    阻塞IO.非阻塞IO的区别 1.类与类之间的关系:依赖,实现,泛化(继承),关联,组合,聚合. 1)依赖(虚线):一个类是 另一个类的函数参数 或者 函数返回值. 2)实现(实线加小圆):对纯虚函数类 ...

  8. 简述同步IO、异步IO、阻塞IO、非阻塞IO之间的联系与区别

    POSIX 同步IO.异步IO.阻塞IO.非阻塞IO,这几个词常见于各种各样的与网络相关的文章之中,往往不同上下文中它们的意思是不一样的,以致于我在很长一段时间对此感到困惑,所以想写一篇文章整理一下. ...

  9. 5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO

    5种IO模型.阻塞IO和非阻塞IO.同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度.环境不一样.所以,我们先说明基本的IO操作及环境.本文是在<UNIX网络编程 ...

最新文章

  1. android web3j 代币查询_wallet-eth 以太坊代币钱包 助记词 私钥 keystore 转账
  2. 算法回顾(三) 二分查找
  3. python excel 自动化-Python 自动化:处理 Excel(笔记)
  4. ImportError cannot import name BytesIO when import caffe
  5. 字符串匹配算法Java_如何简单理解字符串匹配算法?
  6. 对话 Dubbo 唤醒者北纬:3.0 将至,阿里核心电商业务也在用 Dubbo
  7. java中集合的结构Set类型
  8. .net framework 2.0 Silent install(.net framework 静默安装)
  9. 手机协处理器java,HBase1.x实战:协处理器Java开发实例--ObserverCoprocessor
  10. dubbo协议_Dubbo协议解析与OPPO自研ESA RPC框架实践
  11. linux初学文档,51CTO博客-专业IT技术博客创作平台-技术成就梦想
  12. PHP学习笔记十九【析构函数】
  13. myeclipse-common 找不到
  14. 《Linux编程》上机作业 ·001【Linux命令】
  15. cmw500综合测试仪使用_辽宁优质继电器综合测试仪供应商-广州炫通电气科技
  16. jmail组件 java,分享Jmail发送邮件工具类
  17. 深度学习vs深度学习,到底嘛意思?
  18. 视频服务器信号转换器,DVI转换器
  19. SCRAPY爬虫实例
  20. 顶级科学家是哲学家,顶级investor是哲学家

热门文章

  1. 聊聊竞品体验对比评测(上)
  2. Python网络爬虫之urllib2修改Header
  3. 联想win11忘记开机密码的解决办法
  4. Git上传代码到云仓库
  5. [爬虫] 爬取高德地图的面状数据存为shp - 公园数据为例
  6. H - Weekend(folyd+全排列)
  7. 0XID定位多网卡主机 使用记录
  8. Android组件(二)Intent
  9. 2019级软件工程应用与实践-人工智能快递柜(代码分析2)
  10. MySQL 索引概览