a
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
# 串口通信
|
||||
|
||||
<cite>
|
||||
**本文引用的文件列表**
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp)
|
||||
- [SComPort.h](file://h/SComPort.h)
|
||||
- [Constant.h](file://h/Constant.h)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考量](#性能考量)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录](#附录)
|
||||
|
||||
## 简介
|
||||
本文围绕 SComPort 类对 Windows 串口通信进行系统化技术文档化,重点覆盖:
|
||||
- 串口初始化流程:CreateFile 创建句柄、SetupComm 配置缓冲区、SetCommState 设置波特率(115200)与数据格式(8位数据位、无校验、1位停止位)
|
||||
- 异步 I/O 操作中 OVERLAPPED 结构的应用,以及 ReadFile/WriteFile 与 GetOverlappedResult 的配合实现非阻塞读写
|
||||
- ClearCommError 和 PurgeComm 在错误处理与缓冲区清理中的作用
|
||||
- 基于事件驱动的通信模式:EV_RXCHAR 接收字符事件与 EV_BREAK 断线检测的实现原理
|
||||
- 串口打开、数据收发、异常处理与资源释放的完整调用示例
|
||||
- 多线程环境下线程安全策略与注意事项
|
||||
|
||||
## 项目结构
|
||||
SComPort 类位于工具模块中,提供串口底层封装与上层命令交互能力;相关常量定义位于公共头文件;UI 层通过消息回调处理断线事件。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "工具层"
|
||||
SComPort["SComPort 类<br/>cpp/Tools/SComPort.cpp"]
|
||||
SComPortWinXP["SComPort 兼容实现<br/>cpp/Tools/SComPort_winxp.cpp"]
|
||||
Header["头文件声明<br/>h/SComPort.h"]
|
||||
Const["常量定义<br/>h/Constant.h"]
|
||||
end
|
||||
subgraph "界面层"
|
||||
MainFrm["主窗口消息处理<br/>cpp/Views/MainFrm.cpp"]
|
||||
end
|
||||
SComPort --> Header
|
||||
SComPort --> Const
|
||||
SComPortWinXP --> Header
|
||||
SComPortWinXP --> Const
|
||||
MainFrm --> |"WM_BREAKLINE"| SComPort
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L126-L223)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L126-L221)
|
||||
- [SComPort.h](file://h/SComPort.h#L14-L71)
|
||||
- [Constant.h](file://h/Constant.h#L224-L232)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L126-L223)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L126-L221)
|
||||
- [SComPort.h](file://h/SComPort.h#L14-L71)
|
||||
- [Constant.h](file://h/Constant.h#L224-L232)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
|
||||
|
||||
## 核心组件
|
||||
- SComPort 类:封装串口打开、配置、读写、Zmodem 收发、断线检测、日志与缓冲区清理等能力
|
||||
- 常量定义:READBUFFER_SIZE、WRITEBUFFER_SIZE、COMM_TIMEOUT 等
|
||||
- 主窗口消息处理:接收 WM_BREAKLINE 并关闭对应串口、更新设备状态
|
||||
|
||||
章节来源
|
||||
- [SComPort.h](file://h/SComPort.h#L14-L71)
|
||||
- [Constant.h](file://h/Constant.h#L224-L232)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
|
||||
|
||||
## 架构总览
|
||||
SComPort 采用 Windows 异步串口 I/O 模式,使用 OVERLAPPED 结构与事件对象实现非阻塞读写;通过 SetCommMask 注册事件掩码,结合 WaitCommEvent 实现事件驱动的接收路径;ClearCommError 用于查询错误与队列长度;PurgeComm 用于清理发送/接收缓冲区;断线检测通过独立线程轮询端口是否存在并发送 WM_BREAKLINE 给 UI。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant UI as "UI/调用方"
|
||||
participant Port as "SComPort"
|
||||
participant WinIO as "Windows 串口API"
|
||||
participant Mask as "事件掩码(EV_* )"
|
||||
participant Thread as "断线检测线程"
|
||||
UI->>Port : 打开串口(OpenComm)
|
||||
Port->>WinIO : CreateFile(异步标志)
|
||||
Port->>WinIO : SetupComm(读写缓冲区大小)
|
||||
Port->>WinIO : Get/SetCommState(波特率115200/8N1)
|
||||
Port->>WinIO : SetCommMask(EV_RXCHAR/EV_BREAK/...)
|
||||
Note over Port,WinIO : 初始化完成
|
||||
UI->>Port : 发送数据(SendDataDirectly)
|
||||
Port->>WinIO : WriteFile(Overlapped)
|
||||
WinIO-->>Port : ERROR_IO_PENDING
|
||||
Port->>WinIO : WaitForSingleObject(Overlapped.hEvent)
|
||||
WinIO-->>Port : 完成事件
|
||||
Port->>WinIO : GetOverlappedResult(获取字节数)
|
||||
UI->>Port : 接收数据(ReceiveDataDirectly)
|
||||
Port->>WinIO : WaitCommEvent(EV_RXCHAR)
|
||||
WinIO-->>Port : 事件触发
|
||||
Port->>WinIO : ClearCommError(查询InQue)
|
||||
Port->>WinIO : ReadFile(Overlapped)
|
||||
WinIO-->>Port : ERROR_IO_PENDING
|
||||
Port->>WinIO : WaitForSingleObject(Overlapped.hEvent)
|
||||
WinIO-->>Port : 完成事件
|
||||
Port->>WinIO : GetOverlappedResult(获取字节数)
|
||||
Thread->>WinIO : CreateFile(轮询)
|
||||
WinIO-->>Thread : ERROR_FILE_NOT_FOUND
|
||||
Thread->>UI : SendMessage(WM_BREAKLINE)
|
||||
UI->>Port : CloseComm()
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L286-L380)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L382-L420)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L560-L607)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L746-L779)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 串口初始化流程
|
||||
- 句柄创建:使用 CreateFile 打开端口,启用 FILE_FLAG_OVERLAPPED 以支持异步 I/O
|
||||
- 缓冲区配置:SetupComm 设置读写缓冲区大小(由常量 READBUFFER_SIZE/WRITEBUFFER_SIZE 决定)
|
||||
- 参数设置:GetCommState 获取当前 DCB,随后设置 BaudRate=115200、ByteSize=8、Parity=NOPARITY、StopBits=ONESTOPBIT,并调用 SetCommState 应用
|
||||
- 事件掩码:SetCommMask 注册 EV_BREAK、EV_CTS、EV_DSR、EV_ERR、EV_RING、EV_RLSD、EV_RXCHAR、EV_RXFLAG、EV_TXEMPTY 等事件
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L155-L196)
|
||||
- [Constant.h](file://h/Constant.h#L224-L226)
|
||||
|
||||
### 异步 I/O 与 OVERLAPPED
|
||||
- 发送路径:SendDataDirectly 中为 OVERLAPPED.hEvent 创建事件对象,调用 WriteFile;若返回 ERROR_IO_PENDING,则通过 WaitForSingleObject 等待事件;最终通过 GetOverlappedResult 获取实际写入字节数
|
||||
- 接收路径:ReceiveDataDirectly 中先调用 ClearCommError 查询 cbInQue,再根据需要调用 ReadFile;同样在 ERROR_IO_PENDING 情况下等待 Overlapped.hEvent 并用 GetOverlappedResult 获取结果
|
||||
- Zmodem 路径:ZmodemSendDataDirectly/ZmodemReceiveDataDirectly 使用独立 OVERLAPPED 对象,发送前可 PurgeComm 清理发送缓冲区,接收时根据 cbInQue 读取
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L716-L763)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L765-L900)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L382-L420)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L286-L380)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L560-L607)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L609-L744)
|
||||
|
||||
### 错误处理与缓冲区清理
|
||||
- ClearCommError:用于查询通信错误标志与输入缓冲区字节数,避免在无数据时盲目 ReadFile
|
||||
- PurgeComm:在关闭串口或需要清空缓冲区时使用 PURGE_RXCLEAR/PURGE_TXCLEAR/PURGE_RXABORT/PURGE_TXABORT 清理接收/发送队列
|
||||
- 日志输出:通过 PrintLogLast 将通信过程与错误信息写入日志文件,便于定位问题
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L323-L335)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L268-L284)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L585-L595)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L937-L952)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L267-L284)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L429-L439)
|
||||
|
||||
### 事件驱动通信模式
|
||||
- WaitCommEvent:在接收路径中等待 EV_RXCHAR 等事件触发,避免忙等
|
||||
- EV_BREAK:断线检测通过 SetCommMask 注册 EV_BREAK,并结合断线检测线程轮询端口是否存在,当端口不可访问时向 UI 发送 WM_BREAKLINE
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L189-L196)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L225-L256)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L188-L196)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L224-L255)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
|
||||
|
||||
### 断线检测线程与 UI 回调
|
||||
- 断线检测线程:SComPortDetectBreakThreadFun 循环尝试以独占方式打开端口,若返回 ERROR_FILE_NOT_FOUND 则判定断线,调用 CloseComm 并通过 SendMessage(WM_BREAKLINE) 通知 UI
|
||||
- UI 处理:OnBreakLine 中根据通信 ID 关闭对应串口、更新设备状态、刷新界面
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L225-L256)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L224-L255)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
|
||||
|
||||
### 串口打开、数据收发、异常处理与资源释放的完整调用示例
|
||||
- 打开串口:OpenComm -> CreateFile -> SetupComm -> Get/SetCommState -> SetCommMask
|
||||
- 发送数据:SendDataDirectly -> WriteFile(Overlapped) -> WaitForSingleObject -> GetOverlappedResult
|
||||
- 接收数据:WaitCommEvent(EV_RXCHAR) -> ClearCommError(cbInQue) -> ReadFile(Overlapped) -> WaitForSingleObject -> GetOverlappedResult
|
||||
- 异常处理:ERROR_IO_PENDING 分支、超时处理、错误日志
|
||||
- 资源释放:CloseComm -> PurgeComm -> CloseHandle
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L126-L223)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L268-L284)
|
||||
|
||||
### 多线程环境下的线程安全策略
|
||||
- 断线检测线程:独立线程轮询端口,避免阻塞主线程;线程内对串口句柄的创建/关闭遵循最小化持有原则
|
||||
- 串口对象成员:构造函数初始化成员变量,析构函数负责关闭句柄与日志;代码中保留了 m_CriticalSection 字段但未启用,建议在多线程并发访问同一实例时增加互斥保护
|
||||
- UI 与串口交互:通过消息 WM_BREAKLINE 解耦断线检测与 UI 更新,降低锁竞争
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L31-L57)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L87-L91)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L30-L57)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L86-L90)
|
||||
|
||||
## 依赖关系分析
|
||||
- SComPort 依赖 Windows 串口 API(CreateFile、SetupComm、GetCommState、SetCommState、SetCommMask、WaitCommEvent、ReadFile、WriteFile、GetOverlappedResult、ClearCommError、PurgeComm、CloseHandle)
|
||||
- 常量 READBUFFER_SIZE/WRITEBUFFER_SIZE、COMM_TIMEOUT 来自公共头文件
|
||||
- UI 通过消息 WM_BREAKLINE 与串口断线检测解耦
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
SComPort["SComPort 类"] --> WinAPI["Windows 串口API"]
|
||||
SComPort --> Const["常量定义"]
|
||||
SComPort --> UI["主窗口消息处理"]
|
||||
WinAPI --> |"CreateFile/SetupComm/SetCommState/SetCommMask"| Port["物理串口"]
|
||||
WinAPI --> |"WaitCommEvent/ReadFile/WriteFile"| Port
|
||||
WinAPI --> |"ClearCommError/PurgeComm"| Port
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L268-L284)
|
||||
- [Constant.h](file://h/Constant.h#L224-L232)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L268-L284)
|
||||
- [Constant.h](file://h/Constant.h#L224-L232)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
|
||||
|
||||
## 性能考量
|
||||
- 缓冲区大小:READBUFFER_SIZE/WRITEBUFFER_SIZE 为 15360 字节,适合批量传输场景;可根据设备数据速率调整
|
||||
- 超时控制:COMM_TIMEOUT 为 500ms,用于等待事件或 I/O 完成;在高延迟链路中可适当增大
|
||||
- 异步 I/O:OVERLAPPED + 事件对象避免阻塞,提高吞吐;注意避免多个操作共享同一 OVERLAPPED 导致状态混淆
|
||||
- 日志输出:频繁写日志会影响性能,建议在调试阶段开启,生产环境关闭或降频
|
||||
|
||||
[本节为通用指导,无需列出具体文件来源]
|
||||
|
||||
## 故障排查指南
|
||||
- 打开失败:检查 CreateFile 返回值与 GetLastError;确认端口名称格式(含“\\\\.\”前缀)
|
||||
- 设置参数失败:检查 SetCommState 返回值;确认 DCB 字段赋值顺序与目标平台兼容性
|
||||
- 无数据可读:使用 ClearCommError 检查 cbInQue;确保已注册 EV_RXCHAR 事件并正确等待
|
||||
- 发送卡住:确认 WriteFile 返回 ERROR_IO_PENDING 后正确等待 Overlapped.hEvent;检查线程优先级与事件对象创建
|
||||
- 断线误报:断线检测线程轮询间隔与 UI 回调需匹配;避免重复创建线程导致资源泄漏
|
||||
- 日志定位:通过 PrintLogLast 输出的时间戳与错误码快速定位问题
|
||||
|
||||
章节来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L323-L335)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L937-L952)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L155-L196)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L286-L380)
|
||||
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L560-L607)
|
||||
|
||||
## 结论
|
||||
SComPort 类通过 Windows 异步串口 API 提供了稳定可靠的串口通信能力,覆盖初始化、异步读写、事件驱动接收、断线检测与资源管理。结合 ClearCommError 与 PurgeComm,能够有效处理缓冲区与错误状态;通过 WM_BREAKLINE 与 UI 解耦,提升用户体验。建议在多线程并发访问同一实例时增加互斥保护,并根据实际数据速率调整缓冲区与超时参数。
|
||||
|
||||
[本节为总结性内容,无需列出具体文件来源]
|
||||
|
||||
## 附录
|
||||
|
||||
### 关键流程图:ReceiveDataDirectly
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["进入 ReceiveDataDirectly"]) --> CheckHandle["检查串口句柄是否有效"]
|
||||
CheckHandle --> |无效| Fail["返回失败"]
|
||||
CheckHandle --> |有效| ClearErr["ClearCommError 查询错误与InQue"]
|
||||
ClearErr --> InQueZero{"InQue 是否为0"}
|
||||
InQueZero --> |是| ReturnFalse["返回 FALSE"]
|
||||
InQueZero --> |否| CalcNeed["计算需要读取字节数(min(最大读取, InQue))"]
|
||||
CalcNeed --> Read["ReadFile(Overlapped)"]
|
||||
Read --> Pending{"返回值是否为 FALSE"}
|
||||
Pending --> |是 且 错误码为 ERROR_IO_PENDING| Wait["WaitForSingleObject(Overlapped.hEvent)"]
|
||||
Wait --> Gor["GetOverlappedResult 获取字节数"]
|
||||
Gor --> Compare["比较实际字节数与期望字节数"]
|
||||
Compare --> Log["记录日志"]
|
||||
Log --> ReturnTrue["返回成功"]
|
||||
Pending --> |否| CheckRead["检查 ReadFile 返回字节数"]
|
||||
CheckRead --> Log2["记录日志"]
|
||||
Log2 --> ReturnTrue
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
|
||||
|
||||
### 关键流程图:SendDataDirectly
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["进入 SendDataDirectly"]) --> CheckHandle["检查串口句柄与输入参数"]
|
||||
CheckHandle --> |无效| Fail["返回失败"]
|
||||
CheckHandle --> |有效| Prepare["准备 OVERLAPPED.hEvent 与写缓冲区"]
|
||||
Prepare --> Write["WriteFile(Overlapped)"]
|
||||
Write --> Pending{"返回值是否为 FALSE"}
|
||||
Pending --> |是 且 错误码为 ERROR_IO_PENDING| Wait["WaitForSingleObject(Overlapped.hEvent)"]
|
||||
Wait --> Timeout{"等待结果"}
|
||||
Timeout --> |超时| Purge["关闭句柄并返回失败"]
|
||||
Timeout --> |成功| ReturnTrue["返回成功"]
|
||||
Pending --> |否| ReturnTrue
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
|
||||
|
||||
### 关键流程图:断线检测线程
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Loop["循环检测"] --> TryOpen["尝试以独占方式打开串口"]
|
||||
TryOpen --> OpenFail{"打开失败?"}
|
||||
OpenFail --> |是 且 错误码为 文件不存在| Break["标记断线并关闭串口"]
|
||||
Break --> PostMsg["发送 WM_BREAKLINE 给 UI"]
|
||||
PostMsg --> Exit["退出线程"]
|
||||
OpenFail --> |否| CloseHandle["关闭句柄并继续循环"]
|
||||
CloseHandle --> Sleep["Sleep(短时间)"]
|
||||
Sleep --> Loop
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L225-L256)
|
||||
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
|
||||
@@ -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系统不可或缺的核心组件。
|
||||
@@ -0,0 +1,326 @@
|
||||
# 网络通信
|
||||
|
||||
<cite>
|
||||
**本文引用的文件**
|
||||
- [TcpClient.h](file://h/TcpClient.h)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp)
|
||||
- [TransferCtrl.h](file://h/TransferCtrl.h)
|
||||
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp)
|
||||
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h)
|
||||
- [Constant.h](file://h/Constant.h)
|
||||
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
|
||||
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考量](#性能考量)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录](#附录)
|
||||
|
||||
## 简介
|
||||
本文件围绕 TcpClient 类实现的 TCP 客户端通信机制进行深入解析,覆盖 socket 创建、SOCK_STREAM 流式套接字配置、SO_LINGER 和 SO_KEEPALIVE 等套接字选项的作用与效果;connect 建立连接的三次握手过程与超时策略;SendData/RecvData 中 send/recv 的调用逻辑、非阻塞模式下的 select 使用、MSG_PEEK 探测接收状态与错误码处理;ReConnect 重连机制的实现、closesocket 资源释放与新 socket 重建的必要性;GetConnectStatus 通过 select+recv(MSG_PEEK) 组合检测连接存活状态的技术细节。最后给出连接管理、数据收发、异常重连与资源清理的完整流程图与示例路径,并说明在设备固件升级与数据同步场景中的应用。
|
||||
|
||||
## 项目结构
|
||||
该项目为桌面应用工程,网络通信相关代码集中在 Tools 子目录,核心类为 TcpClient 与派生类 CTransferCtrl;协议定义位于 CtrlProtocolDef.h;常量与全局配置位于 Constant.h;设备管理与同步界面位于 Views 子目录;固件升级逻辑位于 Tools 子目录。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "网络工具层"
|
||||
TC["TcpClient<br/>h/TcpClient.h<br/>cpp/Tools/TcpClient.cpp"]
|
||||
TCtrl["CTransferCtrl<br/>h/TransferCtrl.h<br/>cpp/Tools/TransferCtrl.cpp"]
|
||||
end
|
||||
subgraph "协议与常量"
|
||||
Proto["CtrlProtocolDef.h"]
|
||||
Const["Constant.h"]
|
||||
end
|
||||
subgraph "业务视图与操作"
|
||||
View["devmngframe.cpp"]
|
||||
Upg["checkupdate.cpp"]
|
||||
end
|
||||
TCtrl --> TC
|
||||
TCtrl --> Proto
|
||||
TC --> Const
|
||||
View --> TCtrl
|
||||
Upg --> TCtrl
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [TcpClient.h](file://h/TcpClient.h#L1-L76)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L1-L449)
|
||||
- [TransferCtrl.h](file://h/TransferCtrl.h#L1-L50)
|
||||
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L1-L200)
|
||||
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L1-L120)
|
||||
- [Constant.h](file://h/Constant.h#L220-L235)
|
||||
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp#L470-L504)
|
||||
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L1-L120)
|
||||
|
||||
章节来源
|
||||
- [TcpClient.h](file://h/TcpClient.h#L1-L76)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L1-L120)
|
||||
- [TransferCtrl.h](file://h/TransferCtrl.h#L1-L50)
|
||||
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L1-L120)
|
||||
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L1-L120)
|
||||
- [Constant.h](file://h/Constant.h#L220-L235)
|
||||
|
||||
## 核心组件
|
||||
- TcpClient:封装 TCP 套接字生命周期、连接管理、数据收发、连接状态检测与重连。
|
||||
- CTransferCtrl:基于 TcpClient 的控制协议封装,负责控制命令的打包、发送与响应解析,以及与业务层交互。
|
||||
|
||||
章节来源
|
||||
- [TcpClient.h](file://h/TcpClient.h#L18-L76)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L26-L120)
|
||||
- [TransferCtrl.h](file://h/TransferCtrl.h#L34-L50)
|
||||
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L120-L180)
|
||||
|
||||
## 架构总览
|
||||
下图展示 TcpClient 与 CTransferCtrl 的类关系、关键成员与方法,以及与协议定义的关联。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class CTcpClient {
|
||||
+CTcpClient()
|
||||
+~CTcpClient()
|
||||
+Initialize() bool
|
||||
+ConnectToServer(strIP, wPort) bool
|
||||
+ReConnect() bool
|
||||
+SendData(pData, iLen) int
|
||||
+RecvData(pData, iLen, iActualLen) bool
|
||||
+GetConnectStatus() bool
|
||||
+CloseConnect() bool
|
||||
+Disconnect() bool
|
||||
+ClearRecvBuffer() bool
|
||||
-InitailTcp() bool
|
||||
-ConnectServer(strAddr, wPort) bool
|
||||
-m_strIP : CString
|
||||
-m_wPort : WORD
|
||||
-m_iSndBuf : int
|
||||
-m_iRcvBuf : int
|
||||
-m_sockClient : SOCKET
|
||||
-m_bIsConnect : bool
|
||||
-m_bIsInitialed : bool
|
||||
-m_pClearRcvBuf : char*
|
||||
}
|
||||
class CTransferCtrl {
|
||||
+Initialize() bool
|
||||
+SendCtrlInfo(pCtrlHeader, pData, wDataLen, DataType) bool
|
||||
+RecvCtrlMsg(pData, pLen, iMaxLen, iTimeout) int
|
||||
+RecvRspMsg(pData, pLen, iMaxLen, iTimeout) int
|
||||
+GetCtrlRspLength(ucCMd) int
|
||||
+InitialClient() bool
|
||||
+IsCtrlMsg(ucCMd) bool
|
||||
+IsPlcStatusMsg(ucCmd, ucSrcType) bool
|
||||
+GetPlcAddr() uint32
|
||||
+GetCurrCtrlCmd() int
|
||||
-RecvEnoughMsg(pData, iLen, iTimeOut) bool
|
||||
-RecvCommRspMsg(pData, pLen, iMaxLen, iRcvTimeout, bIsRcvCtrl) int
|
||||
-SendCtrlInfoToDev(pTransInfo, pData, wDataLen, ucDataType) bool
|
||||
}
|
||||
CTransferCtrl --|> CTcpClient : "继承"
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [TcpClient.h](file://h/TcpClient.h#L18-L76)
|
||||
- [TransferCtrl.h](file://h/TransferCtrl.h#L34-L50)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 套接字创建与流式配置
|
||||
- socket 创建:构造函数初始化成员,InitailTcp 在首次使用前创建流式套接字(AF_INET, SOCK_STREAM)。
|
||||
- 缓冲区设置:通过 SO_RCVBUF/SO_SNDBUF 设置收发缓冲区大小。
|
||||
- 连接保活:启用 SO_KEEPALIVE,提升链路异常检测能力。
|
||||
- 关闭策略:禁用 SO_LINGER(立即丢弃未发送数据),避免长时间等待导致阻塞。
|
||||
|
||||
章节来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L48-L99)
|
||||
|
||||
### connect 建立连接与三次握手
|
||||
- 连接发起:ConnectServer 将目标地址转换为 sockaddr_in 并调用 connect。
|
||||
- 三次握手:由系统内核完成,应用层无需显式处理握手细节。
|
||||
- 超时策略:当前实现未对 connect 设置超时,如需超时可在上层封装 select 或非阻塞 + 超时判断(建议在业务层统一处理)。
|
||||
|
||||
章节来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L101-L130)
|
||||
|
||||
### SendData 发送逻辑
|
||||
- 连接状态检查:发送前调用 GetConnectStatus,若连接中断则返回错误。
|
||||
- send 调用:调用 send 发送数据,记录日志与错误码。
|
||||
- 返回值语义:返回实际发送字节数或错误标识,便于上层判断。
|
||||
|
||||
章节来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L149-L177)
|
||||
|
||||
### RecvData 接收逻辑与非阻塞 select
|
||||
- 非阻塞探测:使用 select 设置超时(微秒级),在无数据时快速返回。
|
||||
- 可读判定:select 返回正值且 FD_ISSET 检测到可读时,执行 recv。
|
||||
- 异常处理:recv 返回非正数时标记连接中断并更新状态。
|
||||
|
||||
章节来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L282-L354)
|
||||
|
||||
### GetConnectStatus 连接存活检测
|
||||
- select 立即轮询:使用零超时的 select 检查可读事件。
|
||||
- MSG_PEEK 探测:对可读套接字执行 MSG_PEEK 探测,若返回值小于等于 0,通常表示对端关闭或连接异常。
|
||||
- 状态维护:根据探测结果更新 m_bIsConnect。
|
||||
|
||||
章节来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L201-L249)
|
||||
|
||||
### ReConnect 重连机制
|
||||
- 资源释放:先调用 CloseConnect 关闭当前连接。
|
||||
- 新建套接字:调用 InitailTcp 重新创建套接字,以规避某些平台/驱动下“仅关闭连接无法再次成功”的限制。
|
||||
- 再连接:使用原 IP/端口重新 ConnectToServer。
|
||||
|
||||
章节来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L252-L278)
|
||||
|
||||
### CTransferCtrl 控制协议封装
|
||||
- 初始化:Initialize 调用基类 InitailTcp 完成套接字准备。
|
||||
- 发送控制:SendCtrlInfoToDev 构造协议头与数据,调用 SendData 发送。
|
||||
- 接收控制:RecvCtrlMsg/RecvRspMsg/RecvCommRspMsg 实现头部解析、长度校验、CRC 校验与超时处理。
|
||||
- 协议常量:使用 CtrlProtocolDef.h 中的命令字、结构体与枚举。
|
||||
|
||||
章节来源
|
||||
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L72-L180)
|
||||
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L245-L400)
|
||||
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L441-L548)
|
||||
|
||||
### 设备固件升级与数据同步场景
|
||||
- 固件升级:checkupdate.cpp 展示了通过网络下载升级文件、向设备发送文件与执行升级命令的流程,体现网络通信在升级中的作用。
|
||||
- 数据同步:devmngframe.cpp 展示了设备参数同步、任务参数下发与云端托管任务的交互,CTransferCtrl 在其中承担控制通道。
|
||||
|
||||
章节来源
|
||||
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L1-L120)
|
||||
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L1042-L1336)
|
||||
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp#L470-L504)
|
||||
|
||||
## 依赖关系分析
|
||||
- CTransferCtrl 依赖 TcpClient 提供的套接字生命周期与收发能力。
|
||||
- CTransferCtrl 依赖 CtrlProtocolDef.h 的协议结构与命令字。
|
||||
- TcpClient 依赖 Constant.h 中的缓冲区大小与超时常量。
|
||||
- 视图层与操作层通过 CTransferCtrl 间接使用网络通信。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
TCtrl["CTransferCtrl"] --> TC["CTcpClient"]
|
||||
TCtrl --> Proto["CtrlProtocolDef.h"]
|
||||
TC --> Const["Constant.h"]
|
||||
View["devmngframe.cpp"] --> TCtrl
|
||||
Upg["checkupdate.cpp"] --> TCtrl
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L1-L120)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L1-L120)
|
||||
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L1-L120)
|
||||
- [Constant.h](file://h/Constant.h#L220-L235)
|
||||
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp#L470-L504)
|
||||
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L1-L120)
|
||||
|
||||
## 性能考量
|
||||
- 缓冲区大小:通过 SO_RCVBUF/SO_SNDBUF 调整可减少系统调用次数,提高吞吐。
|
||||
- 非阻塞接收:使用 select 微秒级超时,避免长时间阻塞,适合高频轮询场景。
|
||||
- MSG_PEEK 探测:零拷贝探测连接状态,降低开销。
|
||||
- 重连策略:在出现异常时主动重建套接字,避免复用旧句柄导致的不可达。
|
||||
|
||||
[本节为通用指导,无需列出具体文件来源]
|
||||
|
||||
## 故障排查指南
|
||||
- 连接失败:检查 InitailTcp 是否成功创建套接字、SOCK_STREAM 参数是否正确、SO_LINGER 与 SO_KEEPALIVE 设置是否生效。
|
||||
- 发送失败:关注 WSAGetLastError 返回值,结合日志定位错误码。
|
||||
- 接收超时:确认 RecvData 的 select 超时设置是否合理,避免过短导致频繁超时。
|
||||
- 连接中断:GetConnectStatus 的 MSG_PEEK 探测返回非正数时,应视为连接异常并触发重连。
|
||||
- 重连无效:若仅关闭连接而不重建套接字,可能无法再次连接,需遵循 ReConnect 的重建策略。
|
||||
|
||||
章节来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L48-L99)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L149-L177)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L282-L354)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L201-L249)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L252-L278)
|
||||
|
||||
## 结论
|
||||
TcpClient 提供了简洁可靠的 TCP 客户端基础能力:套接字创建与配置、连接管理、非阻塞数据收发、连接状态探测与重连。CTransferCtrl 在此基础上实现了控制协议的编解码与业务交互,支撑设备固件升级与数据同步等场景。通过 MSG_PEEK 与 select 的组合,能够高效检测连接状态并在异常时快速恢复。建议在业务层补充 connect 超时与更细粒度的错误分类,以进一步提升鲁棒性。
|
||||
|
||||
[本节为总结性内容,无需列出具体文件来源]
|
||||
|
||||
## 附录
|
||||
|
||||
### 关键流程图
|
||||
|
||||
#### 连接建立与状态检测序列
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as "应用"
|
||||
participant Client as "CTcpClient"
|
||||
participant Kernel as "系统内核"
|
||||
App->>Client : "Initialize()"
|
||||
Client->>Kernel : "socket(AF_INET, SOCK_STREAM)"
|
||||
Client-->>App : "返回初始化结果"
|
||||
App->>Client : "ConnectToServer(IP, Port)"
|
||||
Client->>Kernel : "connect(目标地址)"
|
||||
Kernel-->>Client : "三次握手完成/失败"
|
||||
Client-->>App : "返回连接结果"
|
||||
App->>Client : "GetConnectStatus()"
|
||||
Client->>Kernel : "select(零超时)+recv(MSG_PEEK)"
|
||||
Kernel-->>Client : "返回可读/不可读"
|
||||
Client-->>App : "返回连接状态"
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L48-L130)
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L201-L249)
|
||||
|
||||
#### 数据发送与接收流程
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["进入 RecvData"]) --> CheckConn["检查连接状态"]
|
||||
CheckConn --> ConnOK{"连接正常?"}
|
||||
ConnOK --> |否| RetFalse["返回失败"]
|
||||
ConnOK --> |是| SelectWait["select 设置微秒级超时"]
|
||||
SelectWait --> SelRet{"select 返回值"}
|
||||
SelRet --> |错误| HandleErr["记录错误并返回失败"]
|
||||
SelRet --> |超时(0)| RetZero["返回成功(0字节)"]
|
||||
SelRet --> |可读(>0)| FDCheck["FD_ISSET 检测套接字"]
|
||||
FDCheck --> FDOK{"可读信号有效?"}
|
||||
FDOK --> |否| HandleErr
|
||||
FDOK --> |是| Recv["recv 接收数据"]
|
||||
Recv --> RecvRet{"recv 返回值<=0?"}
|
||||
RecvRet --> |是| MarkDown["标记连接中断并返回失败"]
|
||||
RecvRet --> |否| SaveLen["保存实际接收长度"]
|
||||
SaveLen --> Log["记录日志"]
|
||||
Log --> Done(["返回成功"])
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L282-L354)
|
||||
|
||||
#### 重连机制流程
|
||||
```mermaid
|
||||
flowchart TD
|
||||
RCStart(["ReConnect 开始"]) --> CloseOld["CloseConnect 关闭旧连接"]
|
||||
CloseOld --> NewSock["InitailTcp 重新创建套接字"]
|
||||
NewSock --> NewSockOK{"创建成功?"}
|
||||
NewSockOK --> |否| ShowErr["提示套接字初始化失败"]
|
||||
ShowErr --> RCEnd(["结束"])
|
||||
NewSockOK --> |是| Reconnect["ConnectToServer(IP, Port)"]
|
||||
Reconnect --> RCResult{"连接成功?"}
|
||||
RCResult --> |是| RCOK["返回成功"]
|
||||
RCResult --> |否| RCFail["返回失败"]
|
||||
RCOK --> RCEnd
|
||||
RCFail --> RCEnd
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [TcpClient.cpp](file://cpp/Tools/TcpClient.cpp#L252-L278)
|
||||
|
||||
### 应用示例路径
|
||||
- 设备参数同步与云端托管任务交互:参见 [devmngframe.cpp](file://cpp/Views/devmngframe.cpp#L470-L504)
|
||||
- 固件升级流程(下载、发送文件、执行升级命令):参见 [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L1042-L1336)
|
||||
- 控制协议发送与接收:参见 [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L120-L180)、[TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L245-L400)
|
||||
@@ -0,0 +1,425 @@
|
||||
# 通信工具
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [SComPort.cpp](file://cpp\Tools\SComPort.cpp)
|
||||
- [SComPort.h](file://h\SComPort.h)
|
||||
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp)
|
||||
- [TcpClient.h](file://h\TcpClient.h)
|
||||
- [Zmodem.cpp](file://cpp\Tools\Zmodem.cpp)
|
||||
- [Zmodem.h](file://h\Zmodem.h)
|
||||
- [FileTransfer.cpp](file://cpp\Tools\FileTransfer.cpp)
|
||||
- [FileTransfer.h](file://h\FileTransfer.h)
|
||||
- [FileTransfer_crul.cpp](file://cpp\Tools\FileTransfer_crul.cpp)
|
||||
- [FileTransfer_crul.h](file://h\FileTransfer_crul.h)
|
||||
- [HttpDownload.cpp](file://cpp\Tools\HttpDownload.cpp)
|
||||
- [HttpDownload.h](file://h\HttpDownload.h)
|
||||
- [Constant.h](file://h\Constant.h)
|
||||
- [CtrlProtocolDef.h](file://h\CtrlProtocolDef.h)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [引言](#引言)
|
||||
2. [串口通信模块](#串口通信模块)
|
||||
3. [TCP通信模块](#tcp通信模块)
|
||||
4. [Zmodem文件传输协议](#zmodem文件传输协议)
|
||||
5. [文件传输协作机制](#文件传输协作机制)
|
||||
6. [HTTP下载功能](#http下载功能)
|
||||
7. [应用场景与调用示例](#应用场景与调用示例)
|
||||
8. [最佳实践](#最佳实践)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 引言
|
||||
GeomativeStudio中的通信工具集成了多种通信方式,包括串口通信、TCP通信、Zmodem协议文件传输和HTTP下载功能。这些工具为设备连接、固件升级和数据同步等关键功能提供了底层支持。本文档详细分析了这些通信模块的实现机制与使用方法。
|
||||
|
||||
## 串口通信模块
|
||||
|
||||
### SComPort类实现
|
||||
`SComPort`类封装了Windows平台下的串口通信功能,提供了异步读写、超时控制和多线程安全处理等特性。
|
||||
|
||||
**串口初始化与配置**
|
||||
`SComPort`类通过`OpenComm`方法初始化串口连接,设置通信参数如波特率(115200)、数据位(8位)、奇偶校验(无)和停止位(1位)。该类使用重叠I/O(Overlapped I/O)实现异步通信,确保主线程不会被阻塞。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class CSComPort {
|
||||
+CString m_szComName
|
||||
+HANDLE m_hCom
|
||||
+HWND m_hOwnerWnd
|
||||
+long m_lCommID
|
||||
+DCB m_dcbBlock
|
||||
+char m_aWriteBuffer[WRITEBUFFER_SIZE]
|
||||
+int m_iWriteSize
|
||||
+char m_aReadBuffer[READBUFFER_SIZE]
|
||||
+int m_iReadSize
|
||||
+DWORD m_dwBLThreadID
|
||||
+HANDLE m_hBLThread
|
||||
+BOOL m_bIsDetBreakFun
|
||||
+BOOL m_bIsScanBreak
|
||||
+DWORD m_dwFactdata
|
||||
+FILE* m_Log
|
||||
+CSComPort()
|
||||
+CSComPort(HWND hOwnerWnd, long lCommID)
|
||||
+~CSComPort()
|
||||
+BOOL FindComName(CStringArray* pStringArray)
|
||||
+BOOL OpenComm(CString szComName)
|
||||
+void SetOwnerWnd(HWND hOwnerWnd)
|
||||
+void SetCommID(long lCommID)
|
||||
+void CloseComm()
|
||||
+BOOL ReceiveDataDirectly(char* pDataBuff, int* iDataSize)
|
||||
+BOOL SendDataDirectly(char* pDataBuff, int iDataSize)
|
||||
+void SetScanBreakSign(BOOL bIsScanBreak)
|
||||
+void ClearCommSendBuff()
|
||||
+void ClearCommReceiveBuff()
|
||||
+BOOL ZmodemReceiveDataDirectly(char* pDataBuff, int* iDataSize)
|
||||
+BOOL ZmodemSendDataDirectly(char* pDataBuff, int iDataSize)
|
||||
+static UINT SComPortDetectBreakThreadFun(LPVOID pParam)
|
||||
}
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [SComPort.h](file://h\SComPort.h#L14-L73)
|
||||
- [SComPort.cpp](file://cpp\Tools\SComPort.cpp#L31-L285)
|
||||
|
||||
**异步读写机制**
|
||||
`ReceiveDataDirectly`和`SendDataDirectly`方法实现了异步读写功能。读取操作首先通过`ClearCommError`检查输入缓冲区中的数据量,然后使用`ReadFile`函数进行异步读取。如果`ReadFile`立即返回`ERROR_IO_PENDING`,则通过`GetOverlappedResult`等待操作完成。
|
||||
|
||||
**超时控制**
|
||||
通信超时通过`GetOverlappedResult`的最后一个参数`TRUE`实现,该参数指示函数应阻塞直到操作完成或超时。日志记录功能通过`fopen`打开日志文件"LOG\\commLog.txt"实现,所有发送和接收的数据都会被记录。
|
||||
|
||||
**多线程安全处理**
|
||||
`SComPort`类通过`m_hBLThread`线程监控串口连接状态。`SComPortDetectBreakThreadFun`线程函数定期尝试重新打开串口,如果失败则通过`SendMessage`通知所有者窗口串口已断开。
|
||||
|
||||
**Section sources**
|
||||
- [SComPort.cpp](file://cpp\Tools\SComPort.cpp#L306-L403)
|
||||
- [SComPort.cpp](file://cpp\Tools\SComPort.cpp#L527-L576)
|
||||
- [SComPort.cpp](file://cpp\Tools\SComPort.cpp#L225-L256)
|
||||
|
||||
## TCP通信模块
|
||||
|
||||
### TcpClient类实现
|
||||
`CTcpClient`类实现了TCP客户端通信功能,支持连接管理、数据收发和异常重连。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class CTcpClient {
|
||||
+CString m_strIP
|
||||
+WORD m_wPort
|
||||
+SOCKET m_sockClient
|
||||
+int m_iRcvBuf
|
||||
+int m_iSndBuf
|
||||
+bool m_bIsConnect
|
||||
+char* m_pClearRcvBuf
|
||||
+CTcpClient()
|
||||
+~CTcpClient()
|
||||
+bool InitailTcp()
|
||||
+bool ConnectServer(CString strAddr, WORD wPort)
|
||||
+bool ConnectToServer(CString strIP, WORD wPort)
|
||||
+int SendData(const char* pData, int wLen)
|
||||
+bool CloseConnect()
|
||||
+bool GetConnectStatus()
|
||||
+bool ReConnect()
|
||||
+bool RecvData(char* pData, int iLen, int& iActualLen)
|
||||
+bool Disconnect()
|
||||
+bool ClearRecvBuffer()
|
||||
}
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [TcpClient.h](file://h\TcpClient.h#L18-L75)
|
||||
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L26-L39)
|
||||
|
||||
**连接管理**
|
||||
`InitailTcp`方法初始化套接字,设置套接字选项如`SO_LINGER`、接收/发送缓冲区大小和`SO_KEEPALIVE`。`ConnectServer`方法建立与服务器的连接,`CloseConnect`方法关闭连接并释放资源。
|
||||
|
||||
**数据收发**
|
||||
`SendData`方法使用`send`函数发送数据,`RecvData`方法使用`select`函数实现非阻塞接收。`select`函数检查套接字是否有数据可读,超时时间为25毫秒。
|
||||
|
||||
**异常重连**
|
||||
`ReConnect`方法实现了异常重连机制。当连接断开时,先调用`CloseConnect`关闭现有连接,然后重新初始化套接字并通过`ConnectToServer`重新连接。
|
||||
|
||||
**Section sources**
|
||||
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L48-L97)
|
||||
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L101-L130)
|
||||
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L252-L278)
|
||||
|
||||
## Zmodem文件传输协议
|
||||
|
||||
### Zmodem类实现
|
||||
`Zmodem`类实现了Zmodem协议,用于在串口连接上进行可靠的文件传输。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Zmodem {
|
||||
+CSComPort* port
|
||||
+FILE* file
|
||||
+long file_length
|
||||
+long byte_count
|
||||
+int file_count
|
||||
+char file_name[128]
|
||||
+char buffer[BUFFSIZE]
|
||||
+unsigned long m_FileSize
|
||||
+int m_ByteTransfer
|
||||
+char m_WriteBuff[8192]
|
||||
+int m_WriteBuffLength
|
||||
+int m_nBufReadIndex
|
||||
+FILE* zmodemLog
|
||||
+DWORD dwBegin
|
||||
+char cReadLog[2048]
|
||||
+int nLogLength
|
||||
+int nIndex
|
||||
+Zmodem(CSComPort* rs232_port)
|
||||
+~Zmodem()
|
||||
+BOOL Send(char* files[])
|
||||
+BOOL Receive(CString Path)
|
||||
+BOOL SendCancelString()
|
||||
+int GetTransferPosit()
|
||||
+int GetFileSize()
|
||||
}
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Zmodem.h](file://h\Zmodem.h#L31-L142)
|
||||
- [Zmodem.cpp](file://cpp\Tools\Zmodem.cpp#L33-L56)
|
||||
|
||||
**帧结构解析**
|
||||
Zmodem协议使用多种帧类型进行通信,包括`ZRQINIT`、`ZRINIT`、`ZFILE`、`ZDATA`、`ZEOF`和`ZFIN`。`ReadHeader`方法解析接收到的帧头,`SendBinaryHeader`方法发送二进制帧头。
|
||||
|
||||
**错误校验**
|
||||
Zmodem协议使用CRC-16或CRC-32进行错误校验。`Crc16`和`Crc32`类用于计算数据的校验和。`SendDataFrame`方法在发送数据帧时包含校验信息。
|
||||
|
||||
**断点续传机制**
|
||||
`ReceiveSingleFile`方法实现了断点续传功能。当接收文件时,如果文件已存在且大小与远程文件相同,则发送`ZRPOS`帧请求从上次中断的位置继续传输。
|
||||
|
||||
**Section sources**
|
||||
- [Zmodem.cpp](file://cpp\Tools\Zmodem.cpp#L128-L127)
|
||||
- [Zmodem.cpp](file://cpp\Tools\Zmodem.cpp#L558-L774)
|
||||
- [Zmodem.cpp](file://cpp\Tools\Zmodem.cpp#L253-L258)
|
||||
|
||||
## 文件传输协作机制
|
||||
|
||||
### FileTransfer与FileTransfer_crul协作
|
||||
`FileTransfer`和`FileTransfer_crul`类共同实现了设备与主机间的文件传输功能。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class FileTransfer {
|
||||
+CSComPort* port
|
||||
+FILE* file
|
||||
+long file_length
|
||||
+long byte_count
|
||||
+int file_count
|
||||
+char file_name[128]
|
||||
+char buffer[BUFFSIZE]
|
||||
+FileTransfer()
|
||||
+virtual ~FileTransfer()
|
||||
+virtual void error(char* fmt, ...)
|
||||
+virtual void status(char* fmt, ...)
|
||||
+virtual BOOL Send(char* files[]) = 0
|
||||
+virtual BOOL Receive(CString Path) = 0
|
||||
}
|
||||
class FileTransfer_crul {
|
||||
+CURL* m_pCrEasyHandl
|
||||
+CURL* m_pCrPuteasyHandl
|
||||
+FILE* m_pLog
|
||||
+CString m_strUrl
|
||||
+CString m_strSavePath
|
||||
+CString m_strSaveName
|
||||
+bool m_bIsNeedProgress
|
||||
+bool m_bIsNeedErrInfo
|
||||
+int m_nTimeOut
|
||||
+bool m_bIsGobalInitial
|
||||
+bool m_bIsHttpsFlag
|
||||
+bool m_bIsSSLCertify
|
||||
+FileTransfer_crul()
|
||||
+virtual ~FileTransfer_crul()
|
||||
+bool Inital()
|
||||
+void UnInital()
|
||||
+void SetOption(EnOptFlag enFlag, CString strInfo, bool bFlag = false)
|
||||
+bool DownloadFile()
|
||||
+DOUBLE GetFileSize()
|
||||
+bool GetUrlResConValild()
|
||||
+void InitalLogInfo()
|
||||
+static size_t WriteToFile(void* ptr, size_t size, size_t nmemb, void* userp)
|
||||
+static int xferinfo(void* p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||
+static size_t WriteToBuff(void* ptr, size_t size, size_t nmemb, void* userp)
|
||||
+static size_t WriteFuncForUrlTest(void* ptr, size_t size, size_t nmemb, void* userp)
|
||||
+void PrintLog(CString strLog)
|
||||
}
|
||||
FileTransfer <|-- Zmodem : "extends"
|
||||
FileTransfer_crul --> CURL : "uses"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [FileTransfer.h](file://h\FileTransfer.h#L26-L48)
|
||||
- [FileTransfer_crul.h](file://h\FileTransfer_crul.h#L55-L110)
|
||||
- [FileTransfer_crul.cpp](file://cpp\Tools\FileTransfer_crul.cpp#L24-L62)
|
||||
|
||||
**协作流程**
|
||||
`FileTransfer`作为基类定义了文件传输的接口,`Zmodem`类继承自`FileTransfer`并实现了基于串口的文件传输。`FileTransfer_crul`类使用libcurl库实现HTTP/HTTPS文件下载功能。
|
||||
|
||||
**Section sources**
|
||||
- [FileTransfer.cpp](file://cpp\Tools\FileTransfer.cpp#L28-L47)
|
||||
- [FileTransfer_crul.cpp](file://cpp\Tools\FileTransfer_crul.cpp#L151-L281)
|
||||
|
||||
## HTTP下载功能
|
||||
|
||||
### HttpDownload类实现
|
||||
`HttpDownload`类实现了HTTP协议下载功能,支持多线程下载和断点续传。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class HttpDownload {
|
||||
+BOOL m_bSupportResume
|
||||
+BOOL m_bResume
|
||||
+BOOL m_bStop
|
||||
+BOOL m_bIsUseFile
|
||||
+BOOL m_bFileLocked
|
||||
+BOOL m_bAddSize
|
||||
+DWORD m_dwDownloadSize
|
||||
+int runningThreadCnt
|
||||
+int nComplete
|
||||
+CString m_strServer
|
||||
+CString m_strObject
|
||||
+CString m_strSavePath
|
||||
+CString m_strTempSavePath
|
||||
+CString m_strReferer
|
||||
+CString m_strDownloadUrl
|
||||
+CTime m_TimeLastModified
|
||||
+UINT m_nPort
|
||||
+FILE* globalFile
|
||||
+DWORD m_dwFileSize
|
||||
+int m_dwThreadCnt
|
||||
+InforImpl inforImpl
|
||||
+NewTask TaskInfo
|
||||
+CSocket m_pSocket
|
||||
+HttpDownload()
|
||||
+virtual ~HttpDownload()
|
||||
+void FreeMemory()
|
||||
+void SetStop(bool stop)
|
||||
+BOOL StartHttpTask(NewTask task)
|
||||
+bool CreateNewFile(CString fileName, DWORD size)
|
||||
+void GetInfofromDevision()
|
||||
+bool GetInfofromTemp(CString filename)
|
||||
+void CreateInforImpl(CString str)
|
||||
+UINT SendRequest(BOOL bHead = false)
|
||||
+UINT GetInfo(LPCTSTR lpszHeader, DWORD& dwContentLength, DWORD& dwStatusCode, CTime& TimeLastModified)
|
||||
+BOOL ParseURL(CString str)
|
||||
+CTime GetTime(LPCTSTR lpszTime)
|
||||
+bool WriteInfoToFile()
|
||||
+bool WriteToFile(CString filePath, DWORD pos, char* buffer, int len)
|
||||
+void CreateThread()
|
||||
+bool Download(int index)
|
||||
}
|
||||
class InforImpl {
|
||||
+CString server
|
||||
+CString object
|
||||
+CString savePath
|
||||
+DWORD fileSize
|
||||
+int threadCnt
|
||||
+DWORD downloadSize
|
||||
+FromToImpl* fromToImpl
|
||||
}
|
||||
class FromToImpl {
|
||||
+DWORD from
|
||||
+DWORD to
|
||||
}
|
||||
class NewTask {
|
||||
+CString server
|
||||
+CString object
|
||||
+UINT port
|
||||
+UINT threadCnt
|
||||
+CString savepath
|
||||
}
|
||||
HttpDownload --> InforImpl : "contains"
|
||||
HttpDownload --> FromToImpl : "contains"
|
||||
HttpDownload --> NewTask : "uses"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [HttpDownload.h](file://h\HttpDownload.h#L69-L154)
|
||||
- [HttpDownload.cpp](file://cpp\Tools\HttpDownload.cpp#L26-L46)
|
||||
|
||||
**多线程下载**
|
||||
`StartHttpTask`方法首先发送HTTP请求获取文件大小,然后根据文件大小和线程数分配下载任务。每个线程负责下载文件的一部分,通过`Range`头指定下载范围。
|
||||
|
||||
**断点续传**
|
||||
`GetInfofromTemp`方法从临时配置文件中读取已下载的文件信息,实现断点续传功能。如果文件大小发生变化,则重新开始下载。
|
||||
|
||||
**Section sources**
|
||||
- [HttpDownload.cpp](file://cpp\Tools\HttpDownload.cpp#L68-L219)
|
||||
- [HttpDownload.cpp](file://cpp\Tools\HttpDownload.cpp#L104-L209)
|
||||
|
||||
## 应用场景与调用示例
|
||||
|
||||
### 设备连接
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 应用程序
|
||||
participant SComPort
|
||||
participant 串口硬件
|
||||
应用程序->>SComPort : OpenComm("COM1")
|
||||
SComPort->>串口硬件 : CreateFile("\\\\.\\COM1")
|
||||
串口硬件-->>SComPort : 返回句柄
|
||||
SComPort->>SComPort : 设置DCB参数
|
||||
SComPort-->>应用程序 : 返回TRUE
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [SComPort.cpp](file://cpp\Tools\SComPort.cpp#L127-L223)
|
||||
|
||||
### 固件升级
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 应用程序
|
||||
participant Zmodem
|
||||
participant SComPort
|
||||
应用程序->>Zmodem : Send(["firmware.bin"])
|
||||
Zmodem->>SComPort : SendDataDirectly(ZRQINIT帧)
|
||||
SComPort->>Zmodem : 接收响应
|
||||
Zmodem->>Zmodem : 发送ZFILE帧
|
||||
Zmodem->>SComPort : 发送数据帧
|
||||
loop 数据传输
|
||||
SComPort->>Zmodem : 接收数据帧
|
||||
Zmodem->>SComPort : 发送ZACK确认
|
||||
end
|
||||
Zmodem->>SComPort : 发送ZFIN帧
|
||||
SComPort->>Zmodem : 接收ZFIN响应
|
||||
Zmodem-->>应用程序 : 返回TRUE
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Zmodem.cpp](file://cpp\Tools\Zmodem.cpp#L62-L127)
|
||||
|
||||
### 数据同步
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 应用程序
|
||||
participant TcpClient
|
||||
participant 服务器
|
||||
应用程序->>TcpClient : ConnectToServer("192.168.1.100", 8080)
|
||||
TcpClient->>服务器 : 建立TCP连接
|
||||
服务器-->>TcpClient : 连接成功
|
||||
TcpClient-->>应用程序 : 返回TRUE
|
||||
应用程序->>TcpClient : SendData(同步数据)
|
||||
TcpClient->>服务器 : 发送数据
|
||||
服务器-->>TcpClient : 返回响应
|
||||
TcpClient-->>应用程序 : 返回发送字节数
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L101-L130)
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 线程同步
|
||||
使用Windows API的同步机制,如`CreateThread`和`WaitForSingleObject`,确保多线程环境下的数据一致性。
|
||||
|
||||
### 资源释放
|
||||
在析构函数中释放所有分配的资源,如关闭文件句柄、套接字和线程句柄。
|
||||
|
||||
### 异常处理
|
||||
使用`try-catch`块捕获异常,并通过日志记录详细的错误信息。
|
||||
|
||||
## 结论
|
||||
GeomativeStudio的通信工具集成功了多种通信方式,为设备连接、固件升级和数据同步提供了可靠的基础。通过深入分析这些模块的实现机制,开发者可以更好地理解和使用这些工具,提高开发效率和系统稳定性。
|
||||
Reference in New Issue
Block a user