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
问题根源
- TCP流式传输无边界:Socket的
recv/send基于流传输,没有天然数据包边界。当文件大小为1024字节整数倍时,服务端while len(data) == 1024的循环会执行下一次recv,此时客户端已开始发送下一个文件的文件名,导致文件名被误当成前一个文件的内容写入,后续读取文件名时又读到文件内容,引发混叠。 - 传输逻辑不严谨:客户端一次性读取整个文件发送,未考虑大文件分块场景;服务端依赖
len(data) == 1024判断文件是否接收完毕,存在逻辑缺陷。 - 无同步机制:客户端发送完文件后直接发送下一个文件名,未等待服务端接收确认,服务端处理速度滞后时会导致数据边界混淆。
修复方案
核心思路是通过长度前缀明确每个数据块的边界,让服务端准确读取对应内容:
修复后的客户端代码
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
相关产品推荐
相关产品推荐

