内容回顾:
第一阶段:
socket, 服务端同时只处理一个请求
第二阶段:
select + socket, 实现伪并发
a. r_list : 即读又写
b. r_list, w_list 读写分离
第三阶段:
socketserver
select / epoll + socket +多线程
并发操作
IO多路复用:通过一种机制,可以监视多个描述符(文件句柄,对象的标识符, 定位文件数据在内存中位置),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
Python中select模块,提供:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。
基于select实现多路复用功能:
server端: import socket sk1 = socket.socket() sk1.bind(("127.0.0.1",8001,)) sk1.listen() sk2 = socket.socket() sk2.bind(("127.0.0.1",8002,)) sk2.listen() sk3 = socket.socket() sk3.bind(("127.0.0.1",8003,)) sk3.listen() inputs = [sk1,sk2,sk3] import select while True: #sk1,sk2, select在内部自动监听多个对象 #如果有人连接 sk1 #r_list = [sk1,sk2],第一个inputs:如果对象发生变化就放到r_list里, #中间的参数传了什么:w_list就是什么,不会变动 #第三个inputs:如果谁发生错误就放到e_list里面 r_list,w_list,e_list = select.select(inputs,[sk1,sk2,],inputs,1)#1为等待时间 for sk in r_list: #每一个连接对象 conn,address = sk.accept() conn.sendall(bytes("hello",encoding="utf-8")) conn.close() for sk in e_list: inputs.remove(sk)#谁出错就把谁去掉 clint端1: import socket obj = socket.socket() obj.connect(("127.0.0.1",8001,)) connent = str(obj.recv(1024), encoding="utf-8") print(connent) obj.close() clint端2: import socket obj = socket.socket() obj.connect(("127.0.0.1",8002,)) connent = str(obj.recv(1024), encoding="utf-8") print(connent) obj.close()
完整的server端和client端示例(伪并发)
实现了一个server,其功能就是可以和多个client建立连接,每个client的发过来的数据加上一个response字符串返回给client端~~~
server端: import socket import select sk = socket.socket() sk.bind(('127.0.0.1', 9000),) sk.listen(5) inputs = [sk, ] outputs = [] message = {} # 实现读写分离 print("start...") while True: # 监听的inputs中的socket对象内部如果有变化,那么这个对象就会在rlist # outputs里有什么对象,wlist中就有什么对象 # []如果这里的对象内部出错,那会把这些对象加到elist中 # 1 是超时时间 rlist, wlist, elist = select.select(inputs, outputs, [], 1) print(len(inputs), len(outputs)) for r in rlist: if r == sk: conn, addr = sk.accept() conn.sendall(b"ok") # 这里记住是吧conn添加到inputs中去监听,千万别写成r了 inputs.append(conn) message[conn] = [] else: try: data = r.recv(1024) print(data) if not data: raise Exception('连接断开') message[r].append(data) outputs.append(r) except Exception as e: inputs.remove(r) del message[r] for r in wlist: data = str(message[r].pop(), encoding='utf-8') res = data + "response" r.sendall(bytes(res, encoding='utf-8')) outputs.remove(r) # 实现读写分离 # IO多路复用的本质是用select、poll、epoll(系统底层提供的)来监听socket对象内部是否有变化 # select 是在Win和Linux中都支持额,相当于系统内部维护了一个for循环,缺点是监听个数有上限(1024),效率不高 # poll的监听个数没有限制,但仍然用循环,效率不高。 # epoll的机制是socket对象变化,主动告诉epoll。而不是轮询,相当于有个回调函数,效率比前两者高 # Nginx就是用epoll。只要IO操作都支持,除开文件操作 # 列表删除指定元素用remove clint端: import socket sc = socket.socket() sc.connect(("127.0.0.1", 9000,)) data = sc.recv(1024) print(data) while True: msg = input(">>>:") if msg == 'q': break if len(msg) == 0: continue send_msg = bytes(msg, encoding="utf-8") sc.send(send_msg) res = sc.recv(1024) print(str(res, encoding="utf-8")) sc.close()
线程:
import threading import time def process(arges): time.sleep(1) print(arges) #轮循 # for i in range(10): # process(i) #创建10个线程,一次执行这10个 for i in range(10): t = threading.Thread(target=process, args=(i,))#创建线程,每创建一个线程让一个方法去执行它 t.start()
进程
协程
成产者消费模型