# 文件传输
最常用的几种文件传输协议有:XModem、YModem、ZModem 等。
XModem 是最早的文件传输协议之一,由于出现较早,几乎大部分的通讯程序所支持的文件传输都使用该协议,通常是传输 128 字节的信息块;这种古老的传输协议速度虽然较慢,但由于使用了 CRC 错误校验方法,传输的准确率可高达 99.6%。
YModem 协议是 XModem 的改进版,它最早用于调制解调器之间的文件传输,具有快速,稳定传输的优点。它的传输速度比 XModem 快,这是由于它可以一次传输 1024 字节的信息块,同时它还支持传输多个文件,也就是常说的批文件传输。
ZModem 协议的处理速度快于 XModem 和 YModem,这是因为它采用了串流式(streaming)传输方式,而且还具有自动改变区段大小和断点续传、快速错误侦测等功能,可以很好地进行断开后恢复传输。这是目前最流行的文件传输协议。
除开上面介绍的三种 X / Y / ZModem 协议,还有个常见的传输协议 --- ASCII 协议,这是最快的传输协议,但只能传送文本文件。
X / Ymodem 协议:
http://pauillac.inria.fr/~doligez/zmodem/ymodem.txt
Zmodem 协议:
http://gallium.inria.fr/~doligez/zmodem/zmodem.txt
# 控制字符定义
<SOH> 01H // 传输 128Byte 启动标志
<STX> 02H // 传输 1024Byte 启动标志
<EOT> 04H // 传输结束
<ACK> 06H // 应答
<NAK> 15H // 没应答
<CAN> 18H // 取消传输
<C> 43H // ASCII 'C' CRC校验请求
<NUL> 00H // 空符填充
<CPMEOF> 1AH // 数据填充 (^Z)
# XModem 通讯
Xmodem 是使用最广泛的文件传输协议之一。原始的 Xmodem 协议使用 128 字节数据包和一个简单的 “校验和 " 错误检测方法。后面增强为 Xmodem-CRC,使用了更安全的循环冗余校验 (CRC) 用于错误检测方法。Xmodem 协议总是首先尝试使用 CRC。如果发件人不确认对 CRC 的请求,接收器将转移到 ” 校验和 “ 模式并继续其传输请求。
增强型 Xmodem-1K 本质上是 Xmodem CRC 模式传输 1K (1024 字节) 数据包。在某些系统和通报上它也可以称为 Ymodem。
Xmodem 协议传输由接收程序和发送程序完成,先由接收程序发送协商字符,协商校验方式,协商通过之后发送程序就开始发送数据包,接收程序接收到完整的一个数据包之后按照协商的方式对数据包进行校验。校验通过之后发送确认字符,然后发送程序继续发送下一包;如果校验失败,则发送否认字符,发送程序重传此数据包。信息报中如果剩余的数据不足 128 字节,不足的部分将以 0x1A
填充。
# 校验和模式
# 帧包格式
Byte1 | Byte2 | Byte3 | Byte4~131 | Byte132 |
---|---|---|---|---|
Start Of Header(SOH) | Packet NumberID | ~(Packet NumberID) | Packet Data[128] | CheckSUM |
# 传输方式
Sender | Flow | Receiver |
---|---|---|
<--- | NAK | |
Time out after 3 Second | ||
<--- | NAK | |
<SOH> <0x01> <0xFE> <Data[0-127]> <Chksum> | ---> | Packet OK |
<--- | ACK | |
<SOH> <0x02> <0xFD> <Data[0-127]> <Chksum> | ---> | Line hit during transmission |
<--- | NAK | |
<SOH> <0x02> <0xFD> <Data[0-127]> <Chksum> | ---> | Packet OK |
<--- | ACK | |
<SOH> <0x03> <0xFC> <Data[0-127]> <Chksum> | ---> | Packet OK |
ACK get garbaged | <--- | ACK |
<SOH> <0x03> <0xFC> <Data[0-127]> <Chksum> | ---> | Duplicate packet |
<--- | NAK | |
<SOH> <0x04> <0xFB> <Data[0-127]> <Chksum> | ---> | UART Framing err on any byte |
<--- | NAK | |
<SOH> <0x04> <0xFB> <Data[0-127]> <Chksum> | ---> | Packet OK |
<--- | ACK | |
<SOH> <0x05> <0xFA> <Data[0-127]> <Chksum> | ---> | UART Overrun err on any byte |
<--- | NAK | |
<SOH> <0x05> <0xFA> <Data[0-127]> <Chksum> | ---> | Packet OK |
<--- | ACK | |
EOT | ---> | Packet OK |
ACK get garbaged | <--- | ACK |
EOT | ---> | Packet OK |
Finished | <--- | ACK |
# CRC 模式
计算 16 位 CRC 校验的除数多项式为: X ^ 16 + X ^ 12 + X ^ 5 + 1
,信息报中的 128 数据字节将参加 CRC 校验的计算,在发送端 CRC-16 的高字节在前,低字节在后。
# 帧包格式
Byte1 | Byte2 | Byte3 | Byte4~131 | Byte132~133 |
---|---|---|---|---|
Start Of Header(SOH) | Packet NumberID | ~(Packet NumberID) | Packet Data[128] | 16-Bit CRC |
# 传输方式
传输流程:接收方要求发送方以 CRC 校验方式发送时以 ‘C’ 来请求,发送方将对此作出应答。
Sender | Flow | Receiver |
---|---|---|
<--- | 'C' | |
Time out after 3 Second | ||
<--- | 'C' | |
<SOH> <0x01> <0xFE> <Data[0-127]> <CRC16> | ---> | Packet OK |
<--- | ACK | |
<SOH> <0x02> <0xFD> <Data[0-127]> <CRC16> | ---> | Line hit during transmission |
<--- | NAK | |
<SOH> <0x02> <0xFD> <Data[0-127]> <CRC16> | ---> | Packet OK |
<--- | ACK | |
<SOH> <0x03> <0xFC> <Data[0-127]> <CRC16> | ---> | Packet OK |
<--- | ACK | |
EOT | ---> | Packet OK |
ACK get garbaged | <--- | ACK |
EOT | ---> | Packet OK |
Finished | <--- | ACK |
# 扩展
关于校验和和 CRC 校验可以看以前的文章《常用校验算法》
XModem Protocol with CRC
# YModem 通讯
Ymodem 本质上是允许多个批处理文件传输的 Xmodem-1K,也可表示为 YModem-1K。
Ymodem 在 Xmodem-1K 基础上发展,沿用了 TeLink
协议的添加空头块的做法,也就是增加 block 0
。该 block 标识即将发送文件的 文件名
, 文件大小
和 文件创建时间戳(一般不填写)
Ymodem-g 是 Ymodem 的变体。它设计用于支持错误控制的调制解调器。该协议不提供软件纠错或恢复,但期望调制解调器提供服务。它是一种流式传输协议,以连续流的形式发送和接收 1K 个数据包,直到指示停止。YModem-g 传输形式与 YModem-1K 差不多,同时在发送完一个数据块后,它不会等待应答确认,而是快速连续地发送下一个数据块;如果有任何块不成功转移,整个转移被取消。
# 起始帧(block 0)
YModem 的起始帧并不直接传输文件的数据,而是将文件名与文件的大小放在数据帧中传输,它的帧长 = 3 字节数据首部 + 128 字节数据 + 2 字节 CRC16 校验码 = 33 字节。
Byte1 | Byte2 | Byte3 | Byte4~131 | Byte132~133 |
---|---|---|---|---|
Start Of Header(SOH) | 0x00 | 0xFF | [ < filename > < filezise > <NUL> ] | 16-Bit CRC |
说明:
- Byte 1~3:头标志是 SOH,起始帧序固定为 0x00,帧序取反为 0xFF。
- Byte 4~131:
filename
是传输的文件名字,如文件名foo.c
,它在起始帧中的格式为:66 6F 6F 2E 63 00
,也就是把 ASCII 码转成十六进制,最后的 0x00 代表文件名结束。filesize
是要传输的文件的大小,比如文件大小为 120 KByte,转换为120 * 1024 = 122880
Byte,转化为十六进制为0x1E00
,它在起始帧中的格式为:31 45 30 30 00
,对应 ASCII 为1E00
,最后的 0x00 代表文件长度结束。- 最后
NUL
代表剩余不足 128 Byte 部分用 0x00 填充。
- Byte 132~133:CRCH、CRCL 分别表示 16 位 CRC 校验码的高 8 位与低 8 位,高字节在前,低字节在后。
# 数据帧(block n)
Byte1 | Byte2 | Byte3 | Byte4~1027 | Byte1028~1029 |
---|---|---|---|---|
Start Of Header(STX) | Packet NumberID | ~(Packet NumberID) | Packet Data[1024] | 16-Bit CRC |
注意:有一种特殊的情况是,如果文件总大小小于等于 128 字节或者文件数据最后剩余的数据小于 128 字节,则 YModem 会选择 SOH 数据帧格式用 128 字节来传输数据,而数据不满 128 字节,剩余的数据用 0x1A
填充。
这时数据帧的结构就变成了:
文件大小小于 128 字节: <SOH> <01> <FE> <data1> <data2> ... <datan> <CPMEOF> ... <CRCH> <CRCL>
文件最后剩余数据小于 128 字节: <SOH> <ID> <~ID> <data1> <data2> ... <datan> <CPMEOF> ... <CRCH> <CRCL>
# 结束帧(block n+1)
YModem 的结束帧数据也采用 SOH 的 128 字节数据帧
Byte1 | Byte2 | Byte3 | Byte4~131 | Byte132~133 |
---|---|---|---|---|
Start Of Header(SOH) | 0x00 | 0xFF | <NUL> | 16-Bit CRC |
# 传输流程
以把 foo.c,大小为 4196Byte(16 进制为 0x1064)的文件作为传输的对象:
Sender | Flow | Receiver |
---|---|---|
<--- | 'C' | |
Time out after 3 Second | ||
<--- | 'C' | |
<SOH> <0x00> <0xFF> <Data[ <foo.c> | <0x1064> | <NUL> ]> <CRC16> | ---> | Packet OK |
<--- | ACK | |
<--- | 'C' | |
<STX> <0x01> <0xFE> <Data[0-1023]> <CRC16> | ---> | Packet OK |
<--- | ACK | |
<STX> <0x02> <0xFD> <Data[0-1023]> <CRC16> | ---> | Packet OK |
<--- | ACK | |
<STX> <0x03> <0xFC> <Data[0-1023]> <CRC16> | ---> | Packet OK |
<--- | ACK | |
<STX> <0x04> <0xFB> <Data[0-1023]> <CRC16> | ---> | Packet OK |
<--- | ACK | |
<SOH> <0x05> <0xFA> <Data[0-99]> <CPMEOF[0-27]> <CRC16> | ---> | Packet OK |
<--- | ACK | |
EOT | ---> | Packet OK |
<--- | NAK | |
EOT | ---> | Packet OK |
<--- | ACK | |
<--- | 'C' | |
<SOH> <0x00> <0xFF> <NUL[0-127]> <CRC16> | ---> | Packet OK |
Finished | <--- | ACK |
# 参考
https://en.wikipedia.org/wiki/XMODEM
https://en.wikipedia.org/wiki/YMODEM
https://en.wikipedia.org/wiki/ZMODEM
XMODEM, YMODEM, and ZMODEM
Transmisja modemowa - Xmodem, Ymodem, Zmodem
lrzsz: free x/y/zmodem implementation
# 附录
CRC 校验计算:
int calcrc(char *ptr, int count) | |
{ | |
int crc; | |
char i; | |
crc = 0; | |
while (--count >= 0) | |
{ | |
crc = crc ^ (int) *ptr++ << 8; | |
i = 8; | |
do | |
{ | |
if (crc & 0x8000) | |
crc = crc << 1 ^ 0x1021; | |
else | |
crc = crc << 1; | |
} while(--i); | |
} | |
return (crc); | |
} |