今天学习了网络传输中的两个阶段、阻塞IO、非阻塞IO和多路复用
一、网络传输中的两个阶段
分别是 waitdata 和 copydata
send就是copydata
recv是waitdata和copydata
二、阻塞 IO
无论是线程 进程 还是线程 进程池 统统都是阻塞IO
三、非阻塞IO
最直接体现 所有和读写相关的函数 都不会阻塞
意味着 在读写时 并不能确定目前是否可以读写 一旦不能读写就抛出异常
只能使用 try except 来判断是否可以读写
必须不断的执行系统调用 CPU占用特别高 当没有任何数据要处理的时候简直就是病毒
#非阻塞IO模型 客户端 import socket c = socket.socket() c.connect(('127.0.0.1',9999)) while True: msg = input('>>>:') if not msg:continue c.send(msg.encode('utf-8')) data = c.recv(1024) print(data.decode('utf-8'))
#非阻塞IO模型 服务器 import socket server = socket.socket() server.setsocket(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) server.bind(('127.0.0.1',9999)) server.listen(5) server.setblocking(False) def data_handler(conn): print('一个新连接..') while True: data = conn.recv(1024) conn.send(data.upper()) clients = [] send_datas = [] del_datas = [] closed_cs = [] while True: try: conn,addr = server.accept() clients.append(conn) except BlockingIOError: for c in clients: try: data = c.recv(1024) if not data: c.close() closed_cs.append(c) continue print('收到%s'%data.decode('utf-8')) send_datas.append((c,data)) except BlockingIOError: pass except ConnectionResetError: c.close() closed_cs.append(c) for data in send_datas: try: data[0].send(data[1].upper()) del_datas.append(data) except BlockingIOError: continue except ConnectionResetError: data[0].close() closed_cs.append(data[0]) del_datas.append(data) for d in del_datas: send_datas.remove(d) del_datas.clear() for c in closed_cs: clients.remove(c) closed_cs.clear()
四、多路复用
核心函数select
帮你检测所有的连接 找出可以被处理(可以读写)的连接
作为处理数据的一方 不再需要重复去向系统询问 select给你谁 你就处理谁 没给就不处理
#多路复用模型 客户端 import socket c = socket.socket() c.connect(('127.0.0.1',9999)) while True: msg = input('>>>:') if not msg:continue c.send(msg.encode('utf-8')) data = c.recv(1024) print(data.decode('utf-8'))
#多路复用模型 服务器 import socket import select #select帮你从一堆连接中找出来需要被处理的连接 server = socket.socket() #重用端口 server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) server.bind(('127.0.0.1',9999)) server.listen(5) #设置是否为阻塞 默认阻塞 server.setblocking(False) #需要检测是 是否可读取的列表 (recv就是一个读取操作) rlist = [server,] #需要检测的 是否写入的列表 (send就是写入操作) wlist = [] #需要发送的数据 目前是因为 我们要把接受的数据再发回去 所以搞了这个东西 正常没有这种需求 #目前客户端与服务器端 交互 是必须客户端发送数据 服务端才能返回数据 正常没有这种需求 dic = {} while True: #用于检测需要处理的连接 需要不断检测 所以循环 #rl目前可读的客户端列表 wl目前可写的客户端列表 rl,wl,xl = select.select(rlist,wlist,[]) #select默认阻塞 阻塞到任意一个连接可以被处理 print(len(rl)) #处理可读的socket for c in rl: #无论是客户端还是服务端只要可读就会执行到这里 if c == server: #接受客户端的连接请求(一个读操作) conn,addr = c.accept() #将新连接也交给select来检测 rlist.append(conn) else: #不是服务器 就是客户端 客户端可读 可以执行recv try: data = c.recv(1024) if not data: c.close() rlist.remove(c) print('%s 发送 %s'%(c,data.decode('utf-8'))) #给客户端发送数据前要保证目前可以发送 将客户端加入检测列表 wlist.append(c) #正常开发中 不可能必须客户端发送数据过来后才能给客户端发送 #可以这个添加到检测列表的操作 应该建立连接后立即执行 #要发送的数据 dic[c] = data except ConnectionResetError: #客户端关闭连接 c.close() rlist.remove(c) #处理可写的socket for c in wl: print(c) try: c.send(dic[c].upper()) #删除数据 dic.pop(c) #从检测列表中删除已发送完成的客户端 wlist.remove(c) except ConnectionResetError: c.close() #关闭连接 dic.pop(c) #删除要发送的数据 wlist.remove(c) #从待检测的列表中删除 except BlockingIOError: #可能缓存满了 发不了 pass