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 文件校验成功! >>:

浙公网安备 33010602011771号