posts - 50,comments - 0,views - 22327

IO模型介绍:

主进程的阻塞问题 ,多进程 多线程 只是分离了阻塞,而没有避免IO,因此有以下的模型:

1.IO阻塞模型(blocking IO);

2.非IO阻塞模型(nonblocking IO);

3.IO多路复用(IO multiplexing);

4.异步IO(asynchronous IO);用Python实现不了,是操作系统帮你的。

5.信号驱动IO(signal driven IO  )不常用;

IO阻塞模型的流程图:

即就是平时我们所写的socket,accpet,recv,都需要等待,阻塞。

 

2非IO阻塞模型的流程图:

就是不断的去循环 询问操作系统是否有顺序;

优点:CUP的利用率提高了;缺点:增加了CUP的负担;不推荐使用;

基于非IO阻塞的socket

server端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 非阻塞IO 实际上就是在得到连接后就 循环的向操作系统询问 是否有数据,没有就可以做其他的事情(在这里实际上就是接收到其他的连接了,后
# 循环的向操作系统询问 是否有数据)直达操作系统copy一份数据给进程。如果对面在传就在这样的接收,这样的过程就避免了IO阻塞等过程的
# 时间,
 
 
import socket
 
sk = socket.socket()
sk.bind(('127.0.0.1',8080))
 
sk.setblocking(False)     #IO阻塞变为不阻塞了。
sk.listen()
 
conn_lsit = []
 
while True:
    try:
        conn,addr = sk.accept()   #有连接就接收到,
        conn_lsit.append(conn)      #为了防止如果有很多的连接的话,只能拿到最后一个,因为其他的被覆盖掉了。
    except BlockingIOError:      #没有连接来的时候报错
        del_list=[]
        for conn in conn_lsit:
            try:
                ret = conn.recv(1024).decode('utf-8')     #尝试着接收数据,没有就继续向下执行
                #当客户端连接关闭后,服务端就会接收空字符
                if not ret:           #证明此连接传输完成,需要关闭此连接的服务端。
                    conn.close()
                    del_list.append(conn)            #这个连接已经接收到了数据。
                else:
                    print(ret)    #正常的传输
                    msg = input('>>>')
                    conn.send(msg.encode('utf-8'))      #发送数据是自己发送的不会调用操作系统,因此不会阻塞。
            except BlockingIOError:   #没有数据的时候报错
                pass
 
        if del_list:
            for conn in del_list:
                conn_lsit.remove(conn)        #为了在以后的循环中不再拿到已经关闭的连接
 
 
    # conn.send(ret.upper())
    #
    # conn.close()
    # sk.close()

 

client端:

1
2
3
4
5
6
7
8
9
10
11
12
13
import threading
import socket
 
def func():
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8080))
    sk.send(b'hello world')
    ret = sk.recv(1024)
    print(ret)
    sk.close()
 
for i in range(10):
    threading.Thread(target=func).start()  #开启10个线程。

   

 

3.IO多路复用的流程图

用select模块来监听recv和accept,因此阻塞就变成了select;有数据的时候就通知;对于单个的对象所用的时间比IO阻塞要多,但是多个对象的时候效率就高了;

优点:减少了CUP的负担,同时也能接收到数据,增加了CUP的利用率,一般多用这个模型;

 

 server端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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],
    # 当sk连接和conn连接同时有数据发来时 rl一个列表里面同时有两者
    # rl = [sk,coon]
    # 有数据的时候就会响应相应的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'))

  

client端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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()

  

 4.异步IO的流程图:

操作系统帮你做数据准备阶段和数据copy阶段;用户在这期间可以做别的,操作系统拿到数据后就直接给你了。

 

 5中IO模型的比较图:

 

 

 从图中可以看出IO阻塞;非IO阻塞;以及IO多路复用 都避免不了数据的copy时间,而异步IO可以。

 readlst [sk,conn,conn2,conn3] 100 问一百次

select ;poll 随着要检测的数据增加 效率会下降

select 有数目的限制;

poll 能处理的对象更多; 

epoll 能处理多对象 不是使用轮换的询问; 而是拿到数据后就直接调用回调函数 。    

epoll —— 但是只能在 linux执行,而epoll在windows下就不支持,好在我们有selectors模块,帮我们默认选择当前平台下最合适的;

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#服务端
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)

 

  

 

posted on   小辉python  阅读(158)  评论(0编辑  收藏  举报
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示