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

Python Sockets问题:客户端请求指定文件后接收阶段挂起

我之前也踩过这个Socket文件传输的坑!客户端收完文件还挂着,本质就是客户端不知道什么时候该停止接收数据,一直在recv()那里阻塞等待。咱们一步步来解决这个问题,先分析核心原因,再给出修正后的代码。

核心问题:TCP流的“边界模糊”

TCP是基于流的协议,不像UDP有数据包边界。客户端的recv(1024)会一直阻塞,直到收到数据或者连接关闭。如果服务器发送完文件后没明确告诉客户端“我发完了”,客户端就会傻等下去。

常见的解决方案有两种:

方案1:先发送文件大小,客户端按大小接收(推荐,支持长连接)

这种方式最可靠,客户端知道要接收多少字节,收够就停止,不用依赖连接关闭。

修改后的server.py

import socket
import os

def main():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('localhost', 12345))
    server_socket.listen(1)
    print("等待客户端连接...")
    conn, addr = server_socket.accept()
    print(f"连接来自 {addr}")

    # 接收客户端发送的文件名
    filename = conn.recv(1024).decode('utf-8')
    print(f"请求文件: {filename}")

    if os.path.exists(filename):
        # 第一步:发送文件大小给客户端
        file_size = os.path.getsize(filename)
        conn.send(str(file_size).encode('utf-8'))
        # 等待客户端确认(防止粘包,可选但更严谨)
        conn.recv(1024)
        
        # 第二步:分块发送文件内容
        with open(filename, 'rb') as f:
            while True:
                chunk = f.read(1024)
                if not chunk:
                    break
                conn.send(chunk)
        print("文件发送完成")
    else:
        # 发送特殊标记表示文件不存在
        conn.send(b'-1')
        print("文件不存在")
    
    conn.close()
    server_socket.close()

if __name__ == "__main__":
    main()

修改后的client.py

import socket

def main():
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect(('localhost', 12345))

    # 发送要请求的文件名
    filename = input("请输入要请求的文件名: ")
    client_socket.send(filename.encode('utf-8'))

    # 第一步:接收文件大小
    file_size_str = client_socket.recv(1024).decode('utf-8')
    if file_size_str == '-1':
        print("服务器上不存在该文件")
        client_socket.close()
        return
    
    file_size = int(file_size_str)
    # 发送确认,告诉服务器可以开始发文件了
    client_socket.send(b'OK')

    # 第二步:按大小接收文件内容
    received_bytes = 0
    save_filename = f'received_{filename}'
    with open(save_filename, 'wb') as f:
        while received_bytes < file_size:
            # 每次接收的字节数不超过剩余需要接收的量,避免冗余
            chunk_size = min(1024, file_size - received_bytes)
            data = client_socket.recv(chunk_size)
            if not data:
                break  # 极端情况:连接中途断开
            f.write(data)
            received_bytes += len(data)
            print(f"已接收: {received_bytes}/{file_size} 字节")
    
    print(f"文件接收完成,已保存为 {save_filename}")
    client_socket.close()

if __name__ == "__main__":
    main()

方案2:服务器发送完文件后主动关闭连接(适合短连接)

如果你的场景是一次连接只传一个文件,也可以让服务器发送完文件后直接关闭连接,客户端收到空数据(recv()返回b'')就停止接收:

简化版server.py(仅修改发送文件部分)

# ... 前面代码不变 ...
with open(filename, 'rb') as f:
    while True:
        chunk = f.read(1024)
        if not chunk:
            break
        conn.send(chunk)
print("文件发送完成")
conn.close()  # 发送完就关闭连接
# ... 后面代码不变 ...

简化版client.py(接收部分)

# ... 前面代码不变 ...
save_filename = f'received_{filename}'
with open(save_filename, 'wb') as f:
    while True:
        data = client_socket.recv(1024)
        if not data:  # 连接关闭时会返回空数据
            break
        f.write(data)
print(f"文件接收完成,已保存为 {save_filename}")
client_socket.close()

关键注意点
  • 粘包问题:方案1里的“客户端确认”步骤是为了避免文件大小和文件内容粘在一起(比如服务器连续发送file_size和文件内容,客户端一次recv()可能把两者都收了)。如果你的文件很小,也可以忽略,但加上更稳妥。
  • 异常处理:实际项目中建议加上try-except块,处理连接中断、文件读写错误等情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 06:53:03