• 035_IO 模型


    IO 模型分类

        * blocking IO           阻塞IO
        * nonblocking IO      非阻塞IO
        * IO multiplexing      IO多路复用
        * signal driven IO     信号驱动IO
        * asynchronous IO    异步IO

    1,阻塞IO 的图

      recv数据

      阻塞IO

      非阻塞IO

     

      IO多路复用

    2,

      阻塞IO    : 工作效率低
      非阻塞IO  : 工作效率高,频繁询问切换加大CPU的负担
      IO多路复用: 在有多个对象需要IO阻塞的时候,能够有效的减少阻塞带来的时间损耗,且能够在一定程度上减少CPU的负担
      异步IO : asyncio  异步IO  工作效率高 CPU的负担少

    3,网络IO

           # recv     recvfrom   accept   requests.get()
           # send    connect    sendto
    4,IO的两个阶 段
           # 数据准备阶段
           # 数据copy阶段
     
    解决阻塞IO 问题
       1)多进程 多线程 分离了阻塞
        但进程线程不能无限开!
       2)以后用进程都用进程池
        单纯的进程池不能满足用户的需求,只适合小并发的问题,真正需要我们解决的是I/O问题
    1,非阻塞IO实例
    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    sk.setblocking(False)    
     # 异步的,但是来不及打开client端建立连接,就已经报错
    # 因为下一句不阻塞了
    # recv 也不阻塞了,但一样会报错 ,有连接时,且设为不阻塞时,没消息就打印空消息。
    # 原因:调用阻塞异常错误(变量没有被赋值)
    conn,addr = sk.accept()
    print(conn)
    sever端
    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    sk.setblocking(False)
    conn_lst = []    # 用来保存已经建立的连接
    
    while True:
        try:
            conn,addr = sk.accept()   #非阻塞  
            conn_lst.append(conn)   # 不将建立成功的连接用列表保存下来,conn会不断被新连接覆盖。
        except BlockingIOError:
            del_lst = []
            for c in conn_lst:    
                try:
                    msg = c.recv(10).decode('utf-8')  # recv不会阻塞
                    if not msg:
                        c.close()
                        del_lst.append(c)
                    else:
                        print(msg)
                        c.send(msg.upper().encode('utf-8'))
                except BlockingIOError:
                    pass
            if del_lst:
                for del_item in del_lst:
                    conn_lst.remove(del_item)
    sever端改进
    import time
    import socket
    import threading
    def func():
        sk = socket.socket()
        sk.connect(('127.0.0.1',8080))
        time.sleep(1)
        sk.send(b'hi')
        print(sk.recv(10))
        sk.close()
    
    for i in range(10):
        threading.Thread(target=func,).start()
    client端

    多路复用

    # 操作系统提供的多路复用机制
          #  select  、 poll 、  epoll、
          # windows 上只有 select
          # linux 兼容三种

      # 原来是recv,现在是交给select ;select([recv])

    1,参数

      select(rlist,wlisr,xlist,timeout = None)
                等能  读,写,改   三个参数是列表,必须传参,没有参数传空列表。返回的结果是一个有三个列表的元组,分别是读,写,改的结果。

    2,实例

    import time
    import socket
    import threading
    def client_async(args):
        sk = socket.socket()
        sk.connect(('127.0.0.1',8099))
        for i in range(10):
            time.sleep(2)
            sk.send(('%s[%s] :hello'%(args,i)).encode('utf-8'))
            print(sk.recv(1024))
        sk.close()
    
    for i in range(10):
        threading.Thread(target=client_async,args=('*'*i,)).start()
    client端
    import socket
    import select
    sk = socket.socket()
    sk.bind(('127.0.0.1',8099))
    sk.listen()
    
    read_lst = [sk]
    while True:
        rl,wl,xl = select.select(read_lst,[],[])   # select阻塞,rl可以读的 wl可以写的 xl可以改的  [sk,conn]
        for item in rl:
            if item == sk:
                conn,addr = item.accept()  # 有数据等待着它接收
                read_lst.append(conn)
            else:
                ret = item.recv(1024).decode('utf-8')
                if not ret:
                    item.close()
                    read_lst.remove(item)
                else:
                    print(ret)
                    item.send(('received %s'%ret).encode('utf-8'))
    sever 端

    3,

    # select poll 随着要检测的数据增加 效率会下降,不适合处理大并发。
    # select  有数目的限制
    # poll    能处理的对象更多
    # epoll   能处理多对象且 不是使用轮询  而是用回调函数 —— linux
    #这三种IO多路复用模型在不同的平台有着不同的支持,而epoll在windows下就不支持,好在我们有selectors模块,帮我们默认选择当前平台下最合适的
    from socket import *
    import selectors
    
    sel=selectors.DefaultSelector()   # 创建一个默认的多路复用模型
    def accept(sk):
        conn,addr=sk.accept()
        sel.register(conn,selectors.EVENT_READ,read)
    
    def read(conn):
        try:
            data=conn.recv(1024)
            if not data:   #win8 win10
                print('closing',conn)
                sel.unregister(conn)
                conn.close()
                return
            conn.send(data.upper()+b'_SB')
        except Exception:    # linux操作系统
            print('closing', conn)
            sel.unregister(conn)
            conn.close()
    
    sk=socket(AF_INET,SOCK_STREAM)
    sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    sk.bind(('127.0.0.1',8088))
    sk.listen(5)
    sk.setblocking(False) #设置socket的接口为非阻塞
    sel.register(sk,selectors.EVENT_READ,accept) #相当于网select的读列表里append了一个文件句柄server_fileobj,并且绑定了一个回调函数accept
    
    while True:
        events=sel.select() #检测所有的fileobj,是否有完成wait data的   #[sk,conn]
        for sel_obj,mask in events:   # 有人触动了你在sel当中注册的对象
            callback=sel_obj.data #callback=accpet   # sel_obj.data就能拿到当初注册的时候写的accept/read方法
            callback(sel_obj.fileobj) #accpet(sk)/read(conn)
    服务端
  • 相关阅读:
    坑爹的 Segmentation fault
    静态全局变量得初始化
    新冠肺炎的感受
    \r和\n的区别
    程序里面带有浮点数,默认会自动转换为double类型存储
    Relativity : Fictitious forces
    Biology 04: The Senses
    156 TCP协议的三次握手和四次挥手
    155 大白话OSI七层协议
    154 互联网和互联网的组成
  • 原文地址:https://www.cnblogs.com/eternity-twinkle/p/10732921.html
Copyright © 2020-2023  润新知