FtpWebRequest大文件上传慢且连接被强制关闭问题求助
我完全懂你现在的头疼——传10GB的大tar文件到DropBox FTP,不仅慢得离谱,还老是弹出Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host的错误,可换FileZilla这类客户端传就快多了。别着急,我一步步给你拆解问题,讲清楚每个解决方案的原理,顺便教你怎么看传输速率。
先搞明白为啥会出问题
你的代码里有几个默认配置的小坑,刚好撞上了大文件上传的场景:
- 缓冲区太小:你用的4KB缓冲区,意味着每次只给服务器发一点点数据,来回交互次数多到爆炸,既拖慢速度,还容易让服务器觉得连接“摸鱼”,直接给你掐断。
- 没开连接保活:大文件传很久,中间要是有几秒没数据流动,DropBox的服务器可能会判定连接闲置,直接强制断开。
- 可能用了主动模式:FTP的主动模式容易被防火墙/路由器拦截,导致连接中断,而专业客户端默认都是用更稳定的被动模式。
一步步改代码,解决问题
1. 把缓冲区调大(立竿见影提速度)
小缓冲区是拖慢速度的核心原因之一,专业客户端一般用64KB-128KB的缓冲区,平衡内存占用和传输效率。把代码里的缓冲区定义改成:
// 替换原来的4096,改成64KB(65536 = 64*1024) byte[] buffer = new byte[65536];
为啥这么改? 更大的缓冲区能减少和服务器的交互次数,让数据传输更连贯,同时降低服务器判定连接闲置的概率。
2. 开连接保活,防止服务器掐线
给FTP请求加上保活机制,定期给服务器发“我还在传”的信号,避免被当成闲置连接断开。在创建FtpWebRequest后加这段代码:
requestObj.KeepAlive = true; // 每隔30秒发一次保活包,10秒没回应就重试(针对.NET Framework) requestObj.ServicePoint.SetTcpKeepAlive(true, 30000, 10000);
解释:KeepAlive让FTP会话保持打开,SetTcpKeepAlive负责定期发心跳包,服务器收到就知道连接还在活跃,不会随便掐断了。
3. 切换到被动模式(解决连接拦截问题)
DropBox这类云FTP基本都要求用被动模式,主动模式很容易被防火墙拦。在请求里加上:
requestObj.UsePassive = true;
啥是被动模式? 简单说,主动模式是服务器主动连你电脑,被动模式是你主动连服务器的指定端口,后者在绝大多数网络环境下都更稳定,不容易被拦截。
4. 加个进度监控,实时看传输速率
要知道传得有多快,我们可以累计已传字节数,结合时间差计算速率。修改你的上传循环代码:
long totalBytesRead = 0; DateTime startTime = DateTime.Now; int read = 0; while ((read = fs.Read(buffer, 0, buffer.Length)) != 0) { rs.Write(buffer, 0, read); totalBytesRead += read; // 每传10MB就打印一次进度和速率(可以改成1MB更频繁) if (totalBytesRead % 10485760 == 0) // 10*1024*1024 = 10485760字节 = 10MB { TimeSpan elapsed = DateTime.Now - startTime; double speedBytesPerSec = totalBytesRead / elapsed.TotalSeconds; double speedMBps = speedBytesPerSec / (1024 * 1024); // 转换成MB/s Console.WriteLine($"已传输: {totalBytesRead / (1024*1024):F2} MB | 当前速率: {speedMBps:F2} MB/s"); } }
效果:运行代码时,控制台会每隔10MB输出一次当前的传输进度和速率,你能清楚看到上传速度有没有提升。
最后说下优先级
先按缓冲区→保活→被动模式的顺序改,这三个是最核心的优化,基本能解决速度慢和连接断开的问题。如果还是偶尔出问题,再考虑分块上传+断点续传(这个稍微复杂点,新手可以先搞定前面的)。
内容的提问来源于stack exchange,提问作者Tango

