1、I/O多路复用指:通过一种机制,可监听多个描述符(soket对象)(文件句柄),一旦某个描述符发送编号(一般指读就绪或写就绪),能够通知程序进行相应的读写操作。
2、I/O多路复用方式:select、poll 、epool。
win 只支持select
select:使用for循环,描述符最多1024个
pool:使用for循环实现,对文件描述符没有限制,
epoll:优化了实现方式,不在使用for循环,使用了异步的方式,效率高
2.1 select方式工作原理:
后面的1表示延时等待时间
nginx在内部就是基于epoll来实现的,使用epool+select来监听用户请求
2.2多路复用+socket实现伪并发处理示例
#!/usr/bin/env python # -*- coding:utf-8 -*- 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, ] inputs = [sk1, ] outputs = [] message_dict = {} import select while True: #[sk1,sk2, ],内部自动监听sk1, sk2 两个对象, 一旦某个句柄发生变化,就能感知到 #如果有人连接 sk1 #r_list = [sk1, ] #如果有人连接sk1发生编号 r_list, w_list, e_list = select.select(inputs, outputs, inputs, 1) print("正在监听的socket对象总计:%d" % len(inputs)) print(r_list) for sk1_or_conn in r_list: #每一个连接对象 if sk1_or_conn == sk1: conn, address = sk1_or_conn.accept() inputs.append(conn) message_dict[conn] = [] else: #有老用户发消息了 try: data_bytes = sk1_or_conn.recv(1024) # data_str = str(data_bytes, encoding='utf-8') # sk1_or_conn.sendall(bytes(data_str+'好', encoding='utf-8')) except Exception as ex: #用户终止连接 print(ex) inputs.remove(sk1_or_conn) else: #用户正常发送消息 data_str = str(data_bytes, encoding='utf-8') message_dict[sk1_or_conn].append(data_str) # sk1_or_conn.sendall(bytes(data_str+'好', encoding='utf-8')) outputs.append(sk1_or_conn) # conn.sendall(bytes('hello',encoding='utf-8')) # conn.close() #w_list 仅仅保存了谁给我发过消息 for conn in w_list: recv_str = message_dict[conn][0] del message_dict[conn][0] conn.sendall(bytes(recv_str+'好', encoding='utf-8')) outputs.remove(conn) for sk1_or_conn in e_list: inputs.remove(sk1_or_conn) # while True: # conn, address = sk.accept() # while True: # content_bytes = conn.recv(1024) # content_str = str(content_bytes, encoding='utf-8') # conn.sendall(bytes(content_str + '好', encoding='utf-8')) # conn.close()
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket obj = socket.socket() obj.connect(('127.0.0.1',8001 )) while True: inp = input('>>>>>') obj.sendall(bytes(inp,encoding='utf-8')) ret = str(obj.recv(1024),encoding='utf-8') print(ret) # # content = str(obj.recv(1024),encoding='utf-8') # print(content) obj.close() # #阻塞 # print('等待中') # ret_bytes = obj.recv(1024) #设置最高多接受的字节数,超过后需要等下次在接受 # print(str(ret_bytes,encoding='utf-8')) # print('接受完成') # # obj.close() # while True: # inp = input('请输入要发送的内容(q退出) >>>') # if inp == 'q': # obj.sendall(bytes(inp,encoding='utf-8')) # break # else: # obj.sendall(bytes(inp,encoding='utf-8')) # ret = str(obj.recv(1024),encoding='utf-8') # print(ret) # obj.close()
#!/usr/bin/env python # -*- coding:utf-8 -*- 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, ],内部自动监听sk1, sk2 两个对象, 一旦某个句柄发生变化,就能感知到 #如果有人连接 sk1 #r_list = [sk1] r_list, w_list, e_list = select.select(inputs, [], [], 1) for sk in r_list: #每一个连接对象 conn, address = sk.accept() conn.sendall(bytes('hello',encoding='utf-8')) conn.close() # while True: # conn, address = sk.accept() # while True: # content_bytes = conn.recv(1024) # content_str = str(content_bytes, encoding='utf-8') # conn.sendall(bytes(content_str + '好', encoding='utf-8')) # conn.close()
2.3关于并发
3、初始超线程
#!/usr/bin/envpython #-*-coding:utf-8-*- importthreading importtime defprocess(arg): time.sleep(1) print(arg) #foriinrange(10): #process(i) foriinrange(10): t=threading.Thread(target=process,args=(i,)) t.start()
4、Soketserver原码解析
原理图
5、初始多线程
#!/usr/bin/env python # -*- coding:utf-8 -*- import threading import time def process(arg): time.sleep(1) print(arg) # for i in range(10): # process(i) for i in range(10): t = threading.Thread(target=process, args=(i,)) t.start()