标签:server 系统 添加 报错 output NPU 监听 异步io 直接
第一种IO模型
阻塞
第二种IO模型
非阻塞IO
原理:由阻塞改为非阻塞,每隔一段时间回来看看(每每看一次,内核态会发送一次系统调用),若没有干其他事情(适用于代码量小),进程主动轮询
当服务器端套接字被setblocking(false),套接字为非阻塞套接字,当接收不到客户端链接时,直接触发异常(证明现阶段CPU空闲,可以做其他事情)。所以可以在异常处理处理其他任务。
缺点
发送太多次系统调用
数据得不到及时处理
第三种IO模型
IO多路复用(单线程下实现并发,原理就是利用IO空闲时间实现并发)(select ,poll,epoll)
select
select函数是系统调用接口。并且select也是会阻塞,
但其好处是同时可监控多个链接,这是决定了为何如此流行.
讲到这里还是莫名其妙,但是可以把socket.accpet()返回的conn添加至监听列表里,就明白了。因为监听那么多,总有一个是内核态有数据的,也就是说总会可以操作的
注意的是:默认,若内核态的数据没有取至用户态,则返回的可读列表里会一直存在该套接字。还有监听数不能超过1024
# select 模拟一个socket server,注意socket必须在非阻塞情况下才能实现IO多路复用。 # 接下来通过例子了解select 是如何通过单进程实现同时处理多个非阻塞的socket连接的。 #server端 import select import socket import queue server = socket.socket() server.bind((‘localhost‘,9000)) server.listen(1000) server.setblocking(False) # 设置成非阻塞模式,accept和recv都非阻塞 # 这里如果直接 server.accept() ,如果没有连接会报错,所以有数据才调他们 # BlockIOError:[WinError 10035] 无法立即完成一个非阻塞性套接字操作。 msg_dic = {} inputs = [server,] # 交给内核、select检测的列表。 # 必须有一个值,让select检测,否则报错提供无效参数。 # 没有其他连接之前,自己就是个socket,自己就是个连接,检测自己。活动了说明有链接 outputs = [] # 你往里面放什么,下一次就出来了 while True: readable, writeable, exceptional = select.select(inputs, outputs, inputs) # 定义检测 #新来连接 检测列表 异常(断开) # 异常的也是inputs是: 检测那些连接的存在异常 print(readable,writeable,exceptional) for r in readable: if r is server: # 有数据,代表来了一个新连接 conn, addr = server.accept() print("来了个新连接",addr) inputs.append(conn) # 把连接加到检测列表里,如果这个连接活动了,就说明数据来了 # inputs = [server.conn] # 【conn】只返回活动的连接,但怎么确定是谁活动了 # 如果server活动,则来了新连接,conn活动则来数据 msg_dic[conn] = queue.Queue() # 初始化一个队列,后面存要返回给这个客户端的数据 else: try : data = r.recv(1024) # 注意这里是r,而不是conn,多个连接的情况 print("收到数据",data) # r.send(data) # 不能直接发,如果客户端不收,数据就没了 msg_dic[r].put(data) # 往里面放数据 outputs.append(r) # 放入返回的连接队列里 except ConnectionResetError as e: print("客户端断开了",r) if r in outputs: outputs.remove(r) #清理已断开的连接 inputs.remove(r) #清理已断开的连接 del msg_dic[r] ##清理已断开的连接 for w in writeable: # 要返回给客户端的连接列表 data_to_client = msg_dic[w].get() # 在字典里取数据 w.send(data_to_client) # 返回给客户端 outputs.remove(w) # 删除这个数据,确保下次循环的时候不返回这个已经处理完的连接了。 for e in exceptional: # 如果连接断开,删除连接相关数据 if e in outputs: outputs.remove(e) inputs.remove(e) del msg_dic[e] #*************************client import socket client = socket.socket() client.connect((‘localhost‘, 9000)) while True: cmd = input(‘>>> ‘).strip() if len(cmd) == 0 : continue client.send(cmd.encode(‘utf-8‘)) data = client.recv(1024) print(data.decode()) client.close()
poll
相比select,就是一点,就是把最大链接数提高了
epoll
epoll实现原理跟select一样(原理图一样),但是实现机制不同(也就是,数据来了,是怎么找到是指定套接字的)
select内部原理是采取轮询,就好比,在一间教室里老师听到有一学生拍了下桌子,但不知道具体谁拍的,
只有一个一个问。所以很多时间都消耗在轮询问上面了(是每一次有数据到来时候,都要轮询)
epoll内部原理是自报家门,就好比,学生不拍桌子,直接站起来我是谁,我要反了
Ngexi服务器底层就是epoll,多进程多线程开得不多,但是一个线程里面通过epoll实现多连接
异步IO
异步是整个过程中一点阻塞都没有。你发完请求你就去做其他事情,等到内核把数据收到且复制到用户态,再发给你一个信号。
所以说以上IO多路复用,不是异步的。
同步IO
在IO操作完成之前存在阻塞。而异步IO是毫无阻塞。所以多路复用都属于同步IO
标签:server 系统 添加 报错 output NPU 监听 异步io 直接
原文地址:https://www.cnblogs.com/ziyide/p/9103062.html