a
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
# 文件传输协议
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp)
|
||||
- [Zmodem.h](file://h/Zmodem.h)
|
||||
- [_zmodem.h](file://h/_zmodem.h)
|
||||
- [FileTransfer.h](file://h/FileTransfer.h)
|
||||
- [FileTransfer.cpp](file://cpp/Tools/FileTransfer.cpp)
|
||||
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp)
|
||||
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp)
|
||||
- [SComPort.h](file://h/SComPort.h)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [引言](#引言)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概述](#架构概述)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 引言
|
||||
本文档全面阐述了基于Zmodem协议的可靠文件传输实现。该协议在Geomative Studio项目中用于设备与主机之间的固件升级、脚本文件和测量数据传输。Zmodem协议通过其强大的错误恢复机制、断点续传功能和高效的二进制传输,确保了在串行通信等不可靠信道上的数据完整性。本分析将深入解析Zmodem类的核心状态机,包括ZRQINIT请求初始化、ZRINIT应答、ZFILE文件信息帧、ZDATA数据帧、ZEOF文件结束和ZFIN传输结束等关键帧类型的交互流程。同时,文档将详细说明CRC-32校验、断点续传(通过ZRPOS携带文件偏移量)和错误恢复(ZCRC校验请求)机制的工作原理,并分析SendSingleFile和ReceiveSingleFile方法中数据分块读写、帧头封装(SendBinaryHeader)和数据帧发送(SendDataFrame)的实现细节。
|
||||
|
||||
## 项目结构
|
||||
Geomative Studio项目是一个用于地质测量设备管理的复杂软件系统。文件传输功能是其核心模块之一,主要由`cpp/Tools/`目录下的工具类实现。与文件传输直接相关的文件包括`Zmodem.cpp`和`Zmodem.h`,它们实现了Zmodem协议的核心逻辑。`Zmodem`类继承自`FileTransfer`基类,后者定义了文件传输的通用接口。为了支持Zmodem协议,项目还包含了`Crc16.cpp`和`Crc32.cpp`文件,用于计算数据校验和。通信底层由`SComPort`类处理,它提供了与串口设备通信的接口。整个文件传输模块通过`FileTransfer`模块进行集成,为上层应用提供统一的文件收发功能。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "文件传输模块"
|
||||
Zmodem[Zmodem.cpp<br/>Zmodem协议实现]
|
||||
FileTransfer[FileTransfer.cpp<br/>传输基类]
|
||||
Crc32[Crc32.cpp<br/>CRC-32校验]
|
||||
Crc16[Crc16.cpp<br/>CRC-16校验]
|
||||
end
|
||||
subgraph "通信模块"
|
||||
SComPort[SComPort.h<br/>串口通信]
|
||||
end
|
||||
subgraph "应用层"
|
||||
FileTransferModule[FileTransfer模块<br/>文件传输应用]
|
||||
end
|
||||
Zmodem --> FileTransfer
|
||||
Zmodem --> Crc32
|
||||
Zmodem --> Crc16
|
||||
Zmodem --> SComPort
|
||||
FileTransferModule --> Zmodem
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1-L1940)
|
||||
- [FileTransfer.cpp](file://cpp/Tools/FileTransfer.cpp#L1-L47)
|
||||
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L1-L51)
|
||||
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L1-L54)
|
||||
- [SComPort.h](file://h/SComPort.h#L1-L74)
|
||||
|
||||
**Section sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1-L1940)
|
||||
- [Zmodem.h](file://h/Zmodem.h#L1-L143)
|
||||
- [FileTransfer.h](file://h/FileTransfer.h#L1-L49)
|
||||
|
||||
## 核心组件
|
||||
Zmodem协议的核心组件是`Zmodem`类,它封装了协议的所有状态机和数据处理逻辑。该类通过继承`FileTransfer`基类,实现了`Send`和`Receive`两个核心接口。`Zmodem`类维护了多个关键状态变量,如`transmitted_file_position`(已发送的文件位置)、`received_file_position`(已接收的文件位置)和`receiver_wants_crc32`(接收方是否支持CRC-32),这些变量共同驱动着协议的状态转换。协议的可靠性主要依赖于`SendBinaryHeader`、`ReadHeader`和`SendDataFrame`等方法,它们负责数据的封装、解析和校验。此外,`GetRinitHeader`和`SyncWithReceiver`等方法处理了协议的初始化和同步过程,确保了通信双方的协调一致。
|
||||
|
||||
**Section sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L33-L1940)
|
||||
- [Zmodem.h](file://h/Zmodem.h#L31-L140)
|
||||
|
||||
## 架构概述
|
||||
Zmodem协议的架构是一个典型的请求-响应式状态机,其核心是围绕一系列预定义的帧类型(Frame Types)进行交互。整个传输过程始于发送方的`ZRQINIT`请求,接收方通过`ZRINIT`帧进行应答,完成初始化握手。随后,发送方通过`ZFILE`帧发送文件名和元数据,接收方确认后,数据传输进入主循环,由`ZDATA`数据帧和`ZACK`确认帧构成。当文件传输完毕,发送方发送`ZEOF`帧,接收方确认后,发送方再发送`ZFIN`帧结束整个会话。该架构通过`ZRPOS`帧实现了断点续传,通过`ZCRC`帧实现了错误恢复,并通过`CAN`序列提供了紧急中断机制。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 发送方 as 发送方 (Zmodem)
|
||||
participant 接收方 as 接收方 (Zmodem)
|
||||
发送方->>接收方 : ZRQINIT (请求初始化)
|
||||
接收方->>发送方 : ZRINIT (初始化应答)
|
||||
发送方->>接收方 : ZFILE (文件信息)
|
||||
接收方->>发送方 : ZRPOS (请求从指定位置开始)
|
||||
loop 数据传输
|
||||
发送方->>接收方 : ZDATA (数据帧)
|
||||
接收方->>发送方 : ZACK (确认)
|
||||
end
|
||||
发送方->>接收方 : ZEOF (文件结束)
|
||||
接收方->>发送方 : ZRPOS (确认)
|
||||
发送方->>接收方 : ZFIN (会话结束)
|
||||
接收方->>发送方 : 'OO' (确认)
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L87-L126)
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L385-L451)
|
||||
- [_zmodem.h](file://h/_zmodem.h#L15-L36)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### Zmodem状态机分析
|
||||
Zmodem协议的状态机是其可靠性的核心。状态机的运行由`Send`和`Receive`方法驱动,它们分别调用`SendSingleFile`和`ReceiveSingleFile`来处理单个文件的传输。状态转换主要通过`ReadHeader`方法接收的帧类型来触发。例如,在`SendSingleFile`方法中,一个`ZCRC`帧会触发发送方重新计算并发送文件的CRC校验值;一个`ZRPOS`帧则会触发发送方跳转到指定的文件偏移量继续发送。在`ReceiveSingleFile`方法中,状态机通过`SendHexHeader`主动发送`ZRPOS`帧来请求数据,根据接收到的`ZDATA`、`ZEOF`或`ZSKIP`等帧来决定后续动作。这种基于事件驱动的状态机设计,使得协议能够灵活应对各种网络状况。
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> 初始化
|
||||
初始化 --> 发送文件信息 : ZFILE
|
||||
发送文件信息 --> 等待响应 : 发送ZFILE
|
||||
等待响应 --> 数据传输 : ZRPOS
|
||||
等待响应 --> 跳过文件 : ZSKIP
|
||||
等待响应 --> 错误恢复 : ZCRC
|
||||
数据传输 --> 发送数据帧 : ZDATA
|
||||
发送数据帧 --> 等待确认 : 发送ZDATA
|
||||
等待确认 --> 数据传输 : ZACK
|
||||
等待确认 --> 错误恢复 : TIMEOUT
|
||||
错误恢复 --> 发送数据帧 : 重发
|
||||
发送数据帧 --> 文件结束 : ZEOF
|
||||
文件结束 --> 等待结束 : 发送ZEOF
|
||||
等待结束 --> 传输结束 : ZRPOS
|
||||
传输结束 --> [*] : ZFIN
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L153-L258)
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L558-L775)
|
||||
|
||||
**Section sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L153-L775)
|
||||
|
||||
### 校验与错误恢复机制分析
|
||||
Zmodem协议通过CRC校验和超时重试机制来保证数据的完整性。协议支持CRC-16和CRC-32两种校验算法,由`receiver_wants_crc32`标志位决定。`Crc32`和`Crc16`类实现了校验算法,其中`Crc32`类使用了预计算的查找表来提高计算效率。当接收方检测到数据帧的CRC校验失败时,它会发送一个`ZNAK`(否定应答)或`ZCRC`(请求校验)帧。发送方在`SyncWithReceiver`方法中处理这些错误,根据情况重发数据或重新同步。`ReadDataFrame`方法是错误处理的关键,它会循环读取数据直到收到正确的`GOTCRCW`等结束标志,否则会返回`ZERROR`。此外,协议还定义了`TIMEOUT`和`GARBAGE_COUNT`等错误码,用于处理超时和垃圾数据。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[开始读取数据帧] --> B{是否为ZDLE?}
|
||||
B --> |是| C[读取转义字符]
|
||||
B --> |否| D{是否为有效数据?}
|
||||
D --> |是| E[更新CRC校验]
|
||||
D --> |否| F[处理XON/XOFF]
|
||||
C --> G{转义字符类型}
|
||||
G --> H[ZCRCE/G/Q/W] --> I[读取4字节CRC]
|
||||
G --> J[ZCAN] --> K[返回ZCAN]
|
||||
G --> L[ZRUB0/1] --> M[替换为0x7f/0xff]
|
||||
I --> N{CRC校验是否通过?}
|
||||
N --> |是| O[返回GOTCRC*]
|
||||
N --> |否| P[返回ZERROR]
|
||||
E --> Q{缓冲区满?}
|
||||
Q --> |否| B
|
||||
Q --> |是| R[返回ZERROR]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1154-L1340)
|
||||
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L1-L51)
|
||||
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L1-L54)
|
||||
|
||||
**Section sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1154-L1340)
|
||||
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L1-L51)
|
||||
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L1-L54)
|
||||
|
||||
### 数据传输流程分析
|
||||
文件传输的核心流程由`SendSingleFile`和`ReceiveSingleFile`方法实现。发送流程始于`Send`方法,它首先发送`ZRQINIT`帧,然后进入`SendSingleFile`。在`SendSingleFile`中,发送方构造包含文件名和长度的`ZFILE`帧并发送,随后等待接收方的`ZRPOS`指令。一旦收到`ZRPOS`,发送方调用`SendFileContents`方法,该方法循环读取文件数据,封装成`ZDATA`帧发送,并根据策略(如`ZCRCW`)等待接收方的`ZACK`确认。接收流程始于`Receive`方法,它调用`WakeUpSender`发送`ZRINIT`帧,然后进入`ReceiveSingleFile`。接收方通过循环发送`ZRPOS`帧来请求数据,`ReadDataFrame`方法负责接收和校验`ZDATA`帧,并将有效数据写入文件。整个流程通过`m_WriteBuff`和`buff`等缓冲区来优化I/O性能。
|
||||
|
||||
**Section sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L153-L378)
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L558-L775)
|
||||
|
||||
## 依赖分析
|
||||
Zmodem协议的实现依赖于多个层次的组件。最底层是`SComPort`类,它通过`ZmodemSendDataDirectly`和`ZmodemReceiveDataDirectly`方法提供串口的直接读写能力。`Zmodem`类依赖于`Crc32`和`Crc16`类来计算数据校验和。`Zmodem`类本身是`FileTransfer`抽象基类的具体实现,它必须实现`Send`和`Receive`两个纯虚函数。此外,`Zmodem`类还依赖于标准C库的文件操作函数(如`fopen`, `fread`, `fclose`)来处理本地文件。这些依赖关系清晰地划分了职责,使得协议逻辑与底层通信、文件系统和校验算法解耦。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class FileTransfer {
|
||||
<<abstract>>
|
||||
+Send(files[] char*) BOOL
|
||||
+Receive(Path CString) BOOL
|
||||
-error(fmt char*, ...)
|
||||
-status(fmt char*, ...)
|
||||
}
|
||||
class Zmodem {
|
||||
-port CSComPort*
|
||||
-file FILE*
|
||||
-file_length long
|
||||
-byte_count long
|
||||
-receiver_wants_crc32 int
|
||||
-received_file_position long
|
||||
+Send(files[] char*) BOOL
|
||||
+Receive(Path CString) BOOL
|
||||
-SendSingleFile(name char*) int
|
||||
-ReceiveSingleFile(Path CString) int
|
||||
-SendBinaryHeader(length int, type int, header char*) void
|
||||
-ReadHeader(header char*) int
|
||||
-SendDataFrame(buffer char*, length int, frameend int) void
|
||||
}
|
||||
class Crc32 {
|
||||
-crc unsigned long
|
||||
+Crc32(init_value unsigned long)
|
||||
+update(c int) void
|
||||
+value() unsigned long
|
||||
}
|
||||
class Crc16 {
|
||||
-crc unsigned short
|
||||
+Crc16(init_value unsigned short)
|
||||
+update(c int) void
|
||||
+value() unsigned short
|
||||
}
|
||||
class CSComPort {
|
||||
+ZmodemSendDataDirectly(pDataBuff char*, iDataSize int) BOOL
|
||||
+ZmodemReceiveDataDirectly(pDataBuff char*, iDataSize int*) BOOL
|
||||
}
|
||||
FileTransfer <|-- Zmodem : "继承"
|
||||
Zmodem --> Crc32 : "使用"
|
||||
Zmodem --> Crc16 : "使用"
|
||||
Zmodem --> CSComPort : "使用"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [FileTransfer.h](file://h/FileTransfer.h#L26-L46)
|
||||
- [Zmodem.h](file://h/Zmodem.h#L31-L140)
|
||||
- [Crc32.h](file://h/Crc32.h#L12-L28)
|
||||
- [Crc16.h](file://h/Crc16.h#L12-L27)
|
||||
- [SComPort.h](file://h/SComPort.h#L14-L73)
|
||||
|
||||
**Section sources**
|
||||
- [FileTransfer.h](file://h/FileTransfer.h#L26-L46)
|
||||
- [Zmodem.h](file://h/Zmodem.h#L31-L140)
|
||||
- [Crc32.h](file://h/Crc32.h#L12-L28)
|
||||
- [Crc16.h](file://h/Crc16.h#L12-L27)
|
||||
- [SComPort.h](file://h/SComPort.h#L14-L73)
|
||||
|
||||
## 性能考虑
|
||||
Zmodem协议的性能主要体现在其高效的二进制传输和流控机制上。协议通过`ZBIN32`和`ZBIN`帧头支持二进制传输,避免了ASCII编码的开销。`SendDataFrame`方法在发送数据时,会根据`receiver_wants_crc32`标志选择使用CRC-32或CRC-16,CRC-32虽然计算开销稍大,但能提供更强的错误检测能力。为了优化性能,代码中使用了`m_WriteBuff`写缓冲区,将多个`SendChar`调用合并为一次`ZmodemSendDataDirectly`调用,减少了系统调用的次数。在接收端,`ReadBuff`方法也实现了读缓冲,提高了数据读取效率。此外,`receiver_buffer_length`参数允许接收方告知发送方其缓冲区大小,从而实现简单的流量控制,防止接收方缓冲区溢出。
|
||||
|
||||
## 故障排除指南
|
||||
当文件传输失败时,应首先检查日志文件(如`log\\zmodemSZLog.txt`和`log\\zmodemRZLog.txt`)。常见的错误包括:
|
||||
- **ZERROR**: 通用错误,可能由多种原因引起,需结合上下文日志分析。
|
||||
- **TIMEOUT**: 通信超时,检查串口连接、波特率设置和设备电源。
|
||||
- **ZCAN**: 通信被对方取消,检查是否有用户手动中断或设备异常。
|
||||
- **Bad CRC**: 数据校验失败,通常由通信线路噪声或波特率不匹配引起。
|
||||
- **Open file failed**: 无法创建或打开文件,检查目标路径权限和磁盘空间。
|
||||
|
||||
代码中通过`error`和`status`方法输出详细的调试信息,并通过`PrintLogLast`方法将时间戳信息写入日志,便于问题定位。对于`ZCRC`错误,协议会自动请求重传,但如果错误频繁发生,则应检查物理连接。
|
||||
|
||||
**Section sources**
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L28-L47)
|
||||
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1906-L1921)
|
||||
- [FileTransfer.cpp](file://cpp/Tools/FileTransfer.cpp#L28-L46)
|
||||
|
||||
## 结论
|
||||
本文档详细分析了Geomative Studio项目中基于Zmodem协议的文件传输实现。该实现是一个功能完整、健壮可靠的协议栈,它通过精心设计的状态机、强大的CRC校验和灵活的断点续传机制,确保了在复杂工业环境下的数据传输成功率。`Zmodem`类的代码结构清晰,职责分明,通过继承和组合的方式,有效地管理了与底层通信、文件系统和校验算法的依赖关系。该模块为设备的固件升级、脚本部署和数据回传提供了坚实的基础,是Geomative Studio系统不可或缺的核心组件。
|
||||
Reference in New Issue
Block a user