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個類的繼承關係:
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和普通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、實現效果:
鏈接是合法的,暗號對上了:
知道有暗號,但是暗號沒對上:
壓根就不知道有暗號:
這樣就成功完成了對鏈接的認證,合法就接入,不合法就斷開。
需要測試代碼的Python學習夥伴們請私聊或評論