IO 模型分类linux
* blocking IO 阻塞IO
* nonblocking IO 非阻塞IO
* IO multiplexing IO多路复用
* signal driven IO 信号驱动IO
* asynchronous IO 异步IOwindows
1,阻塞IO 的图网络
recv数据多线程
阻塞IO并发
非阻塞IOapp
IO多路复用异步
2,socket
阻塞IO : 工做效率低
非阻塞IO : 工做效率高,频繁询问切换加大CPU的负担
IO多路复用: 在有多个对象须要IO阻塞的时候,可以有效的减小阻塞带来的时间损耗,且可以在必定程度上减小CPU的负担
异步IO : asyncio 异步IO 工做效率高 CPU的负担少async
3,网络IOide
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)
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)
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()
多路复用
# 操做系统提供的多路复用机制
# 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()
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'))
3,
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)