网络IO
一 网络IO
recvfrom:
wait data:等待客户端产生数据——》客户端OS--》网络--》服务端操作系统缓存
copy data:由本地操作系统缓存中的数据拷贝到应用程序的内存中(速度很快)
send:
copy data
网路IO的两个阶段(copy data阶段 + wait data阶段),换言之,所有IO都围绕这两个阶段
讲IO模型的目的:自己实现gevent模块,解决单线程下的IO问题(网络IO,不含time.sleep)
从而得到高性能。(之前讲的多进程和多线程并没有解决IO)
1、阻塞IO模型
wait data和copy data阶段一个都不能少,完完整整的等下来即为阻塞IO模型
之前所接触的多进程、多线程、进程池、线程池(除了gevent模块以外)都是阻塞IO模型。
2、非阻塞IO模型(更好的利用wait data阶段)
非阻塞IO只能监测网络IO,不监测time.sleep()这种IO
非阻塞IO有可能大规模占用CPU做无用操作,所以不推荐使用非阻塞IO
服务端:
from socket import *
import time
server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
server.setblocking(False)
#不设置默认是True;将其设置成False,即将所有阻塞编程非阻塞(遇到等不到数据的情况,不阻塞,会抛出信息:BlockingIOError)
#gevent模块中 monkey.patch_all() 即 s.setblocking(False)
conn_l=[]
while True:
try:
print('总连接数[%s]' % len(conn_l))
conn,addr=server.accept()
conn_l.append(conn)
except BlockingIOError:
del_l=[]
for conn in conn_l:
try:
data=conn.recv(1024)
if len(data) == 0:
del_l.append(conn)
continue
conn.send(data.upper())
except BlockingIOError:
pass
except ConnectionResetError:
del_l.append(conn)
for conn in del_l:
conn_l.remove(conn)
客户端:
from socket import *
import os
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg='%s say hello' %os.getpid()
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
3、多路复用IO模型
IO多路复用可同时监测所有套接字,循环询问操作系统是否已准备好数据。
当某个socket有数据到达了,就通知用户进程
当只监测一个套接字时,多路复用比阻塞IO的效率还要低(之间多了通知应用程序,应用程序在向操作系统执行copy_data)
一般会使用select模块帮忙完成IO多路复用模型
客户端:
from socket import *
import time
import select
server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
server.setblocking(False)
data_dic={}
read_list=[server,]
write_list=[]
print('start....')
while True:
rl,wl,xl=select.select(read_list,write_list,[]) #read_list=[server,conn1,conn2,conn3,conn4]存放等到数据的对象(套接字)
# print('read_list:%s rl:%s wl:%s ' %(len(read_list),len(rl),len(wl))) #rl=[conn1,conn2]
for sk in rl: #rl为已经有等到信息的对象,可能为server,亦可为conn;当为s时,执行accept,当为conn时,执行recv
if sk == server:
conn,addr=sk.accept()
read_list.append(conn) #建立好连接后,将连接丢入read_list中监测
else:
# sk.recv(1024)
# print(sk)
data=sk.recv(1024)
write_list.append(sk)
data_dic[sk]=data
for sk in wl:
sk.send(data_dic[sk].upper())
data_dic.pop(sk)
write_list.remove(sk)
客户端:
from socket import *
import os
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg='%s say hello' %os.getpid()
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
4、异步IO模型
异步IO模型的效率最高
应用程序发起read操作之后,立刻就可以开始去做其它的事,
kernel会等待数据准备完成,然后将数据拷贝到用户内存,
当这一切都完成之后,kernel会给应用程序发送一个signal,告诉它read操作完成了。