IO多路複用select模塊加socket模塊,僞多線併發,並非真正的多線程併發,實際經過循環等待仍是一個一個處理的python
IO多路複用,lo就是文件或數據的輸入輸出,IO多路複用就是能夠多用戶操做多線程
IO多路複用,能夠監聽多個文件描述符(socke對象)(文件句柄),一旦文件句柄出現變化,便可感知到,感知到後做出相應操做併發
好比原生socke模塊只能監聽一個端口和只能一個用戶鏈接,要想實現監聽多個端口和支持多用戶,就會使用IO多路複用app
IO多路複用select模塊異步
select()自動監聽socket對象的客戶端,鏈接地址和、客戶端通信地址,四個參數socket
select()自動監聽列表裏的socket對象(文件描述符),一旦監聽的對象,誰發生了變化(有用戶請求),
就將發生變化對象的客戶端鏈接地址賦值給第一個變量a,第一個變量接收的列表,列表裏的每個元素,就是一個端口的用戶鏈接地址
若是是多個端口在同時鏈接客戶端,那麼列表元素就是多個元素spa
最多監聽端口1024個線程
使用方法:a, b, c = select.select(列表類型的socke對象變化, [可選:傳參直接賦值給第二個變量], [可選:列表類型的socke對象監聽錯誤], 多少秒檢測一次)code
須要四個參數 對象
參數一:列表類型的socke對象,監聽變化
參數二:傳參直接賦值給第二個變量
參數三:列表類型的socke對象,監聽錯誤
參數四:多少秒檢測一次
須要三個變量來接收返回值
第一個變量接收的:列表類型客戶端鏈接地址,若是無客戶端鏈接則爲空列表
第二個變量接收的:第二個參數的值
第三個變量接收的:第三個參數監聽socke對象的錯誤信息,返回列表,無錯誤則爲空列表
格式:a, b, c = select.select(lib, [], [], 1)
格式2:a, b, c = select.select(lib, k1, lib, 1)
IO多路複用多端口監聽客戶端鏈接地址、服務端代碼
#!/usr/bin/env python # -*- coding:utf8 -*- """服務端""" import socket #導入模塊 """設置多個對象,綁定不一樣端口""" k1 = socket.socket() #建立對象 k1.bind(('127.0.0.1', 9991)) #綁定服務端IP和端口 k1.listen() #監聽IP和端口 k2 = socket.socket() #建立對象 k2.bind(('127.0.0.1', 9992)) #綁定服務端IP和端口 k2.listen() #監聽IP和端口 k3 = socket.socket() #建立對象 k3.bind(('127.0.0.1', 9993)) #綁定服務端IP和端口 k3.listen() #監聽IP和端口 """將多個對象組合成列表""" lib = [k1, k2, k3] """使用自動監聽模塊""" import select #導入自動監聽多個端口模塊 while True: #select()自動監聽列表裏的socket對象(文件描述符),一旦監聽的對象,誰發生了變化(有用戶請求), # 就將發生變化對象的客戶端鏈接地址賦值給第一個變量a,第一個變量接收的列表,列表裏的每個元素,就是一個端口的用戶鏈接地址 #若是是多個端口在同時鏈接客戶端,那麼列表元素就是多個元素 a, b, c = select.select(lib, [], [], 1) for k in a: #循環出select()獲取到的列表類型客戶端鏈接地址 d, e = k.accept() #將循環到的每個客戶端鏈接地址等待通信,阻塞 d.sendall(bytes("歡迎訪問", encoding="utf-8")) #向鏈接的客戶端發送一條消息
客戶端代碼1
#!/usr/bin/env python # -*- coding:utf8 -*- """建立客戶端""" import socket #導入模塊 z = socket.socket() #建立socket對象 b = z.connect(('127.0.0.1', 9991,))#鏈接服務端,在客戶端綁定服務端IP和端口 f = z.recv(1024) #客戶端接收服務端sendall()發來的信息,1024表示最大接收1024字節 f2 = str(f, encoding='utf-8') #將接收到的服務端字節信息轉換成字符串 print(f2) #打印出服務端發來的信息
客戶端代碼2
#!/usr/bin/env python # -*- coding:utf8 -*- """建立客戶端""" import socket #導入模塊 z = socket.socket() #建立socket對象 b = z.connect(('127.0.0.1', 9992,))#鏈接服務端,在客戶端綁定服務端IP和端口 f = z.recv(1024) #客戶端接收服務端sendall()發來的信息,1024表示最大接收1024字節 f2 = str(f, encoding='utf-8') #將接收到的服務端字節信息轉換成字符串 print(f2) #打印出服務端發來的信息
IO多路複用多端口監聽、客戶端鏈接地址原理圖
IO多路複用監聽客戶端多通信地址
經過select()監聽到客戶端變化信息對象,拿到變化信息對象,判斷是鏈接信息對象,仍是通信信息對象?若是是鏈接信息對象,經過變化信息對象獲取到此鏈接的通信對象,將通信對象添加到select()裏監聽,若是是通信對象,經過通信對象進行信息交互
IO多路複用監聽客戶端多通信地址服務端代碼
#!/usr/bin/env python # -*- coding:utf8 -*- """服務端""" import socket #導入模塊 k1 = socket.socket() #建立對象 k1.bind(('127.0.0.1', 9991)) #綁定服務端IP和端口 k1.listen() #監聽IP和端口 lib = [k1] """使用自動監聽模塊""" import select #導入自動監聽多個端口模塊 while True: #select()自動監聽列表裏的socket對象(文件描述符),一旦監聽的對象,誰發生了變化(有用戶請求), # 就將發生變化對象的客戶端鏈接地址賦值給第一個變量a,第一個變量接收的列表,列表裏的每個元素,就是一個端口的用戶鏈接地址 #若是是多個端口在同時鏈接客戶端,那麼列表元素就是多個元素 a, b, c = select.select(lib, [], [], 1) print("正在監聽的socket對象 %d" % len(lib)) #監聽的對象有多少個 print(a) #監聽發生變化的有哪些 for k in a: #循環出select()獲取到的列表類型客戶端鏈接地址 if k == k1: #判斷循環到的鏈接地址等於socket對象,說明有新用戶鏈接 d, e = k.accept() #等待請求,獲取客戶端通信地址 lib.append(d) #將客戶端通信地址追加到列表,傳入select()監聽通信變化 d.sendall(bytes("你好", encoding="utf-8")) else: #若是不是鏈接信息,說明是通信信息 try: #監控代碼是否出現異常,若是客戶端關閉了就會出現異常,沒出現異常執行裏面的代碼 fd = k.recv(1024) f = str(fd, encoding="utf-8") g = f + "好" k.sendall(bytes(g, encoding="utf-8")) except Exception as ex: #出現異常將當前通信對象移除列表,select不在監聽 lib.remove(k)
客戶端代碼
#!/usr/bin/env python # -*- coding:utf8 -*- """建立客戶端""" import socket #導入模塊 z = socket.socket() #建立socket對象 b = z.connect(('127.0.0.1', 9991,))#鏈接服務端,在客戶端綁定服務端IP和端口 while True: f = z.recv(1024) #客戶端接收服務端sendall()發來的信息,1024表示最大接收1024字節 f2 = str(f, encoding='utf-8') #將接收到的服務端字節信息轉換成字符串 print(f2) #打印出服務端發來的信息 a = input("輸入要發送的信息") z.sendall(bytes(a, encoding="utf-8"))
IO多路複用監聽客戶端多通信原理圖
IO多路複用,有三種方式(select)、(poll)、(epoll) select和poll其實都是調用計算機底層來實現的監聽處理,epoll是異步處理的是文件描述符發生變化後主動告訴epoll的,這裏咱們須要知道一下
注意:wds系統只支持select模塊,
利用select()的第二個參數,實現IO多路複用,服務端讀寫分離
也就是讀和寫分開,在不一樣的代碼塊
服務端讀寫分離代碼
#!/usr/bin/env python # -*- coding:utf8 -*- """服務端實現讀寫分離""" import socket #導入模塊 k1 = socket.socket() #建立對象 k1.bind(('127.0.0.1', 9991)) #綁定服務端IP和端口 k1.listen() #監聽IP和端口 lib = [k1] #監聽的客戶鏈接地址或,客戶通信地址,傳給select第一個參數 lib2 = [] #接收客戶端通信地址,傳給select第二個參數 jilu = {} #接收字典,裏面是客戶端發通信內容的用戶通信地址(鍵)和通信內容(值) """使用自動監聽模塊""" import select #導入自動監聽多個端口模塊 while True: #select()自動監聽列表裏的socket對象(文件描述符),一旦監聽的對象,誰發生了變化(有用戶請求), # 就將發生變化對象的客戶端鏈接地址賦值給第一個變量a,第一個變量接收的列表,列表裏的每個元素,就是一個端口的用戶鏈接地址 #若是是多個端口在同時鏈接客戶端,那麼列表元素就是多個元素 a, b, c = select.select(lib, lib2, lib, 1) print("正在監聽的socket對象 %d" % len(lib)) #監聽的對象有多少個 print(a) #監聽發生變化的有哪些 """循環判斷連接地址或通信地址,獲取客戶端通信信息以及內容""" for k in a: #循環出select()獲取到的列表類型客戶端鏈接地址 if k == k1: #判斷循環到的鏈接地址等於socket對象,說明有新用戶鏈接 d, e = k.accept() #等待請求,獲取客戶端通信地址 lib.append(d) #將客戶端通信地址追加到列表,傳入select()監聽通信變化 jilu[d] = [] #將鏈接用戶通信地址添加到字典爲鍵,值爲空列表 else: #若是不是鏈接信息,說明是通信信息 try: #監控代碼是否異常 fd = k.recv(1024) #獲取客戶端發的信息 except Exception as e: #若是異常將此通信地址移除列表,中止監聽 lib.remove(k) #通信地址移除列表 else: fd2 = str(fd, encoding="utf-8") #接收客戶端發的信息轉換成字符串 jilu[k].append(fd2) # 將客戶端轉換的字符串追加到,字典裏當前客戶鍵的,列表裏爲值 lib2.append(k) #將當前客戶通信地址,添加到select第二個參數列表裏 """向客戶端發信息""" for km in b: #循環select第二個參數列表裏的通信用戶 jkd = jilu[km][0] #經過通信用戶拿到字典裏對應的通信內容 del jilu[km][0] #拿到通信內容後刪除通信內容 km.sendall(bytes(jkd+"好", encoding="utf-8")) #將通信內容加個好字,在發給客戶端 lib2.remove(km ) #在通信列表移除當前通信地址 for fh in c: #若是監聽鏈接出現異常 lib.remove(fh) #移除當前鏈接
客戶端代碼
#!/usr/bin/env python # -*- coding:utf8 -*- """建立客戶端""" import socket #導入模塊 z = socket.socket() #建立socket對象 b = z.connect(('127.0.0.1', 9991,))#鏈接服務端,在客戶端綁定服務端IP和端口 while True: a = input("輸入要發送的信息") z.sendall(bytes(a, encoding="utf-8")) f = z.recv(1024) #客戶端接收服務端sendall()發來的信息,1024表示最大接收1024字節 f2 = str(f, encoding='utf-8') #將接收到的服務端字節信息轉換成字符串 print(f2) #打印出服務端發來的信息
IO多路複用,服務端讀寫分離原理圖