Python3通过TCP套接字实现无临时文件落盘的加密文件传输
Python TCP套接字无临时文件加密文件传输实现
核心思路为全程内存流式分块处理,读文件块→内存加密→直接发送,不落地临时文件,内存占用恒定,适配现有协议架构。
原有代码缺陷修复
当前代码中send_raw/recv_raw存在三个影响文件传输的核心问题:
- 强制将二进制字节转字符串解码,会损坏图片、可执行程序等非文本文件
recv_raw仅调用一次recv,TCP是流式协议,单次调用无法保证收全完整数据,会出现粘包截断- 固定缓冲区大小无法适配大尺寸数据传输
先替换这两个函数的实现,兼容二进制数据、解决粘包问题:
def send_raw(content_type, Bytes_, salt=new_salt()): seperator = b"<|SEPERATE|>" # 全程用字节处理,不做强制编解码 to_send = content_type.encode() + seperator + Bytes_ + seperator + salt.encode() if encryption: f = Fernet(key) to_send = f.encrypt(to_send) # 先发帧长度,解决粘包 frame_len = str(len(to_send)).encode() conn.send(frame_len + b"\n") conn.sendall(to_send) def recv_raw(BufferSize=4096): seperator = b"<|SEPERATE|>" # 先读帧长度 len_data = b"" while b"\n" not in len_data: len_data += conn.recv(1) frame_len = int(len_data.decode().strip()) # 按长度循环读完整帧 data = b"" while len(data) < frame_len: chunk = conn.recv(min(BufferSize, frame_len - len(data))) if not chunk: raise ConnectionError("连接断开") data += chunk if encryption: f = Fernet(key) data = f.decrypt(data) splitted = data.split(seperator, 2) # 只拆分前两个分隔符,避免内容中出现分隔符导致解析错误 content_type = splitted[0].decode() Bytes_ = splitted[1] salt = splitted[2].decode() return {"content_type": content_type, "bytes": Bytes_}
send_file指令服务端实现
直接在代码标注# CODE GOES HERE的位置填入以下逻辑,全程无临时文件写入,分块流式加密发送:
if cmd.startswith("send_file"): # 解析发送路径,支持 send_file 本地文件路径 调用语法 _, local_path = cmd.split(maxsplit=1) if not os.path.exists(local_path): print(f"错误:文件 {local_path} 不存在") continue file_name = os.path.basename(local_path) file_size = os.path.getsize(local_path) # 先发文件元信息控制帧,通知客户端准备接收 meta = json.dumps({ "file_name": file_name, "file_size": file_size }).encode() send_raw("file_start", meta) # 等待客户端就绪确认 ack = recv_raw() if ack["content_type"] != "file_ack": print("客户端未就绪,取消传输") continue # 分块读文件、内存加密、直接发送,块大小设为1MB,平衡内存占用和传输效率 chunk_size = 1024 * 1024 sent = 0 with open(local_path, "rb") as f: while True: chunk = f.read(chunk_size) if not chunk: break send_raw("file_chunk", chunk) sent += len(chunk) print(f"\r传输进度:{sent/file_size*100:.1f}%", end="") # 发送结束帧 send_raw("file_end", b"") # 等待客户端接收完成确认 finish_ack = recv_raw() if finish_ack["content_type"] == "file_complete": print(f"\n文件 {file_name} 发送完成") else: print(f"\n文件传输异常") # 跳过后续普通命令发送逻辑 continue
客户端对应适配逻辑
需要在客户端消息处理循环里新增三类消息的处理,同样全程内存操作,不产生临时文件:
- 收到
file_start帧:解析文件名、大小,回传file_ack确认,初始化接收字节计数器 - 收到
file_chunk帧:把解密后的块直接写入目标保存路径,更新接收进度 - 收到
file_end帧:校验接收总字节数和元信息的文件大小是否一致,回传file_complete确认
方案优势
- 全程无临时磁盘写入,不会占用额外磁盘空间
- 分块处理时内存占用恒定为1MB左右,传输几十GB级大文件也不会出现内存溢出
- 完全复用现有Fernet加密逻辑,所有文件数据全程密文传输
- 修复了原有代码的粘包、二进制兼容问题,普通命令执行、信息采集功能不受影响
内容的提问来源于stack exchange,提问作者Ryder R
相关产品推荐
相关产品推荐

