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

Python Socket文件传输丢失字节问题排查求助

问题排查与修复方案

我仔细看了你的代码,问题主要出在服务器端的文件接收逻辑上,尤其是字节计数方式和循环终止条件的判断,这直接导致了文件传输时出现字节丢失、内容截断的情况。

核心问题分析

  1. 固定累加字节数,忽略实际接收长度
    服务器里的bSize += 2048是错误的:client.recv(2048)并不保证每次都能收到2048字节——比如传输末尾的剩余字节可能远小于这个值,或者网络波动会导致单次接收量不足。你用固定值累加,会让bSize的计算值远大于实际收到的字节数,提前触发循环终止,导致部分数据还没写完就退出了。

  2. 循环终止条件逻辑错误
    你判断if bSize > fileSize就退出,但实际上应该是当累计收到的字节数等于文件大小时停止接收。当前的条件会让程序在还没接收完所有数据时就提前中断,最终导致文件体积偏小、内容缺失。

  3. 未正确处理资源释放与缓冲区刷新
    直接调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:13:51