IO模型
常见的网络罗阻塞状态
- accept
- recv
- recvfrom
send虽然也有io形为,但是不在我们的考虑范围内
阻塞IO
我们之前写的都是阻塞IO模型,协程除外。在服务端开设多进程或着多线程,进程池线程池,只是多个人一起等待,该等的地方还是得等,没有解决io问题
非阻塞IO
从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是用户就可以在本次到下次再发起read询问的时间间隔内做其他事情,或者直接再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存(这一阶段仍然是阻塞的),然后返回。
也就是说非阻塞的recvform系统调用调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,此时会返回一个error。进程在返回之后,可以干点别的事情,然后再发起recvform系统调用。重复上面的过程,循环往复的进行recvform系统调用。这个过程通常被称之为轮询。轮询检查内核数据,直到数据准备好,再拷贝数据到进程,进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态。
所以,在非阻塞式IO中,用户进程其实是需要不断的主动询问kernel数据准备好了没有。
模型实现
# 服务端 import socket server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) server.setblocking(False) # 默认为True,改为False后将所有网络阻塞recv,accept变为非阻塞 r_list = [] del_list = [] while True: try: conn,addr = server.accept() r_list.append(conn) except BlockIOError as e: print('列表长度:',len(r_list)) # 对接收的每一个链接,依次接收消息 for conn in r_list: try: data = conn.recv(1024) # 当前链接没有发送消息,查看下一个。。。 if len(data) == 0: # 客户端断开连接 conn.close() # 关闭conn del_list.append(conn) # 将断开的链接从r_list中删除 continue except BlockingIOError: continue except ConnectionResetError: conn.close() del_list.append(conn) for conn in del_list: r_list.remove(conn)
# 客户端 import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: client.send(b'hello') data = client.recv(1024)
虽然非阻塞模型可以实现无阻塞,但是这个模型会让cpu一直空转,实际应用中不会考虑使用非阻塞IO模型
模型没有实际意义,是用于思想借鉴,搭建框架
IO多路复用
操作系统提供的监管机制,能够帮你监督socket对象和conn对象。只要有人触发了,立刻将对应的对象返回
select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程
当监管的对象只有一个的时候,IO多路复用的效率还比不上阻塞IO,但是多路复用可以一次监管多个对象
监管机制是操作系统本身就有的,如果要用该监管机制,需要导入对应的模块(select)
如何用
import socket import select server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) server.setblocking(False) read_list = [server] while True: r_list, w_list, x_list = select.select(read_list, [], []) """ 帮你监管 一旦有人来了 立刻给你返回对应的监管对象 """ # print(res) # ([<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080)>], [], []) # print(server) # print(r_list) for i in r_list: # """针对不同的对象做不同的处理""" if i is server: conn, addr = i.accept() # 也应该添加到监管的队列中 read_list.append(conn) else: res = i.recv(1024) if len(res) == 0: i.close() # 将无效的监管对象 移除 read_list.remove(i) continue print(res) i.send(b'heiheiheiheihei') # 客户端 import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: client.send(b'hello world') data = client.recv(1024) print(data)
总结:监管机制有很多,select,poll,epoll,等等机制
select机制windows,Linux都有,poll机制只在linux有。poll和select都可以监管多个对象,但是poll监管的数量更多,select和poll机制都不是最完美的机制:当监管对象特别多的时候,会出现延时响应
epoll:只在Linux有,给每一个监管对象都绑定一个回调机制,一旦有相应,惠帝啊机制立刻发起提醒
针对不同的操作系统需要考虑不同的监测机制,写代码繁琐:可以使用selector模块,帮你针对不同的操作平台,选择不同的监管机制
异步IO模型
所有模型中效率最高,使用最广泛的,用于实现高并发,高速的场景
相关的模块和框架 模块:asyncio 框架:sanic,tronado,twisted 速度快
# asynico import asyncio,threading @asyncio.coroutine def hello(): print('hello',threading.current_thread()) yield from asyncio.sleep(1) print
四个IO模型对比
网络并发知识点梳理
-
软件开发框架
-
互联网协议
osi七层,要知道每一层是干什么的,有什么协议 - 以太网协议 - IP协议 - 。。。 TCP/UDP协议 (重要) - 三次握手 - 四次挥手
-
socket简介
-
TCP粘包问题
自定义报头:字典 struct模块
socketserver模块
-
并发编程
- 操作系统发展史
- 进程理论
- 开启进程的两种方式
- 互斥锁
- 生产者消费者模型
- 线程理论
- 开启线程的两种方式
- GIL全局解释器锁
- 进程池,线程池
- 协程
- IO模型
下载MySql 5.6