网络IO模型

为了更好地了解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()
相关文章
相关标签/搜索