Python Socket文件传输丢失字节问题排查求助
问题排查与修复方案
我仔细看了你的代码,问题主要出在服务器端的文件接收逻辑上,尤其是字节计数方式和循环终止条件的判断,这直接导致了文件传输时出现字节丢失、内容截断的情况。
核心问题分析
固定累加字节数,忽略实际接收长度
服务器里的bSize += 2048是错误的:client.recv(2048)并不保证每次都能收到2048字节——比如传输末尾的剩余字节可能远小于这个值,或者网络波动会导致单次接收量不足。你用固定值累加,会让bSize的计算值远大于实际收到的字节数,提前触发循环终止,导致部分数据还没写完就退出了。循环终止条件逻辑错误
你判断if bSize > fileSize就退出,但实际上应该是当累计收到的字节数等于文件大小时停止接收。当前的条件会让程序在还没接收完所有数据时就提前中断,最终导致文件体积偏小、内容缺失。未正确处理资源释放与缓冲区刷新
直接调用sys.exit()会跳过文件对象的缓冲区刷新,可能导致最后写入的部分数据丢失,同时也没有正确关闭socket和文件句柄,存在资源泄漏的风险。
修正后的服务器代码
import socket import argparse import os import sys parser = argparse.ArgumentParser() parser.add_argument("address", help='IP address of the host') parser.add_argument("port", type=int, help='Portnumber of the host') HEADERSIZE = 10 def main(): args = parser.parse_args() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind((args.address, args.port)) sock.listen(5) (client, addr) = sock.accept() print("Received a connection from ", addr) fileSizeAndName = client.recv(128) if not fileSizeAndName: print("No header received from client") client.close() sock.close() return fileSize = int(fileSizeAndName[:HEADERSIZE]) fileName = str(fileSizeAndName[HEADERSIZE:], "utf-8") print(f'Incoming file: {fileName}, size: {fileSize} bytes') # 发送头部接收确认 client.sendall(bytes("Header received", "utf-8")) # 自动创建接收目录,避免目录不存在报错 os.makedirs('received', exist_ok=True) received_bytes = 0 # 使用with语句自动管理文件,确保缓冲区刷新和文件关闭 with open(f'received\\{fileName}','wb') as f: while received_bytes < fileSize: # 计算剩余需要接收的字节数,避免最后一次接收多余空数据 chunk_size = min(2048, fileSize - received_bytes) data = client.recv(chunk_size) if not data: print("Client disconnected unexpectedly during file transfer") break f.write(data) received_bytes += len(data) # 累加实际收到的字节数 # 验证接收结果 final_size = os.path.getsize(f'received\\{fileName}') print(f'File saved successfully. Final size: {final_size} bytes') print(f'Expected size: {fileSize} bytes') # 关闭连接释放资源 client.close() sock.close() if __name__ == "__main__": main()
额外优化建议
- 增加了
received目录的自动创建,避免因为目录不存在导致文件保存失败 - 使用
with语句管理文件,自动处理缓冲区刷新和文件关闭 - 每次接收时计算剩余需要的字节数,避免不必要的空接收操作
- 增加了客户端意外断开的异常处理逻辑
- 去掉了重复的连接信息打印,让日志更简洁
客户端代码的小改进
客户端代码整体没问题,但可以加个判断,确认服务器的确认消息是否正确,避免无效传输:
data = s.recv(128) response = str(data, 'utf-8') print(response) if response == "Header received": s.sendall(contents) else: print("Server didn't confirm header reception, aborting transfer")
内容的提问来源于stack exchange,提问作者Samson Gold
相关产品推荐
相关产品推荐

