day8-socket实现文件发送粘包问题解决
问题简述
上一节在使用socket实现文件发送时,因为服务器端代码中有连续2次的发送,造成服务器端发送数据粘包问题,我们可以提供另一种思路:假设客户端知道自己要接收5M的数据,那么在客户端循环接收服务器端发来的数据时,只接收5M的数据,因为此代码中连续的2次发送,客户端每次默认接收1024,且当接收的数据可能出现大于等于文件原来的大小,有可能最后接收5.01M,在这种情况下,如果客户端只接收5M,然后将文件写入后再接收剩下的数据(MD5)0.1M,那么就可以解决这个粘包的问题,这种情况只会出现在最后一次接收数据,因为最后一次接收的数据可能小于1024,但是客户端默认接收1024,所以会将MD5的数据也发过来,所以解决方案——判断最后一次还剩下多少数据没有接收,只接收剩下的数据(不默认)
服务器端代码
import socket,os,hashlib server = socket.socket() server.bind(("localhost",9963)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: print("等待新指令") data = conn.recv(1024) if not data: print("客户端已断开") break cmd,filename = data.decode().split() #接收客户端发过来的命令和文件名 m = hashlib.md5() #生成MD5对象 print(filename) if os.path.isfile(filename): #判断文件是否存在 with open(filename,"rb") as f1: file_total_size = os.stat(filename).st_size #获取文件大小 conn.send(str(file_total_size).encode()) #发送文件大小给客户端 conn.recv(1024) #接收客户端的ack信息 for line in f1: m.update(line) #计算MD5值 conn.send(line) #边读边发文件给客户端 print("file MD5:",m.hexdigest()) f1.close() conn.send(m.hexdigest().encode()) #编码发送MD5给客户端 print("发送完成!") server.close()
优化客户端:根据最后一次接收文件的数据量(小于1024),剩多少收多少
import socket,hashlib client = socket.socket() client.connect(("localhost",9963)) while True: cmd_input = input(">>:") if len(cmd_input) == 0: continue if cmd_input.startswith("get"): client.send(cmd_input.encode()) server_response = client.recv(1024) #接收文件大小 print("server response:",server_response) client.send("准备接收文件".encode()) file_total_size = int(server_response.decode()) recv_size = 0 #初始化文件大小为0 filename = cmd_input.split()[1] with open(filename+".new","wb") as f1: m = hashlib.md5() while recv_size < file_total_size: #判断接收文件大小和文件总大小 if file_total_size - recv_size > 1024: #要收不止一次 size = 1024 else: #最后一次了,剩多少收多少 size = file_total_size - recv_size print("Last recv:",size) data = client.recv(size) recv_size+=len(data) #累加每次循环接收文件的大小 m.update(data) f1.write(data) #循环写入文件 print(file_total_size,recv_size) else: new_file_md5 = m.hexdigest() print("文件接收完成") f1.close() server_file_md5 = client.recv(1024).decode() print("server file MD5:", server_file_md5) print("client file MD5:", new_file_md5) if new_file_md5 == server_file_md5: print("文件校验成功!") client.close()
执行过程
服务端
#运行输出 new conn: ('127.0.0.1', 63382) 等待新指令 bigfile file MD5: e34d8f2417479e0ee706aebbcdf542cc 等待新指令
客户端
#运行输出 ................. 469073727 469068621 469073727 469069645 469073727 469070669 469073727 469071693 469073727 469072717 Last recv: 1010 469073727 469073727 文件接收完成 server file MD5: e34d8f2417479e0ee706aebbcdf542cc client file MD5: e34d8f2417479e0ee706aebbcdf542cc 文件校验成功! >>: