11 非阻塞套接字与IO多路复用(进阶)
1、非阻塞套接字
第一部分 基本IO模型
1.普通套接字实现的服务端的缺陷


一次只能服务一个客户端!
2.普通套接字实现的服务端的瓶颈!!!
accept阻塞!
在没有新的套接字来之前,不能处理已经建立连接的套接字的请求。
recv 阻塞!
在没有接受到客户端请求数据之前,
不能与其他客户端建立连接!
3.普通服务器的IO模型
第二部分 非阻塞套接字
1.非阻塞套接字与普通套接字的区别
>>> import socket
>>> server = socket.socket()
# 讲socket设置成非阻塞
>>> server.setblocking(False) # 注意!这必须要在其他操作之前!
>>> server.bind(('',8080))
>>> server.listen(5)
>>> server.accept() # 没有连接就引发BlockingIOError
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
server.accept()
File "E:\python\lib\socket.py", line 205, in accept
fd, addr = self._accept()
BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。
# 使用一个客户端(普通的就行,不需要非阻塞)连接过来
>>> conn,addr = server.accept() # 有连接则正确返回
>>> conn.recv(1024) # 没有连接数据就引发BlockingIOError
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
conn.recv(1024)
BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。
2.使用非阻塞套机字实现阻塞的服务端
# 原来的recv
while True:
try:
recv_data = conn.recv(1024)
break
except BlockingIOError:
pass # 原来的accept
while True:
try:
conn,addr = server.accept()
break
except:BlockingIOError:
pass
3。非阻塞客户端套接的注意点
connect操作一定会引发BlockingIOError异常
如果连接没有建立,那么send操作引发OSError异常
第三部分 非阻塞IO模型


第四部分 使用非阻塞套接字实现并发
1.整体思路
吃满 CPU !宁可用 whileTrue ,也不要阻塞发呆!
只要资源没到,就先做别的事!将代码顺序重排,避开阻塞!
2.实现了什么?
并发服务多个客户端!
3.编程范式
import socket
server = socket.socket() # 生成套接字
server.setblocking(False) # 非阻塞
server.bind(('',7788))
server.listen(1000)
# 我们现在生成的非阻塞套接字,非阻塞套接字在执行accept跟recv的时候不会阻塞,但是会报错,
# 所以我们写非阻塞的并发服务器需要用到异常处理
all_conn = [] # 用来保存我们所有的已经生成的套接字(这个客户端还在连接着)
while True:
# 处理连接,生成对等连接套接字
try:
conn,addr = server.accept()
conn.setblocking(False) # conn是新生成的,需要给它设置一下非阻塞
all_conn.append(conn) # 这一行代码的前提是,上面一行代码正常返回
except BlockingIOError:
pass
for conn in all_conn:
try: # 只负责接受数据
recv_data = conn.recv(1024)
if recv_data:
res = recv_data.decode()
print(res)
conn.send(recv_data)
else:
conn.close()
all_conn.remove(conn) # 客户端关闭连接,就把它移除
except BlockingIOError:
pass
2、IO多路复用
第一部分 不完美的CPU利用率
关键一: 任何Python操作都是需要花费CPU资源的 !
关键二: 如果资源还没有到达,那么
accept、recv以及
send(在connect没有完成时)
操作都是无效的CPU花费 !
关键三: 对应BlockingIOError的异常处理
也是无效的CPU花费 !
第二部分 epoll是真正的答案!
IO多路复用技术
我们把socket交给操作系统去监控


2.epoll是惰性的事件回调
惰性事件回调是由用户进程自己调用的。
操作系统只起到通知的作用。


3.为什么是epoll ?
目前Linux上效率最高的IO多路复用 技术 !
第三部分 IO多路复用选择器
1.注册惰性事件回调
>>> import socket
>>> import selectors
>>> server = socket.socket()
>>> server.bind(('',9000))
>>> server.listen(1000)
>>> selector = selectors.EpollSelector() # 实例化一个 epoll 选择器
>>> def create_conn(server):
... conn,addr = server.accept()
... return conn
...
# 套接字、事件、回调函数
>>> selector.register(server,selectors.EVENT_READ,create_conn)
SelectorKey(fileobj=<socket.socket fd=3, # 生成一个打包对象
family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 9000)>, # fileobj是对应套接字
fd=3,
events=1, # 事件(1 表示EVENT_READ)
data=<function create_conn at 0xb70b7b24>) # data是对应的回掉函数
2.事件回调
events = selector.select() # 查询,返回所有已经准备好的资源打包对象
print(enents) # 是一个 ‘二元组’ 的列表
[(SelectorKey(fileobj=<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 8888)>, fd=4, events=1, data=<function accept at 0xb71f292c>), 1)]
# 我们只需要关心,每个元祖的第一项(即打包对象,其中包含了对应的套接字与回掉函数)
# 接下来并不需要关心是什么套接字,什么事件,只需要调用对应的对调函数即可
callbeck = events[0][0].data
sock = events[0][0].fileojb
callbeck(sock)
3.编程范式
# 使用EpollSelector,实现一个并发的服务器
import socket
import selectors # IO多路复用选择器的模块,接口,调用epoll
epoll_selector = selectors.EpollSelector() # 创建一个用来和epoll通信的选择器
server = socket.socket()
server.bind(('',8888))
server.listen(1000)
def read(conn):
recv_data = conn.recv(1024)
if recv_data:
print(recv_data.decode())
conn.send(recv_data)
else:
epoll_selector.unregister(conn) # 现在数据已经传输完了,那我现在就不用再去监控它了,所以# 关闭监控
conn.close() # 关闭连接
def accept(server):
conn, addr = server.accept() # 生成一个对等连接套接字
# 要准备接受数据
epoll_selector.register(conn, selectors.EVENT_READ, read)
epoll_selector.register(server,selectors.EVENT_READ, accept) # 注册事件在可以读的时候的回调函数
# 事件循环(主动去问epoll,哪些socket可以回调了,如果有了,那我就回调)
while True:
events = epoll_selector.select() # 查询所有的已经准备好的事件,返回一个列表(二元组列表)
# a, b = events
for key, mask in events: # 第一项是我们需要用的
callback = key.data # 从key里面把回掉函数拿出来
sock = key.fileobj # 从key里面把我们注册的那个socket拿出来
callback(sock)
11 非阻塞套接字与IO多路复用(进阶)的更多相关文章
- 非阻塞套接字与IO多路复用
我们了解了socket之后已经知道,普通套接字实现的服务端的缺陷:一次只能服务一个客户端! 并且,为了使一个客户端能够不断收发消息,我们还要使用while循环来轮询,这极大地降低了我们的效率 acce ...
- 非阻塞套接字与IO多路复用(转,python实现版)
非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回.epoll工作在非阻塞模式时,才会发挥作用. 我们了解了socket之后已经知道,普通套接字实现的服务端的缺陷:一次只能服务一个 ...
- 非阻塞套接字编程, IO多路复用(epoll)
非阻塞套接字编程: server端 import socket server = socket.socket() server.setblocking(False) server.bind(('', ...
- C++ 非阻塞套接字的使用 (3)
异步非阻塞套接字避免了死循环的接收问题,但是软件用起来体验还是很差.究其原因,软件在指令的发送.接收上, 采取了一种不合理的方式:在指令的发送后,立刻调用接收函数,等待回令. 若是采用同步阻塞套接字, ...
- python_非阻塞套接字及I/O流
http://www.cnblogs.com/lixy-88428977/p/9638949.html 首先,我们要明确2个问题: 普通套接字实现的服务端有什么缺陷吗? 有,一次只能服务一个客户端! ...
- C++ 非阻塞套接字的使用 (1)
在维护代码的过程中,发现软件运行的CPU占用率居高不下,在4核的电脑上占用了25%的CPU.查阅资料的得知,这是可能是由于软件中出现了死循环. 经过对软件的一些测试,最终确定了死循环出现的位置——通讯 ...
- C++ 非阻塞套接字的使用 (2)
继续话题——软件中的异步非阻塞通讯方式. 由于软件基于MFC开发,所以实现异步通讯时使用了CAsyncSocket类. 首先要了解CAsyncSocket异步机制,引用自 http://blog.cs ...
- 五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O
五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O 五种I/O 模式:[1] 阻塞 I/O ...
- python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)
python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...
随机推荐
- 视频监控——从其他浏览器打开低版本IE方案
1. 方案背景 由于低版本IE浏览器并不支持很多新的页面技术,导致部分页面效果难以实现;另一方面IE浏览器版本与操作系统绑定,难以统一,不同版本IE间的不兼容导致多种兼容性问题,因此本项目暂定采用Ch ...
- BIEE入门(二)物理层的定义
使用BIEE的第一步是使用admintool去建立一个多维数据模型,而建立多维数据模型的第一步则是建立物理层,请注意因为BIEE本身并不存 储数据,所以所谓BIEE物理层的意义是需要在BIEE里建立各 ...
- Windows平台,Oracle Database和Client并存方式
由于某些特定需求,生产环境需要在同一台机器上同时安装Database Server和Client,因为plsql没有64位客户端,无法读取64位database的oci.dll文件,所以需要一个32位 ...
- Oracle 11.1.0.6 导入导出bug
实验环境: 11.1.0.6.0 对ANONYMOUSUSER_ALL表中分区进行备份 SQL> select TABLE_NAME,PARTITION_NAME,HIGH_VALUE,PA ...
- dos基础+环境搭建基础理论
dos基础 市面上两大操作系统 windows.*nix(unix.linux.mac.bsd(安全性比较高)) 后三种都属于unix的衍生版本 linux是为了兼容unix开发的,最后开放了源代码 ...
- codis学习
一.codis-proxy 结构 1.Topology 2.Slots 3.ServerGroup 4.Server 二.codis-proxy 启动过程 1.初始化ProxyInfo Id ...
- Android学习笔记_43_网络通信之文件断点上传
1.建立服务端,用于接收上传的文件.这里使用Socket,文件可能会比较大.采用多线程编程,防止并发. package com.socket.service; import java.io.File; ...
- ADO.NET 之 Entity Framework 基础
Entity Framework(EF)是使用直接映射到应用程序中业务对象的对象模型于关系数据库进行交互.它没有将数据视为行和列的集合,而是将其视为强类型对象(成为实体)的集合. 术语:LinQ to ...
- android(eclipse)界面控件以及活动总结(二)
用户界面以及活动总结: (复习时忘了的特别标记了下划线,补充的用了红色) 1用户界面: ~android:id=""专门用于找到指定界面的索引 如果要在后面的程序中调用该控件,则一 ...
- 排序算法 JavaScript
一.冒泡排序 算法介绍: 1.比较相邻的两个元素,如果前一个比后一个大,则交换位置. 2.第一轮把最大的元素放到了最后面. 3.由于每次排序最后一个都是最大的,所以之后按照步骤1排序最后一个元素不用比 ...
