网络IO模型

一、阻塞IO模型

阻塞IO基于socket程序

 

原理:

 

 

① recv接收数据时,不是直接接收数据,而是程序将系统调用的命令发送到操作系统

② 当操作系统收到接收数据的请求,若此时无数据,操作系统会继续等待,处于等待数据阶段(wait for data阶段),这个阶段相对漫长

③ 当数据来了,操作系统会拷贝数据(copy data 阶段),从内核copy到内存

④ 当操作系统copy结束后,程序就能获取到recv的数据

 

阻塞IO模型都会经历两个阶段

wait for data 阶段    阻塞

copy data 阶段                   阻塞

存在的问题:一旦阻塞不能执行其他的任务 

 

二、非阻塞IO模型

特点:

① 选择性阻塞,可以执行其他任务

② 没有并发编程的机制,是一个同步的程序

③ 由于非阻塞的特点,程序不会在某一个连接recv或者sk.accept上进行阻塞,由更多的时间来做信息的收发,用一条线程实现并发的效果

      

 

① 程序发起recv,进行系统调用

② 操作系统接收命令后,若此时无数据,操作系统会返回到程序

③ 当有数据时,操作系统从内核copy到内存里

④ 拷贝结束后,程序会接收到数据

 

缺点:高速运行,占用太多的CPU导致资源浪费,给CPU造成了很大的负担

 

程序模拟

# 非阻塞IO模型

import socket

sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.setblocking(False)       # 设置socket server为非阻塞
sk.listen()

conn_lis = []               # 链接的列表
del_lis = []                # 断开链接的列表
while 1:
    try:
        conn,addr = sk.accept()
        conn_lis.append(conn)
    except BlockingIOError:
        for conn in conn_lis:
            try:
                conn.send(b'hello')             
                print(conn.recv(1024))
            except (NameError,BlockingIOError):pass
            except ConnectionResetError:                # 客户端端口连接后
                conn.close()                            # 关闭链接
                del_lis.append(conn)                    # 添加到断开链接的列表
        
        for del_conn in del_lis:                        # 从链接列表里删除断开的链接
            conn_lis.remove(del_conn)
        del_lis.clear()                                 # 清空断开链接的列表

 

三、IO多路复用模型

原理:非阻塞的情况下加上代理

代理是IO多路复用机制提供的代理,操作系统提供IO多路复用的机制,提供了一个模块select

select 没模块用来操作系统中的select(IO多路复用)机制

select.select(rlist,wlist,xlist)    # 按位置传参,传入列表

rlist       读

wlist      写

xlist       特殊的条件

 

① 程序有一个代理(代替conn),代理进行系统调用,向操作系统发出命令,询问是否有数据

② 此时操作系统没数据,操作系统一直等待数据,代理会一直阻塞

③ 当有数据来了,数据准备好了,通知被代理的对象,conn收到信息,conn发起recv

④ 此操作系统copy数据,操作系统进入copy数据阶段

⑤ copy完成,recv拿到数据

 

程序

import select
import socket

sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.setblocking(False)
sk.listen()

r_lis = [sk]        # 存的是socket对象
while 1:
    r_l,_,_ = select.select(r_lis,[],[])    # 按位置参数,不需要的参数要传入空列表
    for item in r_l:
        if item is sk:                      # 判断是否有链接
            conn,addr = sk.accept()
            r_lis.append(conn)
        else:
            try:
                print((item.recv(1024)).decode())      # conn有消息接收
                item.send(b'hello')
            except ConnectionResetError:               # 客户端断开链接
                item.close()                           # 关闭链接conn
                r_lis.remove(item)    

 

IO多路复用是操作系统的工具,模块 select、poll、epoll都是发挥代理的作用,进行监听。但在windows上只有select模块,而在mac/linux系统上,三种机制并存。

select的底层是操作系统在做轮询,有监听对象个数的限制,并且随着监听对象的个数越来越多,效率越来越低

poll 跟 select 一样,但监听的个数比 select 要多

epoll 给每一个要监听的对象都绑定了一个回调函数,数据来了直接调用对象,不再受到个数增加而降低效率的影响

 

sockserver 底层使用了IO多路复用 + threading线程实现的

selectors模块,在不同的系统上进行IO多路复用机制的自动筛选

 

IO多路复用的好处:多个conn使用一个代理,整体的程序显得不忙碌,既能实现并发,又能节省资源

 

四、异步IO模型

不需要做任何操作,直接发指令即可

过程:程序发起信息,操作系统立即给反应,操作系统一直在监听,当数据来了,会直接将数据放到内存中去,程序可以直接去内存读取

但是python暂时没有对应的模块去使用异步IO

等待数据阶段和拷贝数据阶段都不需要用户处理
所有的操作都由操作系统替你完成
copy数据阶段,只有异步IO是不需要阻塞的

原文地址:https://www.cnblogs.com/st-st/p/9714408.html