zoukankan      html  css  js  c++  java
  • 网络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_forever

    TCP服务端
    import socketserver

    class 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 socketserver

    class 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()
  • 相关阅读:
    关于在pycharm下提示ModuleNotFoundError: No module named 'XXX' 的一种可能
    记录Python类与继承的一个错误
    JavaScript 中this 初步理解笔记
    Kibana6.x.x源码分析--如何自定义savedObjectType对象
    Kibana6.x.x源码分析--如何使用kibana的savedObjectType对象
    AngularJs依赖注入写法笔记
    Kibana6.x.x源码分析--ngReact使用
    Kibana6.x.x源码分析--Error: $injector:nomod Module Unavailable
    互联网时代,我们可以选择不做穷人
    ES6问题记录--Promise
  • 原文地址:https://www.cnblogs.com/du-jun/p/9963303.html
Copyright © 2011-2022 走看看