I/O模型
同步、异步指提交任务方式,异步不等于阻塞,异步一般与回调联合使用
分类:
- 阻塞IO
- 非阻塞IO
- IO多路复用
- 信号驱动IO(使用较少)
- 异步IO
主要研究网络IO:
什么功能属于IO行为?收发消息都属于网络IO行为
阻塞IO
server
from socket import * server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn,addr=server.accept() print(addr) while True: try: data=conn.recv(1024) if not data:break conn.sendall(data.upper()) except ConnectionResetError: break conn.close() server.close()
client
from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: msg=input(">>").strip() if not msg:break client.send(msg.encode("utf-8")) recv=client.recv(1024) print(recv.decode("utf-8"))
非阻塞IO模型,解决IO阻塞问题,充分利用wait data时间
server
from socket import * server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',8080)) server.listen(5) server.setblocking(False)#default is True print("starting....") rlist=[] while True: try: conn,addr=server.accept() conn.setblocking(False) rlist.append(conn) except BlockingIOError: del_list=[] for conn in rlist: try: data=conn.recv(1024) if not data: del_list.append(conn) continue conn.send(data.upper()) except BlockingIOError: continue except Exception as e: print(e) conn.close() del_list.append(conn) for dlist in del_list: rlist.remove(dlist)
client
from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: msg=input(">>").strip() if not msg:break client.send(msg.encode("utf-8")) recv=client.recv(1024) print(recv.decode("utf-8"))
多路复用IO模型,可以同时检测多个套接字IO
优点:
可以检测多个套接字,比阻塞IO快
缺点:
代理套接字太多后,导致很多询问无用,效率更低。linux提供pool功能。eppol可以很好解决,但是跨平台性差,windows不支持epoll
selectors可以更加操作系统选择最优非阻塞IO
server
from socket import * import select server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',8080)) server.listen(5) server.setblocking(False)#default is True rlist=[server]#这里为什么放server?应为也需要检测server是否有accept请求过来呀 wlist=[] xliet=[] dlist={}#保存接收到的数据 while True: rl,wl,xl=select.select(rlist,wlist,xliet,0.5)#timeout参数表示隔多久询问一次操作系统 for sock in rl:#判断是否是rl产生数据,可能是新连接进来 if sock==server:#有连接请求进来,需建立连接 conn,addr=server.accept() print(addr) rlist.append(conn)#连接建立好后,放入rlist中也检测起来,即检测conn是否有数据过来 else:#不是server肯定就是conn了,conn只有接收数据。因为rlist中只有server和conn,没其他对象 try: data=sock.recv(1024)#这里不应该直接发,也经过select来处理,所以,如下 if not data:#linux平台不会抛出异常,一直接收空字符串 sock.close()#关闭 rlist.remove(sock)#移除监控 wlist.append(sock)#放进去后就开始检测,只要缓冲区没有满,就可以发送数据 dlist[sock]=data#存放该conn接收到的数据,用于后面处理 # except ConnectionResetError:#windows下,客户端断开会抛ConnectionResetError错误、 except Exception:##检测所有异常 sock.close()#产生异常的连接关闭掉 rlist.remove(sock)#异常连接不再监控 for sock in wl:#也可能是wl列表产生变化,所以进程迭代,wl列表中的conn只有发送数据一件事可干 data=dlist[sock]#取出之前连接接收到的数据 sock.send(data.upper())#对数据进行处理后发送 wlist.remove(sock)#数据处理完后就不需要监控 该连接了,所以从监控列表中删除 dlist.pop(sock)#也需从存储数据的字典中将该连接的数据删除掉 server.close()
client
from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: msg=input(">>").strip() if not msg:break client.send(msg.encode("utf-8")) recv=client.recv(1024) print(recv.decode("utf-8"))
异步IO(Asynchronous I/O)
后面爬虫项目介绍