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

Python Socket通信需按CTRL-C才能完成服务端文件写入问题

故障根因

核心问题出在TCP字节流特性和代码逻辑的不匹配,附带几处显性语法/逻辑错误:

  • 核心阻塞原因:TCP传输没有消息边界,socket.recv()方法只有在对端主动关闭TCP连接时才会返回空字节。客户端发完PDF文件后没有关闭连接,直接进入等待服务端响应的逻辑,导致服务端receive_file函数里的while received := conn.recv(4096)循环永远等不到空返回值,持续阻塞在recv调用上,既不会打印接收完成提示,也不会正常关闭文件句柄——写入的文件内容一直停留在内存IO缓冲区中,只有按下Ctrl+C中断进程时,系统强制刷新缓冲区,文件才会落盘可见。
  • 其他代码错误:
    • 服务端程序入口判断错误:_name_ == '_main_'使用单下划线包裹变量,Python的入口判断必须使用双下划线包裹的特殊变量__name__ == "__main__",否则服务端启动后不会执行main函数。
    • 客户端接收逻辑类型不匹配:s.recv()返回值是bytes类型,直接和字符串"stop!"做相等判断永远不会成立;且打开练习文件的写句柄后,错误调用f.read()尝试从只写句柄读数据,会直接触发IO异常,正确逻辑应该从socket读取数据写入文件。
    • 全链路无消息边界约定:传输PDF、练习文件名、练习文件内容时都没有明确的结束标识,TCP粘包问题会导致后续消息解析完全混乱。
修复方案

采用「固定8字节文件头传文件长度 + 按长度精准读取内容」的方式划定消息边界,不需要关闭连接就能判断单份文件/消息传输完成,同时修正所有显性代码错误。
需要提前引入struct模块处理固定长度的文件大小数值,采用网络字节序8字节无符号整数(支持最大16EB文件,完全满足常规传输需求)。

修复后服务端代码(server.py)

import socket
import struct
from threading import Thread

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
HOST = socket.gethostname()
IP = socket.gethostbyname(HOST)
PORT = 4243
s.bind(('', PORT))
s.listen(5)

def receive_file(conn, i):
    f = open("exam.pdf", "wb")
    # 先读8字节文件长度
    size_data = conn.recv(8)
    file_size = struct.unpack('!Q', size_data)[0]
    received_size = 0
    while received_size < file_size:
        chunk = conn.recv(min(4096, file_size - received_size))
        if not chunk:
            raise ConnectionError("连接意外中断")
        f.write(chunk)
        received_size += len(chunk)
    print("File is fully copied\n")
    f.close()

def send_result(conn,i):
    while 1:
        nbr = str(input("which exercise? "))
        if nbr == "stop!":
            conn.send(b"stop!")
            break
        file_path = f"exercise{nbr}.txt"
        f = open(file_path, "rb")
        f.seek(0, 2)
        file_size = f.tell()
        f.seek(0)
        # 先发文件名
        conn.send(file_path.encode("utf-8"))
        # 先发8字节文件长度
        conn.send(struct.pack('!Q', file_size))
        # 再发文件内容
        while data := f.read(4096):
            conn.send(data)
        f.close()

def main():
    try:
        while 1:
            i = 0
            conn, addr = s.accept()
            print("Got connection from", addr)
            t = Thread(target=receive_file, args=(conn, i))
            t.start()
            t.join()
            t = Thread(target=send_result, args=(conn, i))
            t.start()
            t.join()
    except KeyboardInterrupt:
        print("interrupting \n")
        conn.close()
        s.close()


if __name__ == "__main__":
    main()

修复后客户端代码(client.py)

import socket
import struct
# from threading import Thread

s = socket.socket()         # Create a socket object
HOST = "101.xx.x.xxx"       # public IP address
PORT = 4243                 # Reserve a port for your service.
PDF_PATH = "exam.pdf"
s.connect((HOST, PORT))

def send():
    f = open(PDF_PATH, "rb")
    f.seek(0, 2)
    file_size = f.tell()
    f.seek(0)
    # 先发8字节文件长度
    s.send(struct.pack('!Q', file_size))
    # 再发文件内容
    while data := f.read(4096):
        s.send(data)
    f.close()
    return
    
def receive():
    while 1:
        exercise = s.recv(4096)
        if exercise == b"stop!":
            s.close()
            break
        file_name = exercise.decode("utf-8")
        f = open(file_name, "wb")
        # 读8字节文件长度
        size_data = s.recv(8)
        file_size = struct.unpack('!Q', size_data)[0]
        received_size = 0
        # 从socket读数据写文件,不要对写句柄调用read
        while received_size < file_size:
            chunk = s.recv(min(4096, file_size - received_size))
            if not chunk:
                raise ConnectionError("连接意外中断")
            f.write(chunk)
            received_size += len(chunk)
        f.close()
    return

def main():
    send()
    receive()
    
if __name__ == "__main__":
    main()
额外说明

如果不需要客户端发完文件后保持连接等待响应,也可以在客户端发完PDF后调用s.shutdown(socket.SHUT_WR)关闭写方向的连接,服务端收完PDF后会读到空字节退出循环,但这种方式后续服务端无法再给客户端发消息,不符合当前业务逻辑,所以优先使用长度头的方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 22:03:25