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()
问题根源与解决方法
问题根源
- TCP流无边界:TCP是字节流协议,客户端发送完一个文件后,没有明确告知服务端“该文件已传输完成”,服务端会一直等待后续数据,而客户端还要继续传下一个文件,不会关闭连接,导致服务端卡在
recv调用上。 - 循环逻辑错误:服务端外层循环的退出条件依赖内层循环最后一次
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
相关产品推荐
相关产品推荐

