Python之基於socket和select模塊實現IO多路復用


'''IO指的是輸入輸出,一部分指的是文件操作,還有一部分
網絡傳輸操作,例如soekct就是其中之一;多路復用指的是
利用一種機制,同時使用多個IO,例如同時監聽多個文件句
柄(socket對象一旦傳送或者接收信息),一旦文件句柄出
現變化就會立刻感知到
'''
1、下面通過IO多路復用實現多人同時連接socket服務器

這是服務端代碼
import socket

sk1 = socket.socket()#sk1,sk2,sk3這就是一個文件描述符
sk1.bind(('127.0.0.1',8002))
sk1.listen()


sk2 = socket.socket()
sk2.bind(('127.0.0.1',8003))
sk2.listen()

sk3 = socket.socket()
sk3.bind(('127.0.0.1',8004))
sk3.listen()

inputs = [sk1,sk2,sk3]
import select
while True:
    '''[sk1,sk2,sk3],select內部會自動監聽sk1,sk21,sk3三個對象
    一旦某個句柄發生變化就會被監聽到
    '''
    #如果有人鏈接sk1,則會被添加進列表,r_list = [sk1]
    r_list,w_list,e_list = select.select(inputs,[],[],1)#1表示等一秒,在while執行到這里的時候監測一秒,沒有人來鏈接的話就接着循環
    #print(r_list,sk1.listen())
    for sk in r_list:
        conn,address = sk.accept()
        #print(conn,address)
        conn.sendall(bytes('你好',encoding='utf-8'))
        conn.close()

  客戶端的代碼都是一樣的,就差個端口

#客戶端一
import socket


obj = socket.socket()
obj.connect(('127.0.0.1',8002))
content = str(obj.recv(1024),encoding='utf-8')
print(content)

obj.close()

#客戶端二
import socket


obj = socket.socket()
obj.connect(('127.0.0.1',8003))
content = str(obj.recv(1024),encoding='utf-8')
print(content)

obj.close()


#客戶端三
import socket


obj = socket.socket()
obj.connect(('127.0.0.1',8004))
content = str(obj.recv(1024),encoding='utf-8')
print(content)

obj.close()

  執行結果:只要啟動服務器端,然后不同的客戶端多次啟動都能收到信息,多個端口成功被監聽

2、下面使用select模塊實現多路復用,使同一個端口同時接收多個鏈接

import socket

sk1 = socket.socket()#sk1,sk2,sk3這就是一個文件描述符
sk1.bind(('127.0.0.1',8002))
sk1.listen()

#
# sk2 = socket.socket()
# sk2.bind(('127.0.0.1',8003))
# sk2.listen()
#
# sk3 = socket.socket()
# sk3.bind(('127.0.0.1',8004))
# sk3.listen()

inputs = [sk1]
import select

#epoll效率更高,但是Windows不支持,它是誰有問題就告訴它,不用循壞
while True:
    '''[sk1,sk2,sk3],select內部會自動監聽sk1,sk21,sk3三個對象
    一旦某個句柄發生變化(某人來鏈接)就會被監聽到
    '''
    #如果有人鏈接sk1,則會被添加進列表,r_list = [sk1]
    r_list,w_list,e_list = select.select(inputs,[],[],1)
    '''第三個參數是監聽錯誤的,只要有錯誤出現,就會被監聽到,返回e_list
       第二個參數返回給w_list,只要傳了什么,就原封不動的傳給w_list'''
    print('正在監聽 %s 多少個對象' % len(inputs))
    for sk in r_list:
        if sk == sk1:
            #句柄跟服務器端的對象一樣,表示有新用戶來鏈接了
            conn,address = sk.accept()
            inputs.append(conn)#加入去之后,inputs有一個鏈接對象和服務器對象
        else:
            #有老用戶發消息了
            try:
                data_byte =sk.recv(1024)

                data_str =str(data_byte,encoding='utf-8')
                sk.sendall(bytes(data_str+'收到了',encoding='utf-8'))
            except:#空信息表示客戶端斷開鏈接,所以要在監聽中移除
               
                inputs.remove(sk)#這里的sk就是之前傳進去的conn,因為r_list接收的是有變化的值
                

 啟動這個服務端之后,就可以實現多路復用了,可以接收多個客戶端同時連接

3、下面介紹一些多路操作里面的讀寫分離

import socket

sk1 = socket.socket()#sk1就是一個文件描述符
sk1.bind(('127.0.0.1',8002))
sk1.listen()
inputs = [sk1]#被檢測發生變動的句柄放這里
outputs=[]#用來記錄誰給服務端發過信息,以便回復
message_dict = {}#接收信息的,根據句柄形成鍵值對
import select

#epoll效率更高,但是Windows不支持,它是誰有問題就告訴它,不用循壞
while True:
    '''[sk1,sk2,sk3],select內部會自動監聽sk1,sk21,sk3三個對象
    一旦某個句柄發生變化(某人來鏈接)就會被監聽到
    '''
    #如果有人鏈接sk1,則會被添加進列表,r_list = [sk1]
    r_list,w_list,e_list = select.select(inputs,outputs,inputs,1)
    '''第三個參數是監聽錯誤的,只要有錯誤出現,就會被監聽到,返回e_list
       第二個參數返回給w_list,只要傳了什么,就原封不動的傳給w_list'''
    print('正在監聽 %s 多少個對象' % len(inputs))
    for sk in r_list:
        if sk == sk1:
            #句柄跟服務器端的對象一樣,表示有新用戶來鏈接了
            conn,address = sk.accept()
            inputs.append(conn)#加入去之后,inputs有一個鏈接對象和服務器對象
            message_dict[conn]=[]#字典的key是具體的鏈接對象
        else:
            #有老用戶發消息了
            try:
                data_byte =sk.recv(1024)

            except Exception as e:#空信息表示客戶端斷開鏈接,所以要在監聽中移除
                print(e)
                inputs.remove(sk)#這里的sk就是之前傳進去的conn,因為r_list接收的是有變化的值
            else:
                data_str = str(data_byte, encoding='utf-8')

                message_dict[sk].append(data_str)
                outputs.append(sk)#把傳送數據的句柄添加進去第二個參數
    for conn in w_list:
        recv_str=message_dict[conn][0]#拿到我們收到了的信息
        del message_dict[conn][0]#刪除信息,防止下次出現一樣的消息
        print(recv_str)
        conn.sendall(bytes(recv_str+'收到了',encoding='utf-8'))
        outputs.remove(conn)#回復完成之后在列表刪除
    for sk_or_conn in e_list:
        e_list.remove(sk_or_conn)

  這樣可以形成簡單的讀寫分離操作

對於select里面的參數關系,這里有個武sir畫的圖

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM