一,简介
一个历史项目里面用了c# .net 2.0的FtpWebRequest进行文件上传;ftp server在各现场用的应该都是Filezilla。
因业务发展,需要上传大文件(500M以上吧),某现场就出现了上传失败的情况。
二,网络问题
最开始的代码里面并没有记录上传失败的具体原因,或者说log记录没能准确定位问题。
代码修改后还是没能准确定位问题。
但从log判断,似乎是网络断开造成的。
这想到可能现场网络不稳定,有瞬断情况。
三,断点续传
听过断点续传,在百度找了些代码,修改一下封装好嵌到项目里面。
当时只在网络畅通的情况下测试过,代码也没还checkin,发现场用户也试试。
反馈还是不行。
看log更加迷糊了,堆栈显示在FtpWebRequest.GetRequestStream.Close里面抛出来的异常。
想不明白啊。
四,重现烂网络
去过现场出差的同事反应,现场的网络真的好烂。
这想到怎么去模拟一个烂网络出来。
设置延时50ms,50%的丢包率,丫的那个异常堆栈重现出来了。
异常信息如下:
这应该说的,连接已经断开了,再关的话就报错了。
程序调试进去发现,最早引发异常的是FtpWebRequest.GetRequestStream.Write,程序里面是有catch,但只是记录了失败的位置偏移以便下次重传,也没有去记录失败原因。
当时close的调用是放在finally块里面的,这个close引发的异常导致续传没能继续执行,log记录的堆栈也就是从这里开始。
五,重现了也没个屁用啊
既然close不掉,那就直接跳到FtpWebRequest.GetResponse.Close好了。
还真不报异常了,GetResponse就直接阻塞了,一直塞到ftp server都超时断开了,还没返回。
看了一下msdn,说好的FtpWebRequest.Timeout咋的没生效呢?FtpWebRequest.ReadWriteTimeout可是好好的呢。
google+stackoverflow也没找到解决,倒是找到一些吐槽FtpWebRequest和Ftp库推荐的。
莫非还真得换库或者直接调些ftp命令?
同时stackoverflow发了第一个问题,我只想知道为什么不超时也不返回,因为我连GetResponse.Close都不调用就直接开始下一次重传的话,会报另一个异常如下:
不造是否英语太烂,或者是问题没到点子上,问题沉了。
6,似乎只能傻逼了
下班路上想到,出现异常的时候,一个close也不调用,无论是否重新连接,因为网络已经不通了,server应该还hold住一个连接,把文件锁住了。
这应该就是上面异常的情况,文件被锁了,新连接就没法操作这个文件,看server log,确实有这个cann't access file的记录。
那很好,client出异常了,等一个足够长的时间,等到server将连接断开就好了,close也就不管了。
但想想这也太傻逼了啊,这得等到什么时候啊。
7,也算彻底解决了,反正可以交货了
试了一下filezilla client,有断点续传功能,发现网络异常断开,开始续传连接开始之前,server那个连接总会很快断开。
这又是怎么解析呢,不是说网络都不通了,server那个连接是怎么放掉的呢?
google一下,stackoverflow上看到FtpWebRequest有个Abort函数,说是断开一个异步请求。
一试,我同步连接也能断开啊,网络异常,啥都不close,直接abort,server那个连接就断了,很快也就可以重传了呢。
8,来都来了
这个abort做了什么鬼呢,想用wireshark抓个包看看,无奈不懂,十来分钟连个filter都没写好。
难道是50%的丢包不够强悍,abort还是有数据逃出去了?
后来百度知道wireshark在windows下要做特殊处理才能抓取本地数据包。
无奈增加本机路由后filezilla server连不上了,最后下了个手机ftp server。
发现abort也没什么特殊的地方,只是通知ftp释放控制连接和数据连接然后马上返回,连接能不能断掉就听天由命了。
100%丢包率的时候,filezilla还真有连接会锁死文件。