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

如何在Python 3中基于服务器中转实现客户端间文件传输

Hey there! Let's tackle this file transfer feature for your client-server chat app. I see you've already got the basic client communication working, so we'll build on that with a clear protocol for file transfers and tweak your existing functions to handle input paths, chunked sending, and proper file reassembly.

Step 1: Define a File Transfer Protocol

First, we need a way to distinguish regular chat messages from file transfer data. Let's use a simple pipe-separated format to avoid confusion:

  • For file content chunks: FILE_CHUNK|{filename}|{content}|{signature}
  • To signal the end of a file: FILE_END|{filename}

This helps the receiving client recognize when it's receiving a file, track its name, and know when to stop writing to disk.

Step 2: Update the Client's Send Logic

Modify the send function to detect when the user enters a file transfer command (like /send /path/to/your/file.txt). We'll parse the input path, validate the file exists, then trigger the updated send_file function.

Step 3: Improve the send_file Function

Instead of reading line-by-line (which breaks for files without newlines), we'll read in chunks matching your BUFSIZ value. We'll also attach the protocol headers and your signature to each chunk to maintain integrity.

Step 4: Enhance the receive Function to Handle Files

Update the receive loop to check for our file transfer markers. When a chunk comes in, we'll start writing to the specified file, and when we get the FILE_END signal, we'll close the file and notify the user.

Modified Client Code

Here's the updated client script with all these changes integrated:

#!/usr/bin/env python3
"""Script for Tkinter GUI chat client with file transfer."""
from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread
import tkinter
import os
# Replace this with your actual signing key initialization
signing_key = None  # Example: signing_key = ed25519.SigningKey(b"your_private_key_here")

def receive():
    """Handles receiving of messages and file transfers."""
    current_file = None
    current_filename = None
    while True:
        try:
            msg = client_socket.recv(BUFSIZ).decode("utf8")
            # Handle file chunks
            if msg.startswith("FILE_CHUNK|"):
                _, filename, content, signature = msg.split("|", 3)
                # Optional: Add your signature verification logic here
                # if not verify_signature(content, signature):
                #     msg_list.insert(tkinter.END, f"⚠️ Invalid signature for {filename}")
                #     continue
                if not current_file:
                    current_filename = filename
                    current_file = open(filename, "wb")  # Open in binary mode for broader support
                current_file.write(content.encode("utf8", errors="replace"))
            # Handle end of file transfer
            elif msg.startswith("FILE_END|"):
                _, filename = msg.split("|", 1)
                if current_file:
                    current_file.close()
                    current_file = None
                    msg_list.insert(tkinter.END, f"✅ File {filename} received successfully!")
            # Handle regular chat messages
            else:
                msg_list.insert(tkinter.END, msg)
        except OSError:
            # Clean up if client disconnects mid-transfer
            if current_file:
                current_file.close()
            break
        except Exception as e:
            msg_list.insert(tkinter.END, f"❌ Error receiving file: {str(e)}")
            if current_file:
                current_file.close()
                current_file = None

def send(event=None):
    """Handles sending of messages and file transfer commands."""
    msg_input = my_msg.get()
    my_msg.set("")  # Clear input field
    # Check for file transfer command
    if msg_input.startswith("/send "):
        parts = msg_input.split(maxsplit=1)
        if len(parts) != 2:
            msg_list.insert(tkinter.END, "⚠️ Usage: /send <file_path>")
            return
        file_path = parts[1]
        # Validate file path
        if not os.path.exists(file_path):
            msg_list.insert(tkinter.END, f"⚠️ File {file_path} does not exist!")
            return
        if not os.path.isfile(file_path):
            msg_list.insert(tkinter.END, f"⚠️ {file_path} is not a valid file!")
            return
        # Notify chat about the file transfer
        client_socket.send(bytes(f"📤 Sending file: {os.path.basename(file_path)}", "utf8"))
        send_file(file_path)
    else:
        # Send regular chat message
        client_socket.send(bytes(msg_input, "utf8"))
        if msg_input == "{quit}":
            client_socket.close()
            top.quit()

def on_closing(event=None):
    """Triggered when the window is closed."""
    my_msg.set("{quit}")
    send()

def send_file(file_path):
    """Sends a file in chunks with protocol headers and signatures."""
    filename = os.path.basename(file_path)
    try:
        with open(file_path, "rb") as file:
            while True:
                # Calculate chunk size to fit within BUFSIZ after adding headers
                max_chunk_size = BUFSIZ - len(f"FILE_CHUNK|{filename}||".encode("utf8"))
                chunk = file.read(max_chunk_size)
                if not chunk:
                    break
                content = chunk.decode("utf8", errors="replace")
                # Generate signature for the chunk
                signature = signing_key.sign(content.encode(), encoding="hex").decode()
                # Build and send the chunk message
                chunk_msg = f"FILE_CHUNK|{filename}|{content}|{signature}"
                client_socket.send(bytes(chunk_msg, "utf8"))
        # Send end signal after all chunks are sent
        client_socket.send(bytes(f"FILE_END|{filename}", "utf8"))
    except Exception as e:
        msg_list.insert(tkinter.END, f"❌ Error sending file: {str(e)}")

top = tkinter.Tk()
top.title("Chatter with File Transfer")

messages_frame = tkinter.Frame(top)
my_msg = tkinter.StringVar()
scrollbar = tkinter.Scrollbar(messages_frame)
msg_list = tkinter.Listbox(messages_frame, height=15, width=50, yscrollcommand=scrollbar.set)
scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
msg_list.pack()
messages_frame.pack()

entry_field = tkinter.Entry(top, textvariable=my_msg)
entry_field.bind("<Return>", send)
entry_field.pack()

send_button = tkinter.Button(top, text="Send", command=send)
send_button.pack()

top.protocol("WM_DELETE_WINDOW", on_closing)

# Socket setup
HOST = input('Enter host: ')
PORT = input('Enter port: ')
if not PORT:
    PORT = 33000
else:
    PORT = int(PORT)

BUFSIZ = 1024
ADDR = (HOST, PORT)

client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(ADDR)

receive_thread = Thread(target=receive)
receive_thread.start()

tkinter.mainloop()

Key Changes Explained

  • Protocol Markers: FILE_CHUNK and FILE_END let the client differentiate file data from regular messages.
  • Chunked Reading: Uses read(BUFSIZ) instead of readline() to support all file types (including those without newlines).
  • Input Validation: Checks if the specified file exists and is a valid file before attempting transfer.
  • File Reassembly: Tracks the active file being received, writes chunks to disk, and cleans up when the transfer ends.
  • Error Handling: Adds safeguards for missing files, transfer interruptions, and general read/write errors.

Quick Notes

  • Make sure to initialize your signing_key with your actual private key (I left a placeholder for this).
  • For true binary file support (like images or executables), encode chunks in base64 before sending and decode them on receipt—this avoids issues with non-UTF8 bytes.
  • Your existing server code doesn't need any changes—it will automatically forward file chunks to all connected clients. If you want to send files to specific users, you'd need to add user targeting logic to the protocol and server.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:20:06