soket粘包問題及解決方案
阿新 • • 發佈:2020-09-15
一、粘包問題
問題1: 無法確認對方傳送過來資料的大小。
import socket client = socket.socket() client.connect( ('127.0.0.1', 9000) ) while True: cmd = input('客戶端輸入的內容: ') client.send(cmd.encode('utf-8')) data = client.recv(19190) print(len(data)) print(data.decode('gbk'))
import socket import subprocess server= socket.socket() server.bind(('127.0.0.1',9000)) server.listen(5) while True: conn,addr = server.accept() print(addr) while True: try: cmd = conn.recv(10) if len(cmd) == 0: continue cmd = cmd.decode('utf-8') #utf8 if cmd == 'q': break #呼叫subprocess連線終端,對終端進行操作,並獲取操作後正確或錯誤的結果 obj = subprocess.Popen( #cmd接受的是解碼後的字串 cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) #結果交給result變數名 result = obj.stdout.read()+obj.stderr.read()print(len(result)) print(result.decode('gbk')) #windows系統下預設編碼gbk #將結果返回給客戶端 conn.send(result) except Exception as e: print(e) break conn.close()
問題2: 在傳送資料間隔短並且資料量小的情況下,會將所有資料一次性發送。
import socket client = socket.socket() client.connect( ('127.0.0.1', 9000) ) client.send(b'hello') client.send(b'hello') client.send(b'hello')
import socket server = socket.socket() server.bind( ('127.0.0.1', 9000) ) server.listen(5) conn, addr = server.accept() data = conn.recv(5) print(data) # b'hello' data = conn.recv(1024) print(data) # b'hello' data = conn.recv(1024) print(data) # b'hello'
二、粘包問題的解決方案:
粘包問題的解決方案: 確認對方資料的大小。
這裡需要用 struct模組
struct是什麼?
是一個python內建的模組,它可以將固定長度的資料,打包成固定格式的長度。
固定格式:如 “ i ” 模式
i : 4
struct作用:
可以將真實資料,做成一個固定長度的報頭,客戶端傳送給伺服器,伺服器可以接受報頭,然後對報頭進行解包,獲取真實資料的長度,進行接收即可
import struct data = b'1111111111111111' print(len(data)) #16 #打包製作報頭 header = struct.pack('i',len(data)) print(header) #b'\x10\x00\x00\x00' print(len(header)) #4 #解包獲取真實資料長度 --->得到一個元組,元組中第一個值是真實資料的長度 res = struct.unpack('i',header)[0] print(res) #16
無論哪一端先發送資料
客戶端
- 1) 先製作報頭,併發送 (struct)
- 2) 傳送真實資料
服務端:
- 1) 接收報頭,並解包獲取 真實資料長度
- 2) 根據真實資料長度 接收真實資料
recv(真實資料長度)
簡單版:
import socket import struct client = socket.socket() client.connect(('127.0.0.1', 9000)) while True: cmd = input('客戶端輸入的內容: ') cmd_bytes = cmd.encode('utf-8') header = struct.pack('i',len(cmd_bytes)) #做一個報頭 print(len(header)) #列印報頭的長度 client.send(header) #傳送報頭 client.send(cmd_bytes) #待服務端確認長度後,傳送真實資料長度 headers = client.recv(4) #接受服務端的報頭 data_len = struct.unpack('i',headers)[0] #解包 result = client.recv(data_len) #接受伺服器返回的真實資料的長度 print('接受伺服器返回的真實資料的長度',len(result)) print(result.decode('gbk'))
import socket import subprocess import struct server = socket.socket() server.bind(('127.0.0.1',9000)) server.listen(5) while True: conn,addr = server.accept() print(addr) while True: try: header = conn.recv(10) #獲取客戶端傳過來的報頭 data_len = struct.unpack('i',header)[0] #解包獲取真實資料的長度 cmd = conn.recv(data_len) #準備接受真實資料 if len(cmd) == 0: continue cmd = cmd.decode('utf-8') if cmd == 'q': break #呼叫subprocess連線終端,對終端進行操作,並獲取操作後正確或錯誤的結果 obj = subprocess.Popen( #cmd接受的是解碼後的字串 cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) result = obj.stdout.read()+obj.stderr.read() #獲取結果 print('傳送給服務端返回的真實資料的長度', len(result)) header = struct.pack('i', len(result)) #做報頭 print(len(header)) conn.send(header) #傳送報頭給客戶端 conn.send(result) #將結果返回給客戶端 except Exception as e: print(e) break conn.close()
序列化版:
import socket,json import struct client = socket.socket() client.connect(('127.0.0.1',9000)) while True: movie_name = input('請輸入上傳的電影名字:') #偽裝電影的真實資料 movie = 1000000 send_dic ={'movie_name':movie_name, 'movie':movie} #序列化 json = json.dumps(send_dic) print(json) print(json.encode('utf-8')) print(len(json.encode('utf-8'))) json_bytes = json.encode('utf-8') #做一個報頭 header = struct.pack('i',len(json_bytes)) #先發送報頭 client.send(header) #再發送真實資料 client.send(json_bytes)
import socket,json import struct server = socket.socket() server.bind(('127.0.0.1',9000)) server.listen(5) while True: conn,addr = server.accept() while True: try: #獲取客戶端傳過來的報頭 header = conn.recv(4) #解包獲取真實資料的長度 json_len = struct.unpack('i',header)[0] #接受json(dic)的真實資料 json_bytes_data = conn.recv(json_len) #將bytes型別資料轉為json資料型別 json_data = json_bytes_data.decode('utf-8') #反序列化 json--->dict back_dic = json.loads(json_data) print(back_dic) print(back_dic.get('movie')) except Exception as e: print(e) break conn.close()