socket传文件

粘包的两种处理方式

  • 一次完整的交互(ack响应)
  • 使用if判断,保证recv的最后一次,剩多少收多少(后面的数据就可再收了)

get文件

ftp server

  1. recv得到文件名
  2. 检测文件是否存在
  3. send文件大小给客户端(如果存在)
  4. 等待客户端确认
  5. 打开文件
  6. 边读边send,同时做MD5
  7. 最后send文件MD5
  8. 验证文件夹
  9. 关闭文件

ftp client

  1. send 命令给server
  2. recv文件大小
  3. send something #一次交互解决粘包
  4. wb模式打开新文件
  5. 边recv边写,同时做md5
  6. 关闭文件
  7. recv服务端发过来的源文件的md5值

在收完文件后又直接接收了md5的值,所以在收文件的过程中(f.close()之前),通过if判断,保证文件准确的接收了那么多

get file 交互图


socket传文件_第1张图片
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()

简单的ftp:https://github.com/jinboxu/ftp

你可能感兴趣的:(socket传文件)