3、TFTP协议基础:TFTP与FTP的区别、TFTP报文格式、TFTP的传输模式

聊到网络启动,TFTP 是个绕不开的角色。说白了,它就是 FTP 的「瘦身版」。我刚开始做 PXE 网络装机时,第一反应也是:为啥不用 FTP?后来踩了坑才明白,TFTP 的存在,恰恰是因为 FTP 太「重」了。

3.1 TFTP 与 FTP 的区别

先看一张对比表,这样更直观:

对比项 TFTP FTP
传输层协议 UDP(端口 69) TCP(端口 21/20)
连接方式 无连接,无会话 有连接,有会话管理
认证机制 无(不验证用户名密码) 有(支持匿名/用户认证)
传输效率 高,但不可靠 低,但可靠
适用场景 局域网、嵌入式、网络启动 广域网、文件管理、大文件传输
代码复杂度 极简(几十行代码可实现) 复杂(状态机、多线程)

我个人习惯把 TFTP 理解为「一次性快递」—— 丢过去就不管了,丢了再重发。而 FTP 更像是「挂号信」,每一步都要确认签收。你想想看,在 PXE 启动阶段,客户端连操作系统都没有,哪来的用户认证?哪来的 TCP 协议栈?所以 TFTP 这种「裸奔」的方式反而最合适。

核心区别一句话总结: TFTP 用 UDP 做「无脑快传」,FTP 用 TCP 做「可靠慢传」。网络启动场景下,快比可靠更重要 —— 因为丢了可以重传,但慢了就超时了。

3.2 TFTP 报文格式

TFTP 的报文结构非常简洁。我当年第一次抓包分析时,差点以为抓错了 —— 就这么几个字段?嗯,没错,就是这么简单。

TFTP 报文分为 5 种类型,由操作码(Opcode)区分:

  • RRQ(读请求,Opcode=1):客户端请求从服务器读取文件
  • WRQ(写请求,Opcode=2):客户端请求向服务器写入文件
  • DATA(数据包,Opcode=3):实际传输的文件数据块
  • ACK(确认包,Opcode=4):对数据包的确认
  • ERROR(错误包,Opcode=5):传输过程中发生错误

报文格式如下(以 RRQ 为例):

2 bytes    string    1 byte    string   1 byte
+--------+----------+--------+---------+--------+
| Opcode | Filename |   0    |  Mode   |   0    |
+--------+----------+--------+---------+--------+
  Opcode = 1 (RRQ)
  Filename = "pxelinux.0"
  Mode = "octet" 或 "netascii"

DATA 包的格式稍微复杂一点:

2 bytes    2 bytes       n bytes
+--------+----------+---------------+
| Opcode | Block #  |    Data       |
+--------+----------+---------------+
  Opcode = 3 (DATA)
  Block # = 1, 2, 3, ... (16位无符号整数)
  Data = 最多 512 字节
一个小细节: 每个 DATA 包最多携带 512 字节数据。如果文件大小正好是 512 的倍数,服务器会在最后发送一个 0 字节的 DATA 包来标识传输结束。这个「哨兵机制」我当年调试时没注意,结果客户端一直等数据,卡死了半小时。

ACK 包更简单:

2 bytes    2 bytes
+--------+----------+
| Opcode | Block #  |
+--------+----------+
  Opcode = 4 (ACK)
  Block # = 被确认的数据块编号

这里有个经典的「锁步协议」机制:客户端发一个 ACK,服务器才发下一个 DATA。说白了就是「你确认一个,我发一个」。这种设计在局域网里够用,但跨网段时延迟会很高。我曾经在跨 VLAN 的 PXE 环境里遇到过,TFTP 传输速度慢得像蜗牛,后来加了 TFTP 代理才解决。

3.3 TFTP 的传输模式

TFTP 支持两种传输模式,这个选择直接影响文件内容:

3.3.1 octet 模式(二进制模式)

这是我最常用的模式。octet 模式把文件当作原始字节流传输,不做任何转换。对于二进制文件(比如内核 vmlinuz、引导加载器 pxelinux.0),必须使用 octet 模式。

举个例子:

# 使用 tftp 客户端以 octet 模式下载文件
tftp> mode octet
tftp> get vmlinuz

octet 模式下,文件内容完全一致,MD5 校验值不会变。我建议所有网络启动场景都默认用 octet 模式,除非你明确知道自己在做什么。

3.3.2 netascii 模式(文本模式)

netascii 模式会做行尾转换。具体来说:

  • 发送端:将本地的换行符(\n)转换为 CRLF(\r\n)
  • 接收端:将 CRLF 转换回本地的换行符

这个模式在 Unix 和 Windows 之间传输文本文件时有用。但说实话,我在网络启动项目中几乎不用它。为什么?因为 PXE 传输的文件大多是二进制文件,netascii 模式会破坏二进制数据。我曾经见过有人用 netascii 模式传输内核,结果内核启动时直接 panic —— 因为二进制数据被「污染」了。

避坑指南: 在 PXE 网络启动中,永远使用 octet 模式。netascii 模式只适合纯文本配置文件(如 pxelinux.cfg 下的配置文件),但即使如此,我个人的习惯也是用 octet 模式传文本文件,然后在客户端手动处理换行符。这样更可控。

3.4 知识体系结构图

下面这张图可以帮你快速理清本章的知识脉络:

TFTP协议知识体系 与FTP的区别 UDP vs TCP 无认证 vs 有认证 轻量 vs 重量 报文格式 RRQ (Opcode=1) WRQ (Opcode=2) DATA (Opcode=3) ACK (Opcode=4) ERROR (Opcode=5) 传输模式 octet(二进制) netascii(文本) 核心结论 网络启动场景:octet模式 + 理解锁步机制 = 稳定传输

嗯,到这里 TFTP 的基础内容就差不多了。记住:TFTP 虽然简单,但它是网络启动的基石。很多 PXE 故障,追根溯源都是 TFTP 配置不当导致的。下一节我们会深入 TFTP 的实战配置,到时候再聊具体怎么调优。

本章要点回顾:
  • TFTP 基于 UDP,FTP 基于 TCP —— 这是最根本的区别
  • TFTP 报文只有 5 种类型,512 字节的数据块是核心
  • 网络启动必须用 octet 模式,netascii 模式会破坏二进制文件
  • 锁步协议(发一个确认一个)是 TFTP 的可靠性保障,也是性能瓶颈

可可精品资料库