FTP上传报错:无法写入传输连接,求原因分析
我开发了一个将文件上传至路由器FTP服务器(挂载U盘)的应用,文件数量不多,单文件最大20MB,也包含小型TXT文件。最初采用逐字节写入的FTP上传方法,代码如下:
private void UploadFileFtp(string filePathTemp, Uri filePathFtp, IProgress<float> progressOneFile) { FtpWebRequest ftpWeb = FtpWebRequest.Create(filePathFtp) as FtpWebRequest; ftpWeb.Method = WebRequestMethods.Ftp.UploadFile; ftpWeb.UseBinary = true; using Stream streamRequest = ftpWeb.GetRequestStream(); using FileStream reader = new FileStream(filePathTemp, FileMode.Open); var lenghtBytes = ftpWeb.ContentLength = reader.Length; int @byte = reader.ReadByte(); long countBytes = 1; int oldPrecent = 0; while (@byte != -1) { streamRequest.WriteByte((byte)@byte); @byte = reader.ReadByte(); int precent = (int)(countBytes++ * 100 / lenghtBytes); if (precent != oldPrecent) { oldPrecent = precent; progressOneFile.Report(precent / 100f); } } streamRequest.Flush(); streamRequest.Close(); using FtpWebResponse response = ftpWeb.GetResponse() as FtpWebResponse; if (!response.StatusDescription.Contains("226") && response.StatusCode != FtpStatusCode.ClosingData) { throw new Exception("Error in fileUnploading" + filePathTemp); } }
在本地及外部网络的笔记本测试均正常,但在朋友的DIR-825路由器上触发错误Unable to write data to the transport connection,且路由器会死机需重启;我的路由器上文件写入流会挂起。排除某一文件后,错误会转移到其他文件。
尝试关闭路由器防火墙、调整KeepAlive、UsePassive等属性均无效。随后重构为缓冲区批量写入的上传方法,代码如下:
private void UploadFileFtp(string filePathTemp, Uri filePathFtp, IProgress<float> progressOneFile) { FtpWebRequest ftpWeb = FtpWebRequest.Create(filePathFtp) as FtpWebRequest; ftpWeb.Method = WebRequestMethods.Ftp.UploadFile; ftpWeb.UseBinary = true; ftpWeb.ConnectionGroupName = "uploadftp"; using FileStream reader = new FileStream(filePathTemp, FileMode.Open); var lenghtBytes = ftpWeb.ContentLength = reader.Length; byte[] buffer = new byte[1024]; using Stream streamRequest = ftpWeb.GetRequestStream(); int @byte = reader.Read(buffer, 0, buffer.Length); long countIterationWrite = @byte; int oldPrecent = 0; while (@byte != 0) { streamRequest.Write(buffer, 0, buffer.Length); @byte = reader.Read(buffer, 0, buffer.Length); int precent = (int)((countIterationWrite+= @byte) * 100 / lenghtBytes); if (precent != oldPrecent) { oldPrecent = precent; progressOneFile.Report(precent/100f); } } streamRequest.Flush(); streamRequest.Close(); using FtpWebResponse response = ftpWeb.GetResponse() as FtpWebResponse; if (!response.StatusDescription.Contains("226") && response.StatusCode != FtpStatusCode.ClosingData) { throw new Exception("Ошибка при загрузке файла " + filePathTemp); } }
调整后可正常上传,但将缓冲区大小设为1字节时错误复现。请问这一现象的原因是什么?
路由器FTP服务器的资源瓶颈
路由器属于嵌入式设备,CPU、内存等硬件资源极为有限,其内置的FTP服务器仅针对常规批量数据传输做了优化,无法处理高频小数据包的冲击。逐字节上传时,每发送1字节就会触发一次TCP传输交互,短时间内产生海量数据包和TCP确认(ACK)请求,直接耗尽路由器的CPU和内存,导致连接中断甚至设备死机。TCP协议的交互开销问题
TCP是面向连接的可靠协议,每次发送小数据包后都需要等待接收方的ACK确认。逐字节传输会让TCP连接始终处于"发送-等待-确认"的循环,不仅传输效率极低,还会导致网络栈缓冲区被大量待确认的微小数据包占满,最终引发传输连接超时或写入失败的错误。FtpWebRequest的底层行为特性
FtpWebRequest的WriteByte方法没有内置缓冲机制,会直接触发底层网络写入操作。当缓冲区设为1字节时,本质和逐字节写入完全一致,同样会产生高频小数据包,触发资源耗尽问题;而使用1024字节这类常规缓冲区时,数据被批量打包发送,大幅减少TCP交互次数,降低路由器的处理压力,从而避免错误。
内容的提问来源于stack exchange,提问作者Евгений Кудик

