立即學習:https://edu.csdn.net/course/play/24458/296244?utm_source=blogtoedu
粘包現象解決(終極版)
?
1.簡單版的問題所在
?
1)報頭信息不一定只是包含著命令執行結果的字節數長度,在文件傳輸的時候也可能包含文件名等,這時候就需要用到字典來將文件的相關信息進行封裝
?
2)struct模塊中struct.pack('l',數據長度),這個數據長度是有限制的,當發送的文件的字節數長度超過時,就會出現錯誤!
?
?
2.知識點
?
1)字典轉為bytes類型:首先使用json.dumps(dict)將字典序列化為json字符串,然后使用.encode('utf-8')對json字符串進行編碼成bytes類型
?
2)bytes類型解析為字典:首先對bytes進行解碼成json字符串,然后將json字符串反序列為字典即可(json.loads(json字符串))
?
3.關鍵代碼
'''
服務端
'''......#2處理命令,執行命令并且獲得命令得到的結果obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,#將正確運行命令得到的結果傳給管道stdout中stderr=subprocess.PIPE)#將沒有正確運行命令得到的返回信息存放在stderr管道中stdout = obj.stdout.read()stderr = obj.stderr.read()total_size = len(stderr + stdout)#1)制作包含文件名和文件大小的文件頭,用字典實現headers_dict = {"filename":"nianbao","filedata":"2020/03/09","total_size":total_size}#2)將字典先序列化成驚悚字符串,再轉為bytes類型文件頭headers_json = json.dumps(headers_dict)#3)獲取bytes類型的長度headers_bytes = headers_json.encode('utf-8')headers_size = len(headers_bytes)#4)將bytes類型文件頭長度定制為固定長度的報頭header = struct.pack('i',headers_size)#5)向客戶端發送報頭conn.send(header)#6)向客戶端發送包含文件信息的字典conn.send(headers_bytes)#7)向客戶端發送真實命令執行的結果data = stdout + stderrconn.send(data)......
'''
客戶端
'''......#4、接收服務器返回來的數據recv()#1)先接收由服務器返回來的報頭,報頭是固定長度的,因此取前面4字節的數據即為報頭header = phone.recv(4)#返回的是一個對象#2)解析報頭,得到bytes類型的文件頭長度obj_truple = struct.unpack('i',header)#返回的是一個元組headers_bytes_size = obj_truple[0]#取元組第一個元素即為總字節數#3)接收bytes類型的文件頭數據headers_bytes = phone.recv(headers_bytes_size)#4)將bytes類型的文件頭數據反序列化成字典headers_json = headers_bytes.decode('utf-8')headers_dict = json.loads(headers_json)#5)從字典中取出字命令執行結果字節總長度total_size = headers_dict['total_size']#6)接收返回的數據recv_size = 0data = b''while recv_size < total_size:recv_data = phone.recv(1024)#接收小于1024bytes的數據recv_size += len(recv_data)data += recv_data#7)打印字典并且打印返回的數據print(headers_dict)print('服務器返回來的數據:',data.decode('gbk'))print('*'*50)......
4.完整代碼:在上一篇筆記的基礎上修改一下即可