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

Python Socket编程服务端传输多视频时阻塞问题求助

问题:多视频文件Socket传输时服务端阻塞

单次传输1个视频可正常运行,但传输2个及以上视频时,服务端会阻塞在conn.recv(SIZE)处,无法触发空数据判断终止循环。

服务端代码

import socket
import os

IP = "127.0.0.1"
PORT = 4456
SIZE = 1024
FORMAT = "utf"
SERVER_FOLDER = "video_folder"


def main():
    print("[STARTING] Server is starting.\n")
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((IP, PORT))
    server.listen()
    print("[LISTENING] Server is waiting for clients.")

    conn, addr = server.accept()
    print(f"[NEW CONNECTION] {addr} connected.\n")

    """ Receiving the folder_name """
    folder_name = conn.recv(SIZE).decode(FORMAT)

    """ Creating the folder """
    folder_path = os.path.join(SERVER_FOLDER, folder_name)
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)
        conn.send(f"Folder ({folder_name}) created.".encode(FORMAT))
    else:
        conn.send(f"Folder ({folder_name}) already exists.".encode(FORMAT))

    """ Receiving files """
    while True:
        msg = conn.recv(SIZE).decode(FORMAT)

        """ Recv the file name """
        print(f"[CLIENT] Received the filename: {msg}.")
        file_path = os.path.join(folder_path, msg)
        file = open(file_path, "wb")
        conn.send(f"{file_path} filename received.".encode(FORMAT))

        while True:
            msg = conn.recv(SIZE)  # 阻塞位置
            if not msg:
                conn.send("The data is saved.".encode(FORMAT))
                break

            file.write(msg)
            print('.', end='', flush=True)

        if not msg: break

    file.close()
    print("\ndone.")


def test():
    while True:
        x = "World"
        print("morning")
        while True:
            print("Hello" + x)
            break


if __name__ == "__main__":
    main()

客户端代码

import socket
import os

IP = "127.0.0.1"
PORT = 4456
SIZE = 1024
FORMAT = "utf"
CLIENT_FOLDER = "C:/Users/wende/OneDrive/Desktop/client_folder"


def main():
    """ Staring a TCP socket. """
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((IP, PORT))

    """ Folder path """
    path = os.path.join(CLIENT_FOLDER, "files")
    folder_name = path.split("/")[-1]

    """ Sending the folder name """
    msg = f"{folder_name}"
    print(f"[CLIENT] Sending folder name: {folder_name}")
    client.send(msg.encode(FORMAT))

    """ Receiving the reply from the server """
    msg = client.recv(SIZE).decode(FORMAT)
    print(f"[SERVER] {msg}\n")

    """ Sending files """
    files = sorted(os.listdir(path))

    for file_name in files:
        """ Send the file name """
        print(f"[CLIENT] Sending file name: {file_name}")
        client.send(file_name.encode(FORMAT))

        """ Recv the reply from the server """
        msg = client.recv(SIZE).decode(FORMAT)
        print(f"[SERVER] {msg}")

        """ Send the data """
        file = open(os.path.join(path, file_name), "rb")
        file_data = file.read()
        client.send(file_data)
        print("Sending File...")
        msg = client.recv(SIZE).decode(FORMAT)
        print(f"[SERVER] {msg}")


if __name__ == "__main__":
    main()

问题根源与解决方法

问题根源

  1. TCP流无边界:TCP是字节流协议,客户端发送完一个文件后,没有明确告知服务端“该文件已传输完成”,服务端会一直等待后续数据,而客户端还要继续传下一个文件,不会关闭连接,导致服务端卡在recv调用上。
  2. 循环逻辑错误:服务端外层循环的退出条件依赖内层循环最后一次recv的空数据,但只有客户端关闭连接时recv才会返回空,客户端传完第一个文件后还会发送第二个文件名,这个条件永远不会触发。

修复步骤

1. 客户端:先传文件大小,再传文件内容

给每个文件添加“大小标记”,让服务端知道需要接收多少数据:

""" Sending files """
files = sorted(os.listdir(path))

for file_name in files:
    """ Send the file name """
    print(f"[CLIENT] Sending file name: {file_name}")
    client.send(file_name.encode(FORMAT))

    """ Recv the reply from the server """
    msg = client.recv(SIZE).decode(FORMAT)
    print(f"[SERVER] {msg}")

    """ Send the data """
    file_path = os.path.join(path, file_name)
    file = open(file_path, "rb")
    # 先发送文件字节大小
    file_size = os.path.getsize(file_path)
    client.send(str(file_size).encode(FORMAT))
    # 等待服务端确认大小接收
    client.recv(SIZE)
    # 发送文件内容
    file_data = file.read()
    client.send(file_data)
    print("Sending File...")
    msg = client.recv(SIZE).decode(FORMAT)
    print(f"[SERVER] {msg}")
    file.close()

# 所有文件发送完毕,发送结束标记
client.send("END".encode(FORMAT))
client.close()

2. 服务端:按文件大小接收数据,处理结束标记

修改服务端接收文件的逻辑,先拿大小再按需接收,同时识别结束标记退出循环:

""" Receiving files """
while True:
    msg = conn.recv(SIZE).decode(FORMAT)
    # 收到结束标记则退出循环
    if msg == "END":
        break

    """ Recv the file name """
    print(f"[CLIENT] Received the filename: {msg}.")
    file_path = os.path.join(folder_path, msg)
    file = open(file_path, "wb")
    conn.send(f"{file_path} filename received.".encode(FORMAT))

    # 先接收文件大小
    file_size = int(conn.recv(SIZE).decode(FORMAT))
    conn.send("File size received.".encode(FORMAT))

    # 按指定大小接收文件内容
    received_size = 0
    while received_size < file_size:
        data = conn.recv(SIZE)
        if not data:
            break
        file.write(data)
        received_size += len(data)
        print('.', end='', flush=True)

    conn.send("The data is saved.".encode(FORMAT))
    file.close()

print("\ndone.")
conn.close()

3. 其他细节优化

  • 及时关闭文件和socket连接,避免资源泄漏;
  • 可以给文件名、文件大小的传输添加固定长度的头部,防止数据截断导致解码错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 14:35:12