解決粘包 模塊struct struct.pack(type,num) type:是num的類型 num :是一個數字 r = struct.pack 把一個數字打包成一個四字節的bytes struct.unpack(type,r) 功能:解包,把r解成原數字,結果是一個元組,原數字在元組的下標位0的位置 #解決粘包:原理是在服務器接收到字典長度后,根據字典長度去recv字典中的內容,就不會造成recv最后一次接收完后剩下的空間留給部分文件內容所造成字典內容和文件內容黏在一起 #實現文件的上傳功能 #client層 import socket import json import struct import os sk=socket.socket() sk.connect(("10.70.2.143",8080)) option={"1":"upload","2":"download"} for index,value in option.items(): print(index,value) num=input("請輸入您的選擇") if num=="1": dic={"opt":option.get(num),"filename":None,"filesize":None} file_path=input("請輸入所需要上傳的文件的絕對路徑") filename=os.path.basename(file_path) filesize=os.path.getsize(file_path) dic["filename"]=filename dic["filesize"]=filesize #將字典轉為字符串類型 str_dic=json.dumps(dic) #獲取字典的長度,為了解決粘包 len_dic=len(str_dic) #解決粘包:原理是在服務器接收到字典長度后,根據字典長度去recv字典中的內容,就不會造成recv最后一次接收完后剩下的空間留給部分文件內容所造成字典內容和文件內容黏在一起 #用一個4bytes的數據表示字典的長度 #服務器接收的時候先接收recv(4)個字節長度就可以得到字典的長度 b_len_dic=struct.pack('i',len_dic) #i是原字典長度的數據類型,第二個參數是需要轉化的那個字典長度本身 #將bytes類型的字典長度和字典內容進行拼接並且發送 sk.send(b_len_dic+str_dic.encode("utf-8")) #此時字典內容是bytes類型所以可以進行bytes的拼接 #文件的上傳 with open(file_path,"rb") as f: #根據文件大小進行文件的上傳 while filesize: content=f.read(1024) sk.send(content) filesize-=len(content) else: pass sk.close() #server層 import socket import json import struct sk=socket.socket() sk.bind(("10.70.2.143",8080)) sk.listen() conn,addr=sk.accept() #先接收從客戶端傳過來的四個字節長度的字典長度 b_len_dic=conn.recv(4) #將字節長度的字典長度進行解包成整型的字典長度 len_dic=struct.unpack('i',b_len_dic)[0] #因為字典長度傳過來的是一個元組的第0個下標元素 #根據字典長度去接收字典的內容 #下次進行文件傳輸的時候就不會造成粘包現象 str_dic=conn.recv(len_dic).decode("utf-8") #將字符串的字典反序列化成字典 dic=json.loads(str_dic) if dic["opt"]=="upload": #為了防止文件同名,使用字符串拼接文件名進行區分 filename="1"+dic["filename"] #接收從客戶端傳過來的文件 with open(filename,"ab") as f: #根據文件大小接收數據 while dic["filesize"]: content=conn.recv(1024) f.write(content) #字典中文件大小減去已接受的文件大小 dic["filesize"]-=len(content) elif dic["opt"]=="download": pass conn.close() sk.close()
