FTP 客户端可能在服务器收到文件之前很久就接受了 put 命令,上传过程中的大多数谜团正藏在这两件事之间的间隙里。必须走完四个阶段:本地文件被读取、服务器接受存储请求、数据连接搬运字节、传输到达最终的成功应答。比较阶段比比较客户端更有用,因为登录成功和文件名出现都只是停在了终点之前。

预备应答与最终应答

对 STOR 来说,125 或 150 应答只是预备性的。它说明服务器愿意尝试、数据连接正在打开,并不说明对象已经以最终形态存在。只有传输在该操作的上下文中到达表示完成的成功应答,通常是 226 或 250,操作才算完成。

426 表示传输被中止,另一条命令打印出的数字并不是这次上传的结果。要读属于该 STOR 的应答,而不是屏幕上的最后一个数字。如果客户端在这条应答到达之前就退出,你根本没有任何完成证据。

控制连接与数据连接

身份验证和目录列表走控制连接。字节走单独的数据连接,它的端点在每次传输时协商。登录成功只证明控制通道可用,完全不能说明客户端能否到达数据地址。

当服务器无法打开数据连接时,要检查协商出的端点以及被允许的路径。被动模式同样需要一个可用的数据地址和端口,而客户端与服务器之间的防火墙或 NAT 设备可能放行控制会话却挡住数据范围。如果服务器拒绝的是存储请求,就去看远端路径名、权限或存储配额。这两条分支都不能用控制连接已通过验证来解释。

基于 TLS 的 FTP 与 SFTP

基于 TLS 的 FTP 仍然是 FTP:两条连接、FTP 应答,每条连接各做一次 TLS 升级。SFTP 是 SSH 的子系统,只有一条连接,命令集也完全不同。它们只共享目的,其他几乎毫无共同点,所以跨越两者的诊断通常从比较错误的日志开始。

不要为了让中间工具查看会话而关闭 TLS,也不要把 SFTP 的失败当成 FTP 应答的问题。如果你需要脚本化的测试,curl 手册记录了显式 TLS、被动模式和上传选项,也是各个开关究竟改变了什么的参考资料。

在调试之前,先选对服务实际提供的协议。只会说 SFTP 的服务器永远不会产生 226 应答,客户端怎么配置都一样。

看得见的文件名与已存储的对象

看得见的文件名可能代表一个不完整的对象,尤其是在传输被中止或客户端提前退出之后。当需要保证字节不变时,请让服务负责人把预期测试文件的大小和可信的内容哈希与已存储的对象做比较。

二进制模式与 ASCII 模式是另一组会改变结果的比较。ASCII 模式可能转换行尾,所以即使传输成功,存下的字节也和本地文件不同。做逐字节核对要保留二进制模式,也不要为了让失败的传输看起来不一样而添加转换选项。

普通的 STOR 可以覆盖已存在的文件,而登录或列出目录的权限并不等于有权在该目录中创建或替换对象。测试时请使用此前未用过的远端名称。

重试之前该比较什么

不要在不确定的残缺文件上盲目追加、续传或重传。与服务负责人商定:是用新名字重新开始,还是使用服务支持的恢复流程,或者删掉失败的测试对象。

把结果写成四句话之一:本地读取失败、存储被拒绝、数据传输中止,或者传输完成且存下的字节一致。这样的记录比同时改 shell、改 VPN、改 FTP 设置直到文件名出现要可靠得多,也能告诉下一个人该检查哪个阶段。把脱敏后的应答文本和这句话放在一起。这样重试就是一个决定,而不是条件反射。

相关文章