IO模型

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端:

# 非阻塞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端:

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端:

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端:

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模块,帮我们默认选择当前平台下最合适的;

#服务端
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)

  

原文地址:https://www.cnblogs.com/zenghui-python/p/10544246.html