You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Socket多文件传输异常排查:首文件正常,次文件文件名与内容混叠

多文件Socket传输问题排查:第二个文件文件名与内容混叠

我正在通过Socket编程实现多文件传输,发送端文件发送正常,但接收端第一个文件能正常接收(包含文件及内部内容),第二个文件出现文件名与内容混叠的情况,导致程序无法识别文件名而中断。

服务端代码

forbidden_extensions = [".exe", ".dll"]

def receive_files():  #images properly but single
    try:
        while True:
            filename = file_client_socket.recv(1024).decode('utf-8')
            print('filename---', filename)

            directory = os.path.join(os.getcwd(), 'Received3')
            os.makedirs(directory, exist_ok=True)
            
            destination = os.path.join(directory, filename)
            print(f'destination : {destination}')  
            with open(destination, 'wb') as file:
                data = file_client_socket.recv(1024)
                print(f'data to be receive : {data}')
                file.write(data)
                time.sleep(2)
                
                while len(data) == 1024:
                    data = file_client_socket.recv(1024)
                    # print(f'data to be sent inside while : {data}')
                    file.write(data)
                    # time.sleep(2)  # Add time delay of 10 seconds between receiving chunks
                
                     

            print('File successfully received:', filename)

            connection_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            log_message = f"{filename} File successfully received from {socket.gethostbyname(socket.gethostname())} at {connection_time}\n"

            with open("connection_log.txt", "a") as file:
                file.write(log_message)

            messagebox.showinfo("File Received ", f"File '{filename}' received successfully!")

            # time.sleep(2)  # Add time delay of 2 seconds after receiving a file

    except Exception as e:
        print(f"An error occurred in receive_files(): {str(e)}")
    # except (NotADirectoryError,FileNotFoundError):
    #    if not filename:
    #             print('inside not filename')
                # If no filename is received, it means all files have been received
                # break
                # file2=filename[0]
                # destination1 = os.path.join(destination1,file2)
                # print(f'file if not filename : {file2} ---------- {destination1}')

客户端代码

def send_files():
    try:
        selected_indices = listbox.curselection()
        if selected_indices:
            files = [listbox.get(index) for index in selected_indices]

            for file_path in files:
                filename = os.path.basename(file_path)
                print(f'file path : {file_path}')

                # Send the filename
                file_server_socket.send(filename.encode('utf-8'))

                # Send the file
                with open(file_path, "rb") as file:
                    data = file.read()
                    # print(f'data to be sent : {data}')
                    file_server_socket.send(data)
                    print(f"File sent: {filename}")
                    
                    time.sleep(2)   
                
                print("All files sent.")
                connection_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

                # Write the log message for each file to a file
                with open("client_connection_log.txt", "a") as file:
                    # for filename in files:
                        log_message = f"{filename} File successfully sent at {connection_time}\n"
                        file.write(log_message)

        else:
            messagebox.showwarning("No File Selected", "Please select at least one file to send.")
    except NameError:
        pass

问题根源

  1. TCP流式传输无边界:Socket的recv/send基于流传输,没有天然数据包边界。当文件大小为1024字节整数倍时,服务端while len(data) == 1024的循环会执行下一次recv,此时客户端已开始发送下一个文件的文件名,导致文件名被误当成前一个文件的内容写入,后续读取文件名时又读到文件内容,引发混叠。
  2. 传输逻辑不严谨:客户端一次性读取整个文件发送,未考虑大文件分块场景;服务端依赖len(data) == 1024判断文件是否接收完毕,存在逻辑缺陷。
  3. 无同步机制:客户端发送完文件后直接发送下一个文件名,未等待服务端接收确认,服务端处理速度滞后时会导致数据边界混淆。

修复方案

核心思路是通过长度前缀明确每个数据块的边界,让服务端准确读取对应内容:

修复后的客户端代码

import os
import datetime
import tkinter.messagebox as messagebox

def send_files():
    try:
        selected_indices = listbox.curselection()
        if selected_indices:
            files = [listbox.get(index) for index in selected_indices]

            for file_path in files:
                filename = os.path.basename(file_path)
                print(f'file path : {file_path}')

                # 1. 发送文件名长度(4字节,大端字节序)
                filename_bytes = filename.encode('utf-8')
                filename_len = len(filename_bytes)
                file_server_socket.send(filename_len.to_bytes(4, byteorder='big'))
                # 2. 发送文件名
                file_server_socket.send(filename_bytes)

                # 3. 发送文件内容长度
                file_size = os.path.getsize(file_path)
                file_server_socket.send(file_size.to_bytes(8, byteorder='big'))

                # 4. 分块发送文件内容
                with open(file_path, "rb") as file:
                    sent_bytes = 0
                    while sent_bytes < file_size:
                        chunk = file.read(1024)
                        file_server_socket.send(chunk)
                        sent_bytes += len(chunk)
                
                print(f"File sent: {filename}")
                connection_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

                # 写入日志
                with open("client_connection_log.txt", "a") as log_file:
                    log_message = f"{filename} File successfully sent at {connection_time}\n"
                    log_file.write(log_message)

            print("All files sent.")
        else:
            messagebox.showwarning("No File Selected", "Please select at least one file to send.")
    except NameError:
        pass

修复后的服务端代码

import os
import datetime
import socket
import tkinter.messagebox as messagebox

forbidden_extensions = [".exe", ".dll"]

def receive_files():
    try:
        while True:
            # 1. 读取文件名长度(4字节)
            filename_len_bytes = file_client_socket.recv(4)
            if not filename_len_bytes:
                break  # 连接断开
            filename_len = int.from_bytes(filename_len_bytes, byteorder='big')
            # 2. 读取文件名
            filename = file_client_socket.recv(filename_len).decode('utf-8')
            print('filename---', filename)

            directory = os.path.join(os.getcwd(), 'Received3')
            os.makedirs(directory, exist_ok=True)
            
            destination = os.path.join(directory, filename)
            print(f'destination : {destination}')  

            # 3. 读取文件内容长度(8字节)
            file_size_bytes = file_client_socket.recv(8)
            if not file_size_bytes:
                break
            file_size = int.from_bytes(file_size_bytes, byteorder='big')

            # 4. 分块接收文件内容
            with open(destination, 'wb') as file:
                received_bytes = 0
                while received_bytes < file_size:
                    chunk = file_client_socket.recv(min(1024, file_size - received_bytes))
                    if not chunk:
                        break  # 连接中断
                    file.write(chunk)
                    received_bytes += len(chunk)

            print('File successfully received:', filename)

            connection_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            log_message = f"{filename} File successfully received from {socket.gethostbyname(socket.gethostname())} at {connection_time}\n"

            with open("connection_log.txt", "a") as log_file:
                log_file.write(log_message)

            messagebox.showinfo("File Received ", f"File '{filename}' received successfully!")

    except Exception as e:
        print(f"An error occurred in receive_files(): {str(e)}")

关键修复点说明

  • 长度前缀:用固定字节数传递文件名、文件内容的长度,服务端可准确读取对应内容,彻底避免数据混叠。
  • 分块传输:大文件分块发送/接收,避免内存占用过高,同时适配Socket缓冲区限制。
  • 明确终止条件:通过recv返回空字节检测连接断开,避免无限阻塞。

内容的提问来源于stack exchange,提问作者pooja testing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 23:34:54