1、IO介绍
对于一个network IO (这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。当一个read操作发生时,该操作会经历两个阶段:
#1)等待数据准备 (Waiting for the data to be ready) #2)将数据从内核拷贝到进程中(Copying the data from the kernel to the process)
服务端:
from socket import *
s = socket()
s.bind(('127.0.0.1',8080))
s.listen(5)
while True:
conn, addr = s.accept()
print(addr)
while True:
try:
data = conn.recv(1024)
if not data: break
print('from client msg: ',data)
except ConnectionResetError:
break
conn.close()
#data = conn.recv这里会有一个明显的阻塞效果
#recv本质是从网卡收消息,但应用程序不能操作硬件,它会给操作系统发请求,找操作系统拿数据,操作系统缓存里有没有数据
#取决于网卡,也就是客户端有没有向网卡发数据,等到操作系统缓存里有数据时,会把数据copy给应用程序的缓存
#所以recv经历了两个阶段,wait data 与 copy data
#accept和recv操作一样
客户端:
from socket import *
client = socket()
client.connect(('127.0.0.1', 8080))
while True:
data = input('>>: ').strip()
if not data:continue
client.send(data.encode('utf-8'))
print('has send')
#client.send里面的数据此时储存在应用程序中,需要调网卡把数据发送出去,但应用程序不能操作网卡,所以应用程序
#先把数据copy给自己的操作系统(此时send操作已经完成了),操作系统调网卡把数据发到对方去。
#这里我们不会感觉send操作会阻塞程序,因为send只要把数据copy给操作系统就完成任务了,如果此时的数据又很小,就不会
#感觉到阻塞,但send本身是一个IO行为,如果copy的数据量大到占满操作系统的缓冲时,这时候就不能向操作系统里写数据了,
# 这就会感觉到阻塞
#所以send 经历了一个copy data
非阻塞IO:

#服务端
from socket import *
import time
s = socket()
s.bind(('127.0.0.1',8080))
s.listen(5)
s.setblocking(False) #非阻塞,默认情况下是阻塞的(True)
r_list=[]
w_list=[]
while True:
try:
conn, addr = s.accept() #捕捉连接,如果没连接来,干其它活
r_list.append(conn) #有连接来的话把连接存下来,再进入下来次循环检测连接
except BlockingIOError:
# time.sleep(0.05)
print('可以去干其他的活了')
print('rlist: ',len(r_list))
# 收消息
del_rlist=[]
for conn in r_list:
try:
data=conn.recv(1024)
if not data: #如果是linux系统客户端单方面断连接recv会收空
conn.close() #关闭连接
del_rlist.append(conn) #把关闭掉的连接放到准备删除的列表里
continue #直接运行下一行代码
# conn.send(data.upper())
w_list.append((conn,data.upper()))
except BlockingIOError: #当检测到recv没有收到数据时进行下一次循环
continue
except ConnectionResetError: #捕捉客户端单方面终止连接(window系统)
conn.close() #回收连接
# r_list.remove(conn)
del_rlist.append(conn) #把终止掉的连接放到准备删除的列表里
# 发消息
del_wlist=[]
for item in w_list:
try:
conn=item[0] #拿连接
res=item[1] #拿数据
conn.send(res) #发送数据
del_wlist.append(item) #发送成功后就不需要了
except BlockingIOError: #检测是否存在发送阻塞
continue
except ConnectionResetError: #检测发消息时客户端有没有单方面断连接
conn.close() #关掉连接
del_wlist.append(item) #加入要删除的列表里,等待被删除
# 回收无用套接字,无需再监听它们的IO操作
for conn in del_rlist:
r_list.remove(conn) #收
for item in del_wlist: #发
w_list.remove(item)
#客户端
from socket import *
import os
client = socket()
client.connect(('127.0.0.1', 8080))
while True:
data='%s say hello' %os.getpid()
client.send(data.encode('utf-8'))
res=client.recv(1024)
print(res.decode('utf-8'))
但是非阻塞IO模型不被推荐,虽然它能够在等待任务完成的时间里干其他活了,但是它循环调用recv()将大幅度推高CPU占用率
多路复用IO(IO multiplexing):
它的好处就在于单个process就可以同时处理多个网络连接的IO,基本原理就是不断的轮询所负责的所有socket,当某个socket
有数据到达了,就通知用户进程。
异步IO:

from concurrent.futures import ThreadPoolExecutor
from threading import current_thread
import time
import os
def task(n):
print('%s is running' %current_thread().name)
time.sleep(2) #模拟IO
return n**2
def parse(obj):
res=obj.result() #拿到task的返回值的结果
print(res)
if __name__ == '__main__':
t=ThreadPoolExecutor(4)
future1=t.submit(task,1)
future1.add_done_callback(parse) #parse函数会在future1对应的任务执行完毕后自动执行,会把future1自动传给parse
future2=t.submit(task,2)
future2.add_done_callback(parse)
future3=t.submit(task,3)
future3.add_done_callback(parse)
future4=t.submit(task,4)
future4.add_done_callback(parse)
五种IO模型:https://www.cnblogs.com/linhaifeng/articles/7454717.html
