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

FTP上传大文件报[WinError 10054]远程主机强制关闭连接 小文件正常

FTP大文件上传触发10054连接强制关闭故障排查

故障现象

  • 小体积文件可正常上传,服务端返回226 文件存储成功响应,全流程走完后可正常收到221退出响应
  • 上传约200MB、耗时15分钟左右的大文件时,即便观测到文件已上传完成,Python脚本的try/except逻辑会触发报错中断流程,报错信息如下:
[WinError 10054] An existing connection was forcibly closed by the remote host
  • 同个大文件用FileZilla上传也会失败:传输约90MB、耗时303秒时触发ECONNABORTED连接中断,自动重试后仍反复提示被动响应地址不可路由,无法完成上传。

现有实现与已尝试方案

此前参考相关方案自定义了SmartFTP类,修复FTP被动模式返回无效主机地址的问题,才实现小文件基础上传功能,当前使用的上传脚本如下:

class SmartFTP(FTP):
    def makepasv(self):
        invalidhost, port = super(SmartFTP, self).makepasv()
        return self.host, port


try:
    for file_name in os.listdir(fullPath):
        if file_name == 'file.csv':
            
            file_path = fullPath + "\\" + file_name
            print('Moving File - ' + file_path + ': ' + str(datetime.now()))
            
            with SmartFTP(FTP_HOST, FTP_USER, FTP_PASS, timeout=2700) as ftp, open(file_path, 'rb') as file:
                ftp.set_debuglevel(2)
                print('here1')
                test = ftp.pwd()
                print('here2')
                ftp.storbinary(f'STOR {file_name}', file)
                print('here3')
                print('FTP Move Completed')
                

except Exception as e:
    print("FTP and File Move process failed: " + str(e))

已尝试的排查操作均未解决问题:

  • 调整连接超时时间到2700秒
  • 切换FTP主动/被动模式(切主动模式后进程无限阻塞无响应)
  • 修复被动模式返回的无效主机地址问题

日志对比

大文件上传异常日志

传输持续约15分钟后,未收到226传输完成响应,直接触发连接关闭报错:

Moving File - file.csv: 2022-07-12 15:27:35.932522

here1

*cmd* 'PWD'

*put* 'PWD\r\n'

*get* '257 "/folder"\n'
*resp* '257 "/folder"'
here2
*cmd* 'TYPE I'
*put* 'TYPE I\r\n'
*get* '200 Transfer Mode: BINARY\n'
*resp* '200 Transfer Mode: BINARY'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering PASV Mode (numbers)\n'
*resp* '227 Entering PASV Mode (numbers)'
*cmd* 'STOR file.csv'
*put* 'STOR file.csv\r\n'
*get* '150 Connecting Data Port...\n'
*resp* '150 Connecting Data Port...'

Then it loads for about 15 minutes

*cmd* 'QUIT'
*put* 'QUIT\r\n'
FTP and File Move process failed: [WinError 10054] An existing connection was forcibly closed by the remote host

小文件上传正常日志

可正常收到226传输完成响应、221退出响应,全流程无异常:

Moving File - file.csv: 2022-07-12 15:50:39.489980
here1
*cmd* 'PWD'
*put* 'PWD\r\n'
*get* '257 "/Atlas_In"\n'
*resp* '257 "/Atlas_In"'
here2
*cmd* 'TYPE I'
*put* 'TYPE I\r\n'
*get* '200 Transfer Mode: BINARY\n'
*resp* '200 Transfer Mode: BINARY'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering PASV Mode (numbers)\n'
*resp* '227 Entering PASV Mode (numbers)'
*cmd* 'STOR file.csv'
*put* 'STOR file.csv\r\n'
*get* '150 Connecting Data Port...\n'
*resp* '150 Connecting Data Port...'
*get* '226 Stored Message: [file.csv] Byte Count: [57824] bytes\n'
*resp* '226 Stored Message: [file.csv] Byte Count: [57824] bytes'
here3
FTP Move Completed
*cmd* 'QUIT'
*put* 'QUIT\r\n'
*get* '221 GoodBye\n'
*resp* '221 GoodBye'

FileZilla大文件上传日志

反复提示被动响应地址不可路由,传输到90MB左右连接中断,重试后仍失败:

Status: Resolving address of web.com
Status: Connecting to IP:21...
Status: Connection established, waiting for welcome message...
Status: Insecure server, it does not support FTP over TLS.
Status: Logged in
Status: Retrieving directory listing...
Status: Directory listing of "/directory" successful
Status: Resolving address of web.com
Status: Connecting to IP:21...
Status: Connection established, waiting for welcome message...
Status: Insecure server, it does not support FTP over TLS.
Status: Logged in
Status: Starting upload of file.csv
Status: Server sent passive reply with unroutable address. Using server address instead.
Status: Disconnected from server: ECONNABORTED - Connection aborted
Command:    STOR file.csv
Response:   150 Connecting Data Port...
Error:  Disconnected from server: ECONNABORTED - Connection aborted
Error:  File transfer failed after transferring 90,439,680 bytes in 303 seconds

Fails here then retries (filezilla settings are to try twice I believe)

Status: Resolving address of web.com
Status: Connecting to IP:21...
Status: Connection established, waiting for welcome message...
Status: Insecure server, it does not support FTP over TLS.
Status: Logged in
Status: Starting upload of file.csv
Status: Retrieving directory listing of "/directory"...
Status: Server sent passive reply with unroutable address. Using server address instead.
Status: Server sent passive reply with unroutable address. Using server address instead.

已知背景信息

  • 服务端配置了自动任务,会将上传目标目录内的文件移动到下载目录,暂不确定该机制是否为故障诱因
  • 切换FTP主动模式后进程无限阻塞,无法完成传输

根因定位优先级

  1. FTP控制连接空闲超时断连:FTP传输数据时走单独的数据连接,控制连接全程处于空闲状态,绝大多数FTP服务器、中间防火墙/NAT设备会对空闲超过阈值的控制连接主动断开。你设置的2700秒(45分钟)超时是客户端侧配置,服务端/网络设备的空闲超时大概率小于15分钟,导致数据还在传输,控制连接已经被强制断开,等数据传完要接收226响应时就会触发10054错误。
  2. 服务端自动移文件任务冲突:如果自动任务的扫描间隔小于大文件上传时长,会在文件还没传完、仍处于写入状态时就尝试移动文件,直接打断传输连接。
  3. 被动模式端口/会话限制:服务端被动模式端口开放范围不足,或者防火墙对长连接的数据传输端口有会话超时限制,长时传输的连接被中间设备拦截。

可落地修复方案

  1. 开启控制连接心跳保活
    传输大文件时定期在控制连接发送NOOP命令,避免控制连接因为空闲被断开。修改SmartFTP类增加保活逻辑,在数据上传过程中定时发送保活指令,示例修改:
    import socket
    class SmartFTP(FTP):
        def makepasv(self):
            invalidhost, port = super(SmartFTP, self).makepasv()
            return self.host, port
        
        def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
            # 每上传32KB数据发送一次NOOP保活
            bytes_sent = 0
            def keepalive_callback(b):
                nonlocal bytes_sent
                bytes_sent += len(b)
                if callback:
                    callback(b)
                if bytes_sent >= 32768:
                    self.voidcmd('NOOP')
                    bytes_sent = 0
            return super().storbinary(cmd, fp, blocksize, keepalive_callback, rest)
    
    同时在客户端层面开启TCP keepalive,连接建立后设置socket选项,避免TCP连接被中间设备回收:
    with SmartFTP(FTP_HOST, FTP_USER, FTP_PASS, timeout=2700) as ftp:
        # 开启TCP保活,空闲60秒开始发探测包,每10秒发一次,探测3次失败判定连接断开
        ftp.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
        # Windows系统专属TCP保活参数设置
        ftp.sock.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 60000, 10000))
        # 后续上传逻辑不变
    
  2. 排查服务端自动任务逻辑
    联系服务端管理员确认自动移文件任务的触发规则:
    • 确认扫描间隔是否小于大文件上传时长
    • 确认任务是否会识别正在写入的未完成文件,要求调整规则为仅移动上传完成、不再被占用的文件,避免传输过程中文件被移动打断连接。
  3. 调整传输配置规避网络拦截
    • 调大storbinary的blocksize参数到64KB甚至更大,减少传输过程中的包数量,降低被中间设备拦截的概率
    • 排查本地防火墙是否拦截主动模式的入站连接:主动模式下服务端会主动发起数据连接到客户端的随机端口,需要在本地防火墙放开FTP程序的入站权限,避免连接被拦截导致阻塞。
    • 联系服务端管理员确认FTP服务的控制连接空闲超时、被动模式端口范围、防火墙会话超时配置,确保超时阈值大于大文件最长传输时长。
  4. 增加断点续传与上传校验逻辑
    大文件上传不要依赖单次长连接成功,增加失败重试+断点续传逻辑,上传完成后通过SIZE命令校验服务端文件大小和本地文件一致,确认上传成功,避免连接提前断开导致的流程异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 10:27:39