Python Socket多文件传输失效问题求助(单文件可正常运行)
多文件Socket传输功能修复
问题说明
单文件传输功能正常,但多文件传输失效,项目为服务端向客户端发送文件,Tkinter仅用于获取选中文件路径。
原服务端代码
import socket import os import tkinter as tk from tkinter import filedialog as fd SERVER = socket.gethostbyname(socket.gethostname()) PORT = 1234 HEADER = 10 WINDOW_X = 600 WINDOW_Y = 350 root = tk.Tk() root.title('MyCoolProgram') root.resizable(False, False) root.geometry(f'{WINDOW_X}x{WINDOW_Y}') print(f'Binding {SERVER} with port {PORT}...') s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((SERVER, PORT)) print(f'Listening...') s.listen(5) def send(files): con, addr = s.accept() print('Connected with ', addr) filedata = b'' for file in files: head, tail = os.path.split(file) os.chdir(head) filename = tail filename = f'{len(filename):<{HEADER}}' + filename con.send(filename.encode()) filesize = os.stat(tail).st_size filesize = f'{filesize:<{HEADER}}' filesize = filesize.encode() with open(tail, 'rb') as f: content = f.read() filedata += filesize + content + b'\0' con.send(filedata) con.close() files = '' T = tk.Text(root, height=18, width=73) T.place(x=5, y=10) T.config(state='disabled') def openfiles(): global files files = fd.askopenfilenames(parent=root, title='选择要发送的文件') T.config(state='normal') for file in files: T.insert('end',file + '\n') T.config(state='disabled') def sendloop(): global files send(files) open_files_button = tk.Button(root, command=openfiles, text="选择文件", height=2, padx=7) open_files_button.place(x=WINDOW_X - 130 , y = WINDOW_Y - 45) send_files_button = tk.Button(root, command=sendloop, text="发送", height=2, padx=27) send_files_button.place(x=WINDOW_X - 230 , y = WINDOW_Y - 45) root.mainloop()
原客户端代码
import socket import tqdm HEADER = 10 SERVER = "服务器公网IP" # 实际代码中为真实IP BUFFER = 1024 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((SERVER, 1234)) print(f'已连接到 {SERVER}.') while True: full_data = b'' new = True name_data = [] while True: data = s.recv(BUFFER) if new: datalen = int(data[:HEADER]) new = False progress = tqdm.tqdm(unit='B',unit_scale=True, unit_divisor=1000, total=int(datalen)) full_data += data progress.update(BUFFER) if len(full_data)-HEADER == datalen: acc_data = full_data[HEADER:] name_data.append(acc_data) new = True full_data = b'' if len(name_data) == 2: with open(name_data[0], 'wb') as f: f.write(name_data[1]) print(f'{name_data[0]} 已接收' ) name_data = [] new = True full_data = b''
问题分析
服务端核心问题
- 连接提前关闭:循环发送文件时,每发送一个文件就调用
con.close(),导致第一个文件发送完成后连接断开,后续文件无法传输。 - 数据包逻辑混乱:
filedata变量累加所有文件数据,且每次发送不重置,导致数据包包含冗余内容;同时文件名和文件数据的发送顺序与客户端接收逻辑不匹配。 - 切换目录风险:循环中调用
os.chdir(head),若多个文件来自不同目录,会导致后续文件路径解析错误。
客户端核心问题
- 进度条逻辑错误:首次接收时用文件名长度作为进度条总长度,后续文件大小的处理逻辑缺失,导致进度条显示混乱。
- 多文件边界无标识:没有处理多文件传输结束的信号,客户端会一直等待数据,陷入死循环。
- 数据解析逻辑模糊:仅通过
name_data长度判断是否完成一个文件的接收,没有明确区分文件名和文件内容的接收阶段。
修复后的代码
修复后服务端代码
import socket import os import tkinter as tk from tkinter import filedialog as fd SERVER = socket.gethostbyname(socket.gethostname()) PORT = 1234 HEADER = 10 WINDOW_X = 600 WINDOW_Y = 350 root = tk.Tk() root.title('MyCoolProgram') root.resizable(False, False) root.geometry(f'{WINDOW_X}x{WINDOW_Y}') print(f'绑定 {SERVER}:{PORT}...') s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((SERVER, PORT)) s.listen(5) def send(files): con, addr = s.accept() print('已连接客户端:', addr) # 先发送文件总数,让客户端知道要接收多少个文件 file_count = f'{len(files):<{HEADER}}' con.send(file_count.encode()) for file in files: head, tail = os.path.split(file) filename = tail # 发送文件名(带长度头) filename_len = f'{len(filename):<{HEADER}}' con.send(filename_len.encode() + filename.encode()) # 获取文件大小并发送(带长度头) filesize = os.path.getsize(file) filesize_str = f'{filesize:<{HEADER}}' con.send(filesize_str.encode()) # 分块发送文件内容,避免大文件一次性读取内存溢出 with open(file, 'rb') as f: while True: chunk = f.read(1024) if not chunk: break con.send(chunk) print(f'已发送文件: {filename}') # 发送结束标识,告诉客户端所有文件已发送完成 con.send(b'END_TRANSFER') con.close() print('传输完成,连接已关闭') files = () T = tk.Text(root, height=18, width=73) T.place(x=5, y=10) T.config(state='disabled') def openfiles(): global files files = fd.askopenfilenames(parent=root, title='选择要发送的文件') T.config(state='normal') T.delete(1.0, tk.END) for file in files: T.insert('end', file + '\n') T.config(state='disabled') def sendloop(): global files if files: send(files) else: print('请先选择要发送的文件') open_files_button = tk.Button(root, command=openfiles, text="选择文件", height=2, padx=7) open_files_button.place(x=WINDOW_X - 130 , y = WINDOW_Y - 45) send_files_button = tk.Button(root, command=sendloop, text="发送", height=2, padx=27) send_files_button.place(x=WINDOW_X - 230 , y = WINDOW_Y - 45) root.mainloop()
修复后客户端代码
import socket import tqdm HEADER = 10 SERVER = "服务器公网IP" # 替换为真实服务器IP BUFFER = 1024 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((SERVER, 1234)) print(f'已连接到 {SERVER}') # 先接收文件总数 file_count_data = s.recv(HEADER) file_count = int(file_count_data.strip()) print(f'即将接收 {file_count} 个文件') for _ in range(file_count): # 接收文件名长度和文件名 filename_len_data = s.recv(HEADER) filename_len = int(filename_len_data.strip()) filename = s.recv(filename_len).decode() # 接收文件大小 filesize_data = s.recv(HEADER) filesize = int(filesize_data.strip()) # 接收文件内容并写入本地 progress = tqdm.tqdm(total=filesize, unit='B', unit_scale=True, unit_divisor=1000, desc=filename) received_size = 0 with open(filename, 'wb') as f: while received_size < filesize: chunk = s.recv(min(BUFFER, filesize - received_size)) if not chunk: break f.write(chunk) received_size += len(chunk) progress.update(len(chunk)) progress.close() print(f'{filename} 接收完成') # 接收结束标识 end_signal = s.recv(16).decode() if end_signal == 'END_TRANSFER': print('所有文件接收完成') s.close()
修复要点说明
服务端:
- 先发送文件总数,让客户端明确接收任务量
- 每个文件单独按「文件名长度头→文件名→文件大小头→文件内容」的顺序发送
- 分块读取发送大文件,避免内存溢出
- 所有文件发送完成后发送结束标识,最后关闭连接
- 移除
os.chdir,直接使用文件完整路径读取,避免目录切换错误
客户端:
- 先接收文件总数,循环对应次数接收每个文件
- 明确区分文件名、文件大小、文件内容的接收阶段
- 进度条使用真实文件大小作为总长度,显示更准确
- 接收完成后等待结束标识,然后关闭连接,避免死循环
内容的提问来源于stack exchange,提问作者yut
相关产品推荐
相关产品推荐

