TFTP 是最简单的文件传输协议:5 种报文、没有认证、一次只发一个块等一个 ACK。它简单到可以写进网卡的启动固件里,所以至今仍用来网络启动和配置设备。这篇自己实现它,看清它的每个报文、为什么在有延迟的链路上那么慢,以及 blksize 选项能救多少。
C→S 客户端临时端口→ 服务器 69 端口(此处为 9400) 18 字节 RRQ 文件=small.txt 模式=octet
C→S 客户端临时端口→ 服务器 69 端口(此处为 9400) 18 字节 RRQ 文件=small.txt 模式=octet
S→C 服务器 TID→ 客户端 516 字节 DATA 块号=1 数据=512 字节
C→S 客户端→ 服务器 TID 9402 4 字节 ACK 块号=1
S→C 服务器 TID→ 客户端 516 字节 DATA 块号=2 数据=512 字节
C→S 客户端→ 服务器 TID 9402 4 字节 ACK 块号=2
S→C 服务器 TID→ 客户端 516 字节 DATA 块号=3 数据=512 字节
C→S 客户端→ 服务器 TID 9402 4 字节 ACK 块号=3
S→C 服务器 TID→ 客户端 468 字节 DATA 块号=4 数据=464 字节
C→S 客户端→ 服务器 TID 9402 4 字节 ACK 块号=4
文件 2000 字节 = 3 × 512 + 464:前 3 个 DATA 是 512 字节(满块),最后一个 DATA 只有 464 字节(不足 512)——“不足 512 字节的 DATA 就是传输结束”。如果文件大小恰好是 512 的整数倍,最后要多发一个 0 字节的 DATA
服务器回应用的端口 9402 不是请求发往的端口 9400:服务器为每个传输新开一个端口(TID),后续两边都只认这一对端口;拿别的端口来插话会被回 ERROR 5(Unknown transfer ID)RRQ / WRQ / DATA / ACK / ERROR;服务器每个传输换一个新端口(TID)。
请求 nope.txt → ('ERR', 1, 'File not found');ERROR 码=1 信息='File not found'不足 512 字节的 DATA 表示传输结束;文件不存在回 ERROR 1。
块大小 512:共 196 个 DATA 块,耗时 9.94 秒,吞吐 10 KB/s;内容正确;每个块一个往返 → 理论 196 × 0.05 = 9.80 秒 块大小 1428:共 71 个 DATA 块,耗时 3.66 秒,吞吐 27 KB/s;内容正确;每个块一个往返 → 理论 71 × 0.05 = 3.55 秒;第一个包是 RRQ 文件=big.bin 模式=octet 选项=['blksize', '1428'],服务器回 OACK 选项=['blksize', '1428'] 吞吐 ≈ 块大小 ÷ 往返时间(512 ÷ 0.05 = 10 KB/s;1428 ÷ 0.05 ≈ 29 KB/s):TFTP 在有延迟的链路上很慢,因为它每次只允许一个块在路上
吞吐 ≈ 块大小 ÷ 往返时间;blksize 选项(OACK)把 512 提升到 1428,耗时降到 37%。
丢包 0%:耗时 3.65 秒;客户端超时重传 0 次,服务器超时重传 0 次;内容正确 丢包 3%:耗时 4.26 秒;客户端超时重传 0 次,服务器超时重传 1 次;内容正确 每丢一个包(DATA 或 ACK)就要等一个完整的超时(0.6 秒)才重传,而正常一个往返只要 0.05 秒——丢包让 TFTP 的耗时按“超时”而不是按“往返”增长
重传靠超时,一次丢包多等一个完整超时。