Python--day39(IO阻塞模型、非阻塞IO、多路复用)
作者:互联网
1. IO阻塞模型
当执行到recv时,如果对象并没有发送数据,程序阻塞了,无法执行其他任务
解决方案:
1.1 多线程或多进程
当客户端并发量非常大的时候,服务器可能就无法开启新的线程或进程,如果不对数量加以限制 服务器就崩溃了
1.2 线程池或进程池
首先限制了数量 保证服务器正常运行,但是问题是,如果客户端都处于阻塞状态,这些线程也阻塞了
1.3 协程
使用一个线程处理所有客户端,当一个客户端处于阻塞状态时可以切换至其他客户端任务
2. 非阻塞IO模型
阻塞IO模型在执行recv 和 accept 时 都需要经历wait_data
非阻塞IO即 在执行recv 和accept时 不会阻塞 可以继续往下执行
如何使用:
将server的blocking设置为False 即设置非阻塞
存在的问题 :
这样一来 你的进程 效率 非常高 没有任何的阻塞
很多情况下 并没有数据需要处理,但是我们的进程也需要不停的询问操作系统 会导致CPU占用过高
而且是无意义的占用
服务器
import socket import time server = socket.socket() server.bind(("127.0.0.1", 1688)) server.listen() server.setblocking(False) # 默认为阻塞 设置为False 表示非阻塞 # 用来存储客户端的列表 clients = [] # 用来存储需要发送的数据和客户端对象 msgs = [] # 链接客户端的循环 while True: try: client, address = server.accept() # 接受三次握手信息 print("来了一个客户端了...%s " % address[1]) # 有人链接成功了 clients.append(client) except BlockingIOError as e: print("还没有人连过来") time.sleep(0.5) # 收数据的操作 for c in clients[:]: try: # 开始通讯任务 data = c.recv(1024) if not data: c.close() clients.remove(c) msgs.append((c, data)) # c.send(data.upper()) # 如果碰巧缓存满了,这个数据就可能丢失了 # 由于此处捕获了异常,所以应该单独来发送数据 except BlockingIOError: print("这个客户端暂时还没有数据需要处理") except ConnectionResetError: # 断开后删除客户端 c.close() clients.remove(c) # 发送数据的的操作 for i in msgs[:]: try: c, msg = i c.send(msg.upper()) msgs.remove(i) # 如果发送成功,需要删除这个数据 except BlockingIOError: passView Code
3. 多路复用
服务器
import socket import select server = socket.socket() server.bind(("127.0.0.1", 1688)) server.listen() rlist = [server,] # 将需要检测(是否可读==recv)的socket对象放到该列表中 # accept也是一个读数据操作,默认也会阻塞,也需要让select来检测 wlist = [] # 将需要检测(是否可写==send)的socket对象放到该列表中 # 只要缓冲区不满都可以写 msgs = [("socket", "msg")] while True: readable_list, writeable_list, _ = select.select(rlist, wlist, []) # 阻塞直到socket可读或者可写 """ 参数1 rlist 里面存储需要被检测是否可读(是否可以执行recv)的socket对象 参数2 wlist 里面存储需要被检测是否可写(是否可以执行send)的socket对象 参数3 xlist 存储需要关注异常条件 (忽略) 参数4 timeout 检测超时时间,一段时间后还是没有可以被处理的socket对象,那就返回空列表 返回值: 三个列表 1. 已经有数据到达的socket对象 2. 可以发送数据的socket对象 怎么可以发?缓冲区,没有满 3. 忽略 """ print("%s个socket可读" % len(readable_list), "%s个socket可写" % writeable_list) # readable_list 中存储的是已经可以读取数据的socket对象,可能是服务器,也可能是客户端 for soc in readable_list: if soc == server: # 服务器的处理 client, address = server.accept() # 将新连接的socket对象 加入到待检测列表中 rlist.append(client) else: try: # 客户端处理 data = soc.recv(1024) if not data: # 如果对方下线了,关闭socket,并且从待检测列表中删除 soc.close() rlist.remove(soc) continue # 不能直接发 因为此时缓冲区可能已经满了,导致send阻塞住,所以在发送数据前一定要先将socket交给select来检查 # soc.send(data.upper()) if soc not in wlist: wlist.append(soc) # 将要发送的数据先存储起来 msgs.append((soc,data)) except ConnectionResetError: soc.close() # 对方下线后 应该从待检测列表中杀出socket rlist.remove(soc) wlist.remove(soc) # 处理可写列表 for soc in writeable_list: # 由于一个客户端可能有多个数据要发送,所以遍历所有客户端 for i in msgs[:]: if i[0] == soc: soc.send(i[1]) # 发送成功 将这个数据从列表中删除 msgs.remove(i) # 数据已经都发送给客户端 这个socket不必继续监测是否可写,必须要删除 # 否则 只要缓冲区不满,就会一直处于可写,导致死循环 wlist.remove(soc)View Code
标签:soc,socket,day39,阻塞,server,IO,data,客户端 来源: https://www.cnblogs.com/wangyong123/p/10999993.html