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

 

posted @ 2017-10-25 10:06  Mr.hu  阅读(147)  评论(0编辑  收藏  举报