zoukankan      html  css  js  c++  java
  • python之路10:IO多路复用

    1. IO模型
    2. select模块
    3. selectors模块

    IO模型

    网络IO的本质是socket的读取,socket在linux系统中被抽象为流,IO可以理解为对流的操作.对于一次IO访问,数据会先被拷到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间,所以说当一个read操作发生时,它会经理两个阶段:

    第一阶段:等待数据准备;第二阶段:将数据从内核拷贝到进程中

    对socket流而言:第一步:通常涉及等待网络上的数据分组到达,然后被复制到内核的某个缓冲区。第二步:把数据从内核缓冲区复制到应用进程缓冲区。

    IO的模型大致有如下几种:

    异步IO(asynchronous IO)

    同步IO(synchronous IO) 

    1. 阻塞IO(bloking IO)
    2. 非阻塞IO(non-blocking IO)
    3. 多路复用IO(multiplexing IO)
    4. 信号驱动式IO(signal-driven IO)

    这里写图片描述

    同步阻塞 IO(blocking IO)

    这里写图片描述

    同步非阻塞 IO(nonblocking IO)

    这里写图片描述

    IO 多路复用( IO multiplexing) 

    这里写图片描述

    信号驱动式 IO(signal-driven IO)

    这里写图片描述

     异步非阻塞 IO(asynchronous IO)

    这里写图片描述

    五种模型总结

       

    select模块

    Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

    select(rlist, wlist, xlist, timeout=None)

    select 函数监视的文件描述符分3类,分别是readfds、writefds和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。

    应用:

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 __author__ = 'BillyLV'
     4 
     5 from socket import *
     6 import select
     7 
     8 server = socket(AF_INET, SOCK_STREAM)  # ipv4, tcp
     9 server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 端口重用
    10 server.bind(('127.0.0.1', 8090))
    11 server.listen(10)
    12 server.setblocking(False)  # 设置socket的套接字为非阻塞
    13 read_l = [server, ]  # 因为不只就那么一个列表要检测,所以不要在参数里面定死了
    14 while True:
    15     r_l, w_l, x_l = select.select(read_l, [], [])
    16     print(r_l)  # 检测到有数据
    17     for ready_obj in r_l:
    18         if ready_obj == server:
    19             conn, addr = ready_obj.accept()  # accept要经历两个阶段,但是程序如果走到这一步,那肯定是数据准备好了
    20             # print(addr)
    21             read_l.append(conn)
    22         else:
    23             try:
    24                 data = ready_obj.recv(1024)  # 此时的ready_obj等于conn
    25                 if not data:
    26                     read_l.remove(ready_obj)
    27                     continue
    28                 ready_obj.send(data.upper())
    29             except ConnectionResetError:
    30                 read_l.remove(ready_obj)
    服务端IO多路复用
     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 __author__ = 'BillyLV'
     4 
     5 from socket import *
     6 client = socket(AF_INET, SOCK_STREAM)  # ipv4, tcp
     7 client.connect(('127.0.0.1', 8091))
     8 while True:
     9     msg = input('>>: ')
    10     if not msg:
    11         continue
    12     client.send(msg.encode('utf-8'))
    13     data = client.recv(1024)
    14     print(data.decode('utf-8'))
    客户端IO多路复用

    selectors模块

    SelectPollEpoll这三种IO多路复用模型在不同的平台有着不同的支持,相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。而epoll在windows下就不支持,好在我们有selectors模块,帮我们默认选择当前平台下最合适的。该模块允许基于所选模块原语的高水平和高效的IO多路复用。鼓励用户使用此模块,除非他们希望对所使用的操作系统级原语进行精确控制。

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 __author__ = 'BillyLV'
     4 
     5 import selectors
     6 from socket import *
     7 
     8 sel = selectors.DefaultSelector()  # 选择最佳实施
     9 
    10 
    11 def accept(server_fileobj, mask):
    12     conn, addr = server_fileobj.accept()
    13     print('accepted', conn, 'from', addr)
    14     conn.setblocking(False)
    15     sel.register(conn, selectors.EVENT_READ, read)
    16 
    17 
    18 def read(conn, mask):
    19     data = conn.recv(1024)
    20     if data:
    21         print('echoing', repr(data), 'to', conn)
    22         conn.send(data)
    23     else:
    24         print('closing', conn)
    25         sel.unregister(conn)
    26         conn.close()
    27 
    28 
    29 server_fileobj = socket(AF_INET, SOCK_STREAM)
    30 server_fileobj.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    31 server_fileobj.bind(('localhost', 2130))
    32 server_fileobj.listen(200)
    33 server_fileobj.setblocking(False)  # 设置socket的接口为非阻塞
    34 # 相当于往select的读列表里append了一个文件句柄server_fileobj,并且绑定了一个回调函数accept
    35 sel.register(server_fileobj, selectors.EVENT_READ, accept)
    36 
    37 while True:
    38     events = sel.select()  # 检测所有的fileobj,是否有完成wait data的
    39     for sel_obj, mask in events:
    40         callback = sel_obj.data  # callback=accpet
    41         callback(sel_obj.fileobj, mask)  # accpet(server_fileobj,1)
    服务端
     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 __author__ = 'BillyLV'
     4 
     5 from socket import *
     6 client = socket(AF_INET, SOCK_STREAM)  # ipv4, tcp
     7 client.connect(('localhost', 2130))
     8 while True:
     9     msg = input('>>: ')
    10     if not msg:
    11         continue
    12     client.send(msg.encode('utf-8'))
    13     data = client.recv(1024)
    14     print(data.decode('utf-8'))
    客户端

    参考: 

    http://www.cnblogs.com/alex3714

    http://www.cnblogs.com/wupeiqi

    internet&python books

    PS:如侵权,联我删。

  • 相关阅读:
    中国地区免费注册bitcointalk论坛教程
    Broken Keyboard (a.k.a. Beiju Text) 思路
    IE兼容性測試軟件
    HttpRunner接口自动化框架的使用
    在Linux服务器上安装Python3.7
    在Linux系统上安装Git
    在Linux系统上安装配置ant环境
    桥接模式:探索JDBC的接口
    Intelij Idea下的git使用
    SSM+Maven(教程二):Idea快速入门SSM+Maven框架。
  • 原文地址:https://www.cnblogs.com/BillyLV/p/11013410.html
Copyright © 2011-2022 走看看