为了更好地了解IO模型,咱们须要事先回顾下:同步、异步、阻塞、非阻塞1.网络传输中的两个阶段 分别是 waitdata 和 copydata send---copydata recv---waitdata + copydata 记住这两点很重要,由于这些IO模型的区别就是在两个阶段上各有不一样的状况。2.阻塞IO 不管是线程 进程 仍是线程 进程池 通通都是阻塞IO 应用程序 发送 系统调用---操做系统等待数据(wait)---数据准备好 return data 因此,blocking IO的特色就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。3.非阻塞IO 协程是一种非阻塞IO server.setblocking(False)将阻塞修改成非阻塞 最直接体现 recv send accept 都不会阻塞 会当即执行 可是不能保证立马就有数据 没有数据抛出异常 咱们须要手动捕获异常 捕获异常后能够处理别的任务 能够实现单线程并发的效果 但会大量占用CPU资源 while True: pass 因此,在非阻塞式IO中,用户进程实际上是须要不断的主动询问kernel数据准备好了没有。4.多路复用 管理链接的一种方式 为何使用它? 相对于非阻塞IO下降无用的系统调用 怎么管? 核心函数select 帮你检测全部的链接 找出能够被处理(能够读写)的链接 (默认时阻塞的 阻塞到有任意一个链接能够被处理) 结论: select的优点在于能够处理多个链接,不适用于单个链接 一 建立链接 和管理链接 1.建立服务器socket对象 2.将服务器对象交给select来管理 3.一旦有客户端发起链接 select将不在阻塞 4.select将返回一个可读的socket对象(第一次只有服务器) 5.服务器的可读表明有链接请求 须要执行accept 返回一个客户端链接conn 因为是非阻塞 不能当即去recv 6.把客户端socket对象也交给select来管理 将conn加入两个被检测的列表中 7.下一次检测到可读的socket 多是服务器 也可能客户端 因此加上判断 服务器就accept 客户端就recv 8.若是检测到有可写(能够send就是系统缓存可用)的socket对象 则说明能够向客户端发送数据了 7 和 8 执行顺序不是固定的 二 处理数据收发 两个须要捕获异常的地方 1.recv 执行第7步 表示能够读 为何异常 只有一种可能客户端断开链接 还须要加上if not 判断是否有数据 ;linux下 对方下线不会抛出异常 会收到空消息 2.send 执行第8步 表示能够写 为何异常 只有一种可能客户端断开链接) 强调: 1. 若是处理的链接数不是很高的话,使用select/epoll的web server不必定比 使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。 select/epoll的优点并非对于单个链接能处理得更快,而是在于能处理更多的链接。 2. 在多路复用模型中,对于每个socket,通常都设置成为non-blocking,可是,如上图所示, 整个用户的process实际上是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。 结论: select的优点在于能够处理多个链接,不适用于单个链接5.异步IO 网络IO+本地IO 都适用 IO包括 网络IO 本地IO 上面的三种IO模型描述的都是网络IO,不是本地IO的问题 解决的方案就是: 将同步的IO操做改为异步的IO操做 在IO期间 能够执行其余的任务 最终的解决方案就是协程 使用asyncio模块 该模快实现异步IO 内部使用协程实现它的流程:用户进程发起read操做以后,马上就能够开始去作其它的事。而另外一方面,从kernel的角度, 当它受到一个asynchronous read以后,首先它会马上返回,因此不会对用户进程产生任何block。 而后,kernel会等待数据准备完成,而后将数据拷贝到用户内存, 当这一切都完成以后,kernel会给用户进程发送一个signal,告诉它read操做完成了。socketserver 是什么? 对服务器端的socket的封装 封装了多线程 多进程 IO模型,支撑高并发 高并发 的socket套接字 为何用? 简化代码 使用方法: socketserver (forkingUDP forkingTCP windows没法使用) 核心类 ThreadingUDPServer ThreadingTCPServer ThreadingTCPServer 实例化时 传入服务器地址 和 自定义的一个数据处理类 自定义类须要继承BaseRequestHandler类中需包含handle函数 对象调用serve_foreverTCP服务端import socketserverclass MyHandler(socketserver.BaseRequestHandler): def handler(self): while True: try: data=self.request.recv(1024) if not data:break print(data.decode('utf-8')) self.request.send(data.upper()) except ConnectionResetError: break self.request.close()if __name__ == '__main__': server=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyHandler) server.serve_forever()UDP服务端import socketserverclass MyHandler(socketserver.BaseRequestHandler): def handle(self): data,server=self.request print(data.decode('utf-8')) server.sendto(data.upper(),self.client_address)if __name__ == '__main__': server=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyHandler) server.serve_forever()