粘包的两种处理方式
- 一次完整的交互(ack响应)
- 使用if判断,保证recv的最后一次,剩多少收多少(后面的数据就可再收了)
get文件
ftp server
- recv得到文件名
- 检测文件是否存在
- send文件大小给客户端(如果存在)
- 等待客户端确认
- 打开文件
- 边读边send,同时做MD5
- 最后send文件MD5
- 验证文件夹
- 关闭文件
ftp client
- send 命令给server
- recv文件大小
- send something #一次交互解决粘包
- wb模式打开新文件
- 边recv边写,同时做md5
- 关闭文件
- recv服务端发过来的源文件的md5值
在收完文件后又直接接收了md5的值,所以在收文件的过程中(f.close()之前),通过if判断,保证文件准确的接收了那么多
get file 交互图
import hashlib
import socket,os
server = socket.socket()
server.bind(('localhost', 9999))
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()
print(filename)
if os.path.isfile(filename):
f = open(filename, 'rb')
m = hashlib.md5()
file_size = os.stat(filename).st_size
conn.send( str(file_size).encode() ) #send file size
conn.recv(1024) #等待确认
for line in f:
m.update(line)
conn.send(line)
print("file md5", m.hexdigest())
f.close()
conn.send(m.hexdigest().encode())
print("send done")
server.close()
import socket
import hashlib
client = socket.socket()
client.connect(('localhost', 9999))
while True:
cmd = input('>>:').strip()
if not cmd:continue
if cmd.startswith("get"):
client.send(cmd.encode())
server_response = client.recv(1024)
print("server response:", server_response)
client.send(b'ready to recv file')
file_total_size = int(server_response.decode())
received_size = 0
filename = cmd.split()[1]
f = open(filename+".new",'wb')
m = hashlib.md5()
while received_size < file_total_size:
#如果没有此处if判断,可能会发生粘包(最后一次接受1024可能将md5一起收过来)
if file_total_size - received_size > 1024: #要收不止一次
size = 1024
else: #最后一次,剩多少收多少
size = file_total_size - received_size
data = client.recv(size) #用size替换1024
received_size += len(data)
m.update(data)
f.write(data)
print(file_total_size,received_size)
else:
new_file_md5 = m.hexdigest()
print("file recv done")
f.close()
server_file_md5 = client.recv(1024) #此处接收md5值(发生粘包客户端会在此处阻塞)
print("server file md5:",server_file_md5)
print("client file md5",new_file_md5)
client.close()