該程序基於tcp協議,實現服務端向客戶端傳輸文件的功能。
當服務端發送的文件過大時,客戶端無法正常接受,造成黏包現象。解決的方案是,每次發送文件時,告訴客戶端服務器要發送文件的大小,同時將文件按指定大小(m)拆分開發送。客戶端在接受到文件大小后,按指定大小(n)進行接受。m最好和n相等,感興趣的話可以自己試試看,m和n的之間存在什么關系。
這里用struct模塊優化代碼。len_a = struct.pack('i', len(a))將a的值長度計算出來,並轉化成4位的bytes類型; struct.unpack('i',len)將bytes類型的len轉化成int型的數字,並放入到元組中;所有b= struct.unpack('i',len).[0]中,b是變量a的值長度,即len(a)=b。關於struct的使用,詳細使用看【】注釋對應的行。當然,我們也可以通過其len函數來獲取a值長度,然后再轉化成bytes類型,再解碼。
服務端的代碼
1 import json 2 import socket 3 import struct 4 import os 5 server = socket.socket() 6 server.bind(('127.0.0.1',9002)) 7 server.listen() 8 9 conn,addr = server.accept() 10 #發送文件的信息:文件名、文件大小、文件路徑 11 f_head = {'f_name': r'xxxx2019.doc', #文件名稱(r表示字符串不發生轉換) 12 'f_size': None, #文件大小(未賦值) 13 'f_path': r'C:\Users\Apple\Desktop\學習\s1' #文件存儲位置 14 } 15 fPath = os.path.join(f_head['f_path'],f_head['f_name']) #文件路徑(位置+名字) 16 fSize = os.path.getsize(fPath) #文件大小 17 f_head['f_size'] = fSize #給字典f_head的f_size賦值 18 j_head = json.dumps(f_head) #字典f_head轉換成字符串 19 b_head = j_head.encode('utf-8') #字符串(f_head)轉bytes 20 head_len = struct.pack('i', len(b_head)) #【獲取(f_head)bytes類型長度】 21 conn.send(head_len) #發送f_head長度到客戶端 22 conn.send(b_head) #發送f_head內容到客戶端 23 num = 1024 #m 24 25 #讀取fpath路徑下的文件,拆分文件並發送;至所有文件發送完畢跳出循環26 with open(fPath,'rb') as f: 27 while fSize > num: 28 conn.send(f.read(num)) 29 fSize = fSize - num 30 conn.send(f.read(fSize)) 31 32 conn.close() 33 server.close()
客戶端的代碼
1 import json 2 import socket 3 import struct 4 5 client = socket.socket() 6 client.connect(('127.0.0.1', 9002)) 7 8 num = 1024 #n 9 head_len = struct.unpack('i', client.recv(4))[0] #【接受服務端發來的f_head(bytes類型)的大小,並轉化成int類型】 10 j_head = client.recv(head_len).decode('utf-8') #接受服務端發來的f_head,並解碼得到字符串 11 head = json.loads(j_head) #講字符串類型的f_head,轉換為字典類型 12 fSize = head['f_size'] #獲取head的f_size值(即服務端f_head的f_size值) 13 14 #分部接受來自服務端的文件,並寫入;至所有文件接受完畢結束循環。15 with open(head['f_name'],'wb') as f: 16 while fSize > num: 17 msg = client.recv(num) 18 f.write(msg) 19 fSize = fSize - num 20 msg = client.recv(fSize) 21 f.write(msg) 22 23 client.close()
