当前位置: 首页 > news >正文

Qt 实现Ymodem协议源码分享

Qt 实现Ymodem协议源码分享

  • 一、Ymodem协议介绍
    • 1、协议基础特性
    • 2、传输流程分阶段
      • (1) 、初始化阶段
      • (2) 、数据传输阶段
      • (3) 、结束阶段
    • 3、关键改进与变体
    • 4、典型应用场景
  • 二、源码分享
    • 1、效果展示
    • 2、工程结构
    • 3、源码分享
  • 三、完整工程下载

在这里插入图片描述

一、Ymodem协议介绍

Ymodem协议是一种基于串行通信的文件传输协议,由Xmodem协议改进而来,主要用于通过RS-232等低速链路可靠传输文件。其核心设计目标是提高传输效率支持批处理传输,以下是分层解析:


1、协议基础特性

  • 数据块大小
    默认采用 1024字节(1KB)数据块(可回退至128字节)。
    数学表达传输效率提升:
    效率增益=有效数据总传输量∝10241024+头部开销\text{效率增益} = \frac{\text{有效数据}}{\text{总传输量}} \propto \frac{1024}{1024 + \text{头部开销}} 效率增益=总传输量有效数据1024+头部开销1024
    相比Xmodem的128字节块,传输开销显著降低。

  • 错误检测机制
    使用CRC-16(16位循环冗余校验)或可选校验和(Checksum)。
    校验多项式:$ G(x) = x^{16} + x^{15} + x^2 + 1 $(标准CRC-CCITT)。

  • 批处理支持
    支持单次会话传输多个文件,通过文件头块传递文件名和大小。


2、传输流程分阶段

(1) 、初始化阶段

  • 接收端发送'C'(ASCII 0x43)请求CRC模式(或NAK请求校验和模式)。
  • 发送端响应首个块(块编号 0),包含文件头信息:
    文件名 + 文件大小 + 时间戳(固定128字节,不足补NULL)。

(2) 、数据传输阶段

  • 块结构(每块共1028字节):
    | SOH/STX (1B) | 块号 (1B) | ~块号 (1B) | 数据 (1024B) | CRC (2B) |
    
    • SOH(0x01)标识128字节块,STX(0x02)标识1024字节块。
    • 块号范围:$ 1 \leq \text{块号} \leq 255 $(模256循环)。
    • 数据不足1024字节时以EOF(0x1A)填充。
  • 接收端响应
    • ACK(0x06):成功接收,请求下一块。
    • NAK(0x15):校验失败,请求重传。
    • CAN(0x18):取消传输。

(3) 、结束阶段

  • 发送端发出EOT(0x04)表示文件结束。
  • 接收端回应ACK后,若需传输下一文件,重复初始化阶段。
  • 无后续文件时,发送端发出NULL(0x00)结束会话。

3、关键改进与变体

  • Ymodem-g(流式传输)
    取消接收端确认(ACK/NAK),数据连续发送。要求物理链路0误码率,否则整体重传。
  • 与Xmodem对比优势
    特性XmodemYmodem
    块大小128字节1024字节
    批处理不支持支持
    文件名传递文件头块实现
    最大文件大小8MB限制理论无限制

4、典型应用场景

  • 嵌入式系统:固件升级(如路由器Bootloader)。
  • 工业控制:PLC程序传输。
  • 遗留系统:通过串口/Telnet传输文件(如Linuxsz/rz工具)。

:现代应用中,Ymodem因速率限制(通常<115.2kbps)逐渐被Zmodem取代,但其简洁性仍适用于低资源环境。实际实现需注意超时重传(默认10秒)和块号翻转处理

二、源码分享

1、效果展示

在这里插入图片描述

2、工程结构

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0acfed9e90324fb3aef3866eb3c3987b.png

3、源码分享

widget.hpp

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include "YmodemFileTransmit.h"
#include "YmodemFileReceive.h"namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();private slots:void on_comButton_clicked();void on_transmitBrowse_clicked();void on_receiveBrowse_clicked();void on_transmitButton_clicked();void on_receiveButton_clicked();void transmitProgress(int progress);void receiveProgress(int progress);void transmitStatus(YmodemFileTransmit::Status status);void receiveStatus(YmodemFileReceive::Status status);private:Ui::Widget *ui;QSerialPort *serialPort;YmodemFileTransmit *ymodemFileTransmit;YmodemFileReceive *ymodemFileReceive;bool transmitButtonStatus;bool receiveButtonStatus;
};#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QFileDialog>
#include <QSerialPortInfo>Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget),serialPort(new QSerialPort),ymodemFileTransmit(new YmodemFileTransmit),ymodemFileReceive(new YmodemFileReceive)
{transmitButtonStatus = false;receiveButtonStatus  = false;ui->setupUi(this);QSerialPortInfo serialPortInfo;foreach(serialPortInfo, QSerialPortInfo::availablePorts()){ui->comPort->addItem(serialPortInfo.portName());}serialPort->setPortName("COM1");serialPort->setBaudRate(115200);serialPort->setDataBits(QSerialPort::Data8);serialPort->setStopBits(QSerialPort::OneStop);serialPort->setParity(QSerialPort::NoParity);serialPort->setFlowControl(QSerialPort::NoFlowControl);connect(ymodemFileTransmit, SIGNAL(transmitProgress(int)), this, SLOT(transmitProgress(int)));connect(ymodemFileReceive, SIGNAL(receiveProgress(int)), this, SLOT(receiveProgress(int)));connect(ymodemFileTransmit, SIGNAL(transmitStatus(YmodemFileTransmit::Status)), this, SLOT(transmitStatus(YmodemFileTransmit::Status)));connect(ymodemFileReceive, SIGNAL(receiveStatus(YmodemFileReceive::Status)), this, SLOT(receiveStatus(YmodemFileReceive::Status)));
}Widget::~Widget()
{delete ui;delete serialPort;delete ymodemFileTransmit;delete ymodemFileReceive;
}void Widget::on_comButton_clicked()
{static bool button_status = false;if(button_status == false){serialPort->setPortName(ui->comPort->currentText());serialPort->setBaudRate(ui->comBaudRate->currentText().toInt());if(serialPort->open(QSerialPort::ReadWrite) == true){button_status = true;ui->comPort->setDisabled(true);ui->comBaudRate->setDisabled(true);ui->comButton->setText(u8"关闭串口");ui->transmitBrowse->setEnabled(true);ui->receiveBrowse->setEnabled(true);if(ui->transmitPath->text().isEmpty() != true){ui->transmitButton->setEnabled(true);}if(ui->receivePath->text().isEmpty() != true){ui->receiveButton->setEnabled(true);}}else{QMessageBox::warning(this, u8"串口打开失败", u8"请检查串口是否已被占用!", u8"关闭");}}else{button_status = false;serialPort->close();ui->comPort->setEnabled(true);ui->comBaudRate->setEnabled(true);ui->comButton->setText(u8"打开串口");ui->transmitBrowse->setDisabled(true);ui->transmitButton->setDisabled(true);ui->receiveBrowse->setDisabled(true);ui->receiveButton->setDisabled(true);}
}void Widget::on_transmitBrowse_clicked()
{ui->transmitPath->setText(QFileDialog::getOpenFileName(this, u8"打开文件", ".", u8"任意文件 (*.*)"));if(ui->transmitPath->text().isEmpty() != true){ui->transmitButton->setEnabled(true);}else{ui->transmitButton->setDisabled(true);}
}void Widget::on_receiveBrowse_clicked()
{ui->receivePath->setText(QFileDialog::getExistingDirectory(this, u8"选择目录", ".", QFileDialog::ShowDirsOnly));if(ui->receivePath->text().isEmpty() != true){ui->receiveButton->setEnabled(true);}else{ui->receiveButton->setDisabled(true);}
}void Widget::on_transmitButton_clicked()
{if(transmitButtonStatus == false){serialPort->close();ymodemFileTransmit->setFileName(ui->transmitPath->text());ymodemFileTransmit->setPortName(ui->comPort->currentText());ymodemFileTransmit->setPortBaudRate(ui->comBaudRate->currentText().toInt());if(ymodemFileTransmit->startTransmit() == true){transmitButtonStatus = true;ui->comButton->setDisabled(true);ui->receiveBrowse->setDisabled(true);ui->receiveButton->setDisabled(true);ui->transmitBrowse->setDisabled(true);ui->transmitButton->setText(u8"取消");ui->transmitProgress->setValue(0);}else{QMessageBox::warning(this, u8"失败", u8"文件发送失败!", u8"关闭");}}else{ymodemFileTransmit->stopTransmit();}
}void Widget::on_receiveButton_clicked()
{if(receiveButtonStatus == false){serialPort->close();ymodemFileReceive->setFilePath(ui->receivePath->text());ymodemFileReceive->setPortName(ui->comPort->currentText());ymodemFileReceive->setPortBaudRate(ui->comBaudRate->currentText().toInt());if(ymodemFileReceive->startReceive() == true){receiveButtonStatus = true;ui->comButton->setDisabled(true);ui->transmitBrowse->setDisabled(true);ui->transmitButton->setDisabled(true);ui->receiveBrowse->setDisabled(true);ui->receiveButton->setText(u8"取消");ui->receiveProgress->setValue(0);}else{QMessageBox::warning(this, u8"失败", u8"文件接收失败!", u8"关闭");}}else{ymodemFileReceive->stopReceive();}
}void Widget::transmitProgress(int progress)
{ui->transmitProgress->setValue(progress);
}void Widget::receiveProgress(int progress)
{ui->receiveProgress->setValue(progress);
}void Widget::transmitStatus(Ymodem::Status status)
{switch(status){case YmodemFileTransmit::StatusEstablish:{break;}case YmodemFileTransmit::StatusTransmit:{break;}case YmodemFileTransmit::StatusFinish:{transmitButtonStatus = false;ui->comButton->setEnabled(true);ui->receiveBrowse->setEnabled(true);if(ui->receivePath->text().isEmpty() != true){ui->receiveButton->setEnabled(true);}ui->transmitBrowse->setEnabled(true);ui->transmitButton->setText(u8"发送");QMessageBox::warning(this, u8"成功", u8"文件发送成功!", u8"关闭");break;}case YmodemFileTransmit::StatusAbort:{transmitButtonStatus = false;ui->comButton->setEnabled(true);ui->receiveBrowse->setEnabled(true);if(ui->receivePath->text().isEmpty() != true){ui->receiveButton->setEnabled(true);}ui->transmitBrowse->setEnabled(true);ui->transmitButton->setText(u8"发送");QMessageBox::warning(this, u8"失败", u8"文件发送失败!", u8"关闭");break;}case YmodemFileTransmit::StatusTimeout:{transmitButtonStatus = false;ui->comButton->setEnabled(true);ui->receiveBrowse->setEnabled(true);if(ui->receivePath->text().isEmpty() != true){ui->receiveButton->setEnabled(true);}ui->transmitBrowse->setEnabled(true);ui->transmitButton->setText(u8"发送");QMessageBox::warning(this, u8"失败", u8"文件发送失败!", u8"关闭");break;}default:{transmitButtonStatus = false;ui->comButton->setEnabled(true);ui->receiveBrowse->setEnabled(true);if(ui->receivePath->text().isEmpty() != true){ui->receiveButton->setEnabled(true);}ui->transmitBrowse->setEnabled(true);ui->transmitButton->setText(u8"发送");QMessageBox::warning(this, u8"失败", u8"文件发送失败!", u8"关闭");}}
}void Widget::receiveStatus(YmodemFileReceive::Status status)
{switch(status){case YmodemFileReceive::StatusEstablish:{break;}case YmodemFileReceive::StatusTransmit:{break;}case YmodemFileReceive::StatusFinish:{receiveButtonStatus = false;ui->comButton->setEnabled(true);ui->transmitBrowse->setEnabled(true);if(ui->transmitPath->text().isEmpty() != true){ui->transmitButton->setEnabled(true);}ui->receiveBrowse->setEnabled(true);ui->receiveButton->setText(u8"接收");QMessageBox::warning(this, u8"成功", u8"文件接收成功!", u8"关闭");break;}case YmodemFileReceive::StatusAbort:{receiveButtonStatus = false;ui->comButton->setEnabled(true);ui->transmitBrowse->setEnabled(true);if(ui->transmitPath->text().isEmpty() != true){ui->transmitButton->setEnabled(true);}ui->receiveBrowse->setEnabled(true);ui->receiveButton->setText(u8"接收");QMessageBox::warning(this, u8"失败", u8"文件接收失败!", u8"关闭");break;}case YmodemFileReceive::StatusTimeout:{receiveButtonStatus = false;ui->comButton->setEnabled(true);ui->transmitBrowse->setEnabled(true);if(ui->transmitPath->text().isEmpty() != true){ui->transmitButton->setEnabled(true);}ui->receiveBrowse->setEnabled(true);ui->receiveButton->setText(u8"接收");QMessageBox::warning(this, u8"失败", u8"文件接收失败!", u8"关闭");break;}default:{receiveButtonStatus = false;ui->comButton->setEnabled(true);ui->transmitBrowse->setEnabled(true);if(ui->transmitPath->text().isEmpty() != true){ui->transmitButton->setEnabled(true);}ui->receiveBrowse->setEnabled(true);ui->receiveButton->setText(u8"接收");QMessageBox::warning(this, u8"失败", u8"文件接收失败!", u8"关闭");}}
}

三、完整工程下载

https://download.csdn.net/download/qq_15181569/91667788

在这里插入图片描述

http://www.xdnf.cn/news/1295641.html

相关文章:

  • MySQL工具包中的其他程序
  • 从概率填充到置信度校准:GPT-5如何从底层重构AI的“诚实”机制
  • 树莓派 4B 上部署 Minecraft PaperMC 1.20.x 的一键部署脚本
  • ASQA: 面向模糊性事实问题的长格式问答数据集与评估框架
  • C#WPF实战出真汁02--登录界面设计
  • 利用 Python 爬虫按图搜索 1688 商品(拍立淘)实战指南
  • Windows批处理脚本自动合并当前目录下由You-get下载的未合并的音视频文件
  • LeetCode 分类刷题:2302. 统计得分小于 K 的子数组数目
  • 我的第一个开源项目-jenkins集成k8s项目
  • 开疆智能Ethernet转ModbusTCP网关连接UR机器人配置案例
  • 区块链 + 域名Web3时代域名投资的新风口(上)
  • 《算法导论》第 25 章:所有结点对的最短路径问题
  • 常见的tls检测的绕过方案
  • Mybatis学习笔记(二)
  • Transformer之多头注意力机制和位置编码(二)
  • vue更改style
  • 双椒派E2000D网络故障排查指南
  • 【Linux】库制作与原理
  • 2025年5月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(三)
  • 苹果正计划大举进军人工智能硬件领域
  • 解决EKS中KEDA访问AWS SQS权限问题:完整的IRSA配置指南
  • 能源行业数字化转型:边缘计算网关在油田场景的深度应用
  • 支持pcm语音文件缓存顺序播放
  • 从感知到执行:人形机器人低延迟视频传输与多模态同步方案解析
  • Python 类元编程(导入时和运行时比较)
  • 【Linux学习|黑马笔记|Day3】root用户、查看权限控制信息、chmod、chown、快捷键、软件安装、systemctl、软连接、日期与时区
  • 17. 如何判断一个对象是不是数组
  • 技术速递|使用 AI Toolkit 构建基于 gpt-oss-20b 的应用程序
  • 工业元宇宙:迈向星辰大海的“玄奘之路”
  • 【Linux】常用命令(三)