用 Python 写一个简易文件传输器(含多实例 + 项目实战 + 调试技巧)

一、基础准备:Socket 是怎么传文件的?

文件怎么传?思想只有一句话:

读取文件 → 发送字节流 → 对端接收 → 写入文件

换句话说,文件传输就是数据流的搬运。

1. 正面示例:最小可运行 socket 程序

服务器(server.py):

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("0.0.0.0", 9000))
server.listen(1)

print("等待客户端连接...")
conn, addr = server.accept()
print(f"客户端已连接:{addr}")

conn.send(b"Hello from server")
conn.close()
server.close()

客户端(client.py):

import socket

client = socket.socket()
client.connect(("127.0.0.1", 9000))

data = client.recv(1024)
print("收到:", data)

client.close()

这个版本能跑,但距离“文件传输器”还有若干星系的距离。

2. 错误示例:新手最常见的发送 bug

f = open("a.jpg", "rb")
data = f.read()
sock.send(data)   # ❌ 看起来没问题,其实大问题!

原因:
send() 不保证送完全部数据,需要循环发送:

sock.sendall(data)

或手动循环:

while True:
    chunk = f.read(4096)
    if not chunk:
        break
    sock.send(chunk)

调试技巧:使用 netstat 或 tcpdump 查看连接是否建立

Linux/macOS:

netstat -an | grep 9000

Windows Powershell:

netstat -ano | findstr 9000

看到 LISTENINGESTABLISHED 才说明 socket 工作正常。


二、项目实战:构建可传任意文件的文件传输器

接下来我们写一个能完整运行、可传任意文件的“简易文件传输器”。

项目结构

file_transfer/
    server.py
    client.py

三、服务器端:支持多线程、多用户同时上传文件

关键技术点:

  • 每来一个客户端连接,就开启一个线程处理它
  • 先收文件大小,再收文件内容
  • 循环接收,直到收满为止

server.py 完整示例

import socket
import threading
import os

def handle_client(conn, addr):
    print(f"[+] 客户端连接:{addr}")

    # 1. 接收文件名长度
    filename_len = int(conn.recv(8).decode().strip())
    filename = conn.recv(filename_len).decode()

    # 2. 接收文件大小
    filesize = int(conn.recv(16).decode().strip())
    print(f"开始接收文件:{filename} 大小:{filesize} bytes")

    # 3. 接收文件内容
    received = 0
    with open("recv_" + filename, "wb") as f:
        while received < filesize:
            data = conn.recv(4096)
            if not data:
                break
            f.write(data)
            received += len(data)
            print(f"进度:{received}/{filesize}", end="\r")

    print(f"\n文件接收完成:{filename}")
    conn.close()


def main():
    server = socket.socket()
    server.bind(("0.0.0.0", 9000))
    server.listen(5)
    print("服务器已启动,等待客户端上传文件...")

    while True:
        conn, addr = server.accept()
        t = threading.Thread(target=handle_client, args=(conn, addr))
        t.start()


if __name__ == "__main__":
    main()

四、客户端:上传文件到服务器

client.py 完整示例

import socket
import os

def send_file(path):
    client = socket.socket()
    client.connect(("127.0.0.1", 9000))

    filename = os.path.basename(path)
    filesize = os.path.getsize(path)

    # 1. 发送文件名长度(8字节对齐)
    client.send(f"{len(filename):<8}".encode())
    client.send(filename.encode())

    # 2. 发送文件大小(16字节对齐)
    client.send(f"{filesize:<16}".encode())

    # 3. 发送文件内容
    with open(path, "rb") as f:
        while True:
            data = f.read(4096)
            if not data:
                break
            client.sendall(data)

    client.close()
    print("文件发送完毕")

send_file("test.jpg")  # 发送任意文件

五、错误示例:可能让文件损坏的坑

坑 1:用 recv(1024) 接收文件大小,结果被拆包

原因:TCP 是字节流协议,没有消息边界,所以:

recv(16)

可能一次收不到 16 字节。

解决方法:要自己写“固定长度报文”或“分段协议”。

坑 2:未 flush 缓冲区导致文件不完整

如果写:

f.write(data)

但未及时关闭文件,内容未写入磁盘。

解决:使用 with open(...) 自动 flush 与 close。

坑 3:客户端单线程会卡住 GUI 或其他逻辑

解决:客户端发送操作独立线程化。


六、调试技巧:文件收不到怎么办?

常见问题与排查方式:

  1. 是否端口被占用 → 用 netstat 查
  2. 是否传输过程中卡住 → 打印接收字节计数
  3. 文件大小不一致 → 可能粘包或 recv 裁剪
  4. 是否 firewall 阻塞 → 关闭系统防火墙测试
  5. recv 返回空字符串 → 对端断开连接

把这些排查写进调试日志,非常有用:

print(f"[DEBUG] recv len={len(data)}")

七、高级技巧:让你的传输器更专业

以下技巧非常适合课程设计、CSDN 进阶文章、真实项目。

1. 进度条显示

可以用 tqdm:

from tqdm import tqdm

2. 断点续传 (resume upload)

实现思路:

  1. 服务器检查 recv_xxx.file 是否存在
  2. 如果文件已经有部分长度,告知客户端 resume_offset
  3. 客户端从 offset 开始读取并传输

这是简易 FTP 的核心。

3. 传输校验(MD5)

防止传输损坏:

import hashlib

md5 = hashlib.md5(open("file").read()).hexdigest()

客户端和服务器都计算 md5,对比即可。

4. 大文件分块传输

对于几GB的大文件,不能一次性读取,用 chunk 处理更稳。


八、工作中的实际应用

这个项目虽然简易,但非常接近实际企业生产环境中的:

  • 云服务之间的文件同步
  • 日志系统把文件 push 到 Log Server
  • 大模型训练时传数据集
  • IoT 设备上传数据文件
  • DevOps 工具中收集机器快照

若你未来从事 AIOps 或云计算方向,文件传输、流控、可靠传输机制都会非常重要。


九、拓展:背后的网络原理

学习 socket 传文件时,建议顺带理解一些核心概念:

1. TCP 粘包 / 拆包

原因:TCP 不保证一发一收,需要自定义协议。

2. 三次握手 / 四次挥手

连接建立与断开的基本过程。

3. 滑动窗口

TCP 提升性能的关键。

4. 流控与拥塞控制

保障网络不会被拖垮。

这些原理并不是必须掌握,但会让你写出的文件传输器更稳、更快、更专业。


AI 创作声明:本文部分内容由 AI 辅助生成,并经人工整理与验证,仅供参考学习,欢迎指出错误与不足之处。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐