Python之網絡編程(socketserver模塊詳解、鏈接認證)

socketserver本質是基於socket進行的一個封裝,將多線程併發功能集成到一個新的模塊裏,就叫socketserver;
它用來解決TCP套接字無法併發的問題,也就是無法一個服務端不能同時服務多個客戶端的問題(UDP沒有此問題,因爲它不需要鏈接)

多線程對多客戶端

多線程對多客戶端

socketserver

分類(2種)

1、server類:處理鏈接
BaseServer 處理鏈接必須
TCPServer 處理流式(TCP)鏈接
UDPServer 處理數據報式(UDP)鏈接

2、request類:處理通信必須
BaseRequestHandler 處理通信
StreamRequestHandler 處理流式通信,即TCP
DatagramRequestHandler 處理數據報式通信,即UDP

12個類的繼承關係(原理)

socketserver模塊的源碼裏面,主要只有12個類,這12個類實現了socketserver的所有功能,下面是這12個類的繼承關係:
12個類的繼承關係

socketserver的實現效果

1、服務端測試代碼:

#導入socketserver模塊
import socketserver

#定義一個類,該類必須繼承socketserver下的BaseRequestHandler類
class MyServer(socketserver.BaseRequestHandler):
    def handle(self):  #必須定義一個handle方法
        print('conn is ',self.request)  #self.request是獲取的鏈接,相當於conn
        print('addr is ',self.client_address)  #self.client_address則是獲取的地址,相當於addr

        #循環收發消息
        while True:
            try:  #異常處理
                #收消息
                data = self.request.recv(1024)  #1024字節
                if not data:continue  #如果是空就重新輸入
                if data == 'quit':break  #退出
                print('客戶端發來的消息是:',data.decode('utf-8'),'\n')

                #發消息
                self.request.sendall(data.upper())  #將受到的數據轉化爲大寫後發回客戶端
            except Exception as e:
                print(e)  #打印錯誤
                break

if __name__ == '__main__':
    #多線程的TCP服務端,即可以同時開啓多個鏈接運行,也就是多客戶端併發效果;
    #再將這樣的功能實例化成一個對象s
    s = socketserver.ThreadingTCPServer(('192.168.43.247',8080),MyServer)
    #第一個參數爲服務端的ip地址,用元組傳進;第二個參數是剛剛定義的那個類
    
    s.serve_forever()  #永遠,就是通信循環(鏈接循環)

2、客戶端測試代碼:

#客戶端就不需要socketserver模塊了,用socket模塊收發消息就可以了
from socket import *

ip_port = ('192.168.43.247',8080)  #記錄ip地址
back_log = 5
buffer_size = 1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

#實現循環發收消息
while True:
    msg = input('請輸入>>>')
    tcp_client.send(msg.encode('utf-8'))  #發消息
    print('客戶端已發送出消息!')  #接收到的消息要解碼
    #接收消息
    data = tcp_client.recv(buffer_size)  #buffer_size=1024默認參數,表示字節格式
    print('服務端發回的消息是:',data.decode('utf-8'))

tcp_client.close()  #關閉socket對象

3、多個相同的客戶端
當然我們需要準備多個相同的客戶端才能看到效果
多個客戶端
4、測試結果:
測試結果
斷開併發
可以看到,成功實現了tcp套接字的併發效果,只有一個客戶端,可以同時服務多個客戶端,並且,四個客戶端可以分別斷開,不影響其他客戶端的使用

認證鏈接的合法性

所謂認證客戶端合法性,就是通過一條或多條規則,讓服務端和客戶端做一個接口驗證;

如果驗證成功就表明鏈接的合法的,就讓對方的鏈接接入進來;錯了當然就不允許接入

說白了就像對暗號一樣,如果對了,就說明是自己人,就允許你進入;錯了,就不準進

加鹽(hmac)

我們通過哈希算法,可以驗證一段數據是否有效,方法就是對比該數據的哈希值,來判斷用戶的暗號是否正確;

爲了防止黑客通過彩虹表根據哈希值反推我們的暗號,在計算哈希的時候,不能只通過對原始數據進行比較,還需要增加一些salt來使得相同的輸入也能得到不同的哈希,這樣,就可以讓我們的數據更安全;

一般都是我們隨機生成一個salt(一段隨機碼),然後與我們自定義的一個暗號進行混合運算,這個就是hmac算法,它通過一個標準算法,在計算哈希的過程中,把key混入計算過程中;

hmac算法練習代碼:

import hmac

message = b'ViewIn is strongest'  #自定義一段文字
secret_key = b'Very'  #自定義暗號的關鍵文字

h = hmac.new(secret_key, message, digestmod='MD5')  #將前兩段文字用hmac算法混個,模式爲MD5
#如果文字很長,可以多次使用h.update(msg)操作

print(h.hexdigest())  #以十六進制形式輸出h

練習結果:
hmac算法
可見使用hmac和普通hash算法都非常類似,hmac輸出的長度和原始哈希算法的長度一樣長。不過傳入的key和message都必須是bytes類型,str類型需要首先編碼爲bytes;
所以上文練習代碼裏在字符串前加了一個b,表示是二進制形式的變量

對暗號

1、我們要完成驗證鏈接合法性,就必須先用hmac對生成我們的暗號

#驗證鏈接的合法性
def conn_auth(conn):
    print('開始驗證新鏈接的合法性')
    msg=os.urandom(32)  #生成一個32位隨機碼
    conn.sendall(msg)  #發送隨機碼給想接入的鏈接
    h=hmac.new(secret_key,msg)  #將隨機碼與我們自定義的暗號混合(加鹽操作)
    digest=h.digest()  #再將混合後的內容轉化成二進制
    respone=conn.recv(len(digest))  #接收對方發來的加鹽結果
    return hmac.compare_digest(respone,digest)
    #再返回digest與想接入鏈接發來的消息做比較的結果,是一個布爾值
    #該布爾值作爲對暗號的結果

2、暗號生成完了,自然就是要開始對暗號了

#開始對暗號
def data_handler(conn,bufsize=1024):  #傳入鏈接、可接收內容的大小
    #驗證鏈接合法性
    if not conn_auth(conn):  #傳入需要驗證的鏈接到conn_auth
        print('該鏈接不合法,關閉!!!')
        conn.close()
        return
    
    #暗號對了,通信
    print('鏈接合法,開始通信!')
    while True:  #通信就是循環收發消息
        data=conn.recv(bufsize)  #收消息
        if not data:continue
        if data == 'quit':break
        conn.sendall(data.upper())  #發消息

經過這兩步操作我們就可以對任何傳來的鏈接進行驗證了,客戶端發來正確的暗號就讓其連接上服務端;如果暗號錯誤或者壓根就不知道有暗號,那肯定就連接不上服務端

3、實現效果:
鏈接是合法的,暗號對上了:
鏈接合法
知道有暗號,但是暗號沒對上:
非法1
壓根就不知道有暗號:
不知道有暗號
這樣就成功完成了對鏈接的認證,合法就接入,不合法就斷開。
需要測試代碼的Python學習夥伴們請私聊或評論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章