第五十五節,IO多路複用select模塊加socket模塊,僞多線併發

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多路複用,服務端讀寫分離原理圖

相關文章
相關標籤/搜索