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

【嵌入式——QT】QT集成Ymodem协议使用UDP进行传输

【嵌入式——QT】QT集成Ymodem协议使用UDP进行传输

  • Ymodem协议
    • 帧的数据格式
      • 帧头
      • 包号
      • 校验
    • 通讯过程
      • 握手信号
      • 起始帧
      • 数据帧
      • 结束帧
      • 代码块
    • Ymodem命令
  • QT实现
    • YmodemFileTransmit.h
    • YmodemFileTransmit.cpp
    • BootLoader.h
    • BootLoader.cpp
    • Ymodem协议源码

Ymodem协议

帧的数据格式

帧头、包号、包号反码、数据、校验。

帧头包号包号反码数据校验高位校验低位
Soh/Stx0x000xFFDATACRC_HCRC_L

帧头

以Soh(0x01)开始的数据包,信息块是128字节,该帧类型总长度为133字节。
以Stx(0x02)开始的数据包,信息块是1024字节,该帧类型总长度为1029字节。

包号

包号是为数据块的编号,将要传送的数据进行分块编号,只有一个字节,范围为0~255。大于255的则归零重复计算。

校验

Ymodem采用的是CRC16校验算法,校验值为2字节。

uint16_t Ymodem::crc16(uint8_t *buff, uint32_t len)
{uint16_t crc = 0;while(len--){crc ^= (uint16_t)(*(buff++)) << 8;for(int i = 0; i < 8; i++){if(crc & 0x8000){crc = (crc << 1) ^ 0x1021;}else{crc = crc << 1;}}}return crc;
}

通讯过程

握手信号

发送方收到接收方发送的CodeC(0x43)命令后,才可以开始发送起始帧。

起始帧

帧头包号包号反码文件名称文件大小填充区校验高位校验低位
CodeSoh0x000xFFFileName+0x00FileSize+0x00NULL(0x00)CRC_HCRC_L

文件名称后必须添加0x00作为结束,文件大小值后必须加0x00作为结束,余下的位置以0x00填充。

数据帧

帧头包号包号反码有效数据校验高位校验低位
CodeSoh/CodeStx0x000xFFDATACRC_HCRC_L

对于SOH帧,若余下数据小于128字节,则以0x1A填充,该帧长度仍为133字节;
对于STX帧需考虑几种情况:

  • 余下数据等于1024字节,以1029长度帧发送;
  • 余下数据小于1024字节,但大于128字节,以1029字节帧长度发送,无效数据以0x1A填充;
  • 余下数据等于128字节,以133字节帧长度发送;
  • 余下数据小于128字节,以133字节帧长度发送,无效数据以0x1A填充;

结束帧

帧头包号包号反码数据区校验高位校验低位
CodeSoh0x000xFFDATACRC_HCRC_L
数据区,校验都以0x00填充。

代码块

void Ymodem::transmit()
{switch(stage){case StageNone:{transmitStageNone();break;}case StageEstablishing:{transmitStageEstablishing();break;}case StageEstablished:{transmitStageEstablished();break;}case StageTransmitting:{transmitStageTransmitting();break;}case StageFinishing:{transmitStageFinishing();break;}default:{transmitStageFinished();}}
}

Ymodem命令

CodeEot、CodeCan由发送端发送;
CodeAck、CodeNak、CodeC由接收端发送;

命令命令码备注
CodeNone0x00
CodeSoh0x01133字节长度
CodeStx0x021024字节长度
CodeEot0x04文件传输结束指令
CodeAck0x06接收正确指令
CodeNak0x15重传当前数据包请求指令
CodeCan0x18取消传输指令,连续发送5个该命令,终止传输
CodeC0x43字符C
CodeA10x41
CodeA20x61

QT实现

YmodemFileTransmit.h

#ifndef YMODEMFILETRANSMIT_H
#define YMODEMFILETRANSMIT_H#include <QFile>
#include <QTimer>
#include <QObject>
#include "Ymodem.h"
#include <QUdpSocket>class YmodemFileTransmit : public QObject, public Ymodem
{Q_OBJECTpublic:explicit YmodemFileTransmit(QObject* parent = nullptr);~YmodemFileTransmit();void setFileName(const QString& name);void setIpAddress(const QString& ip);void setPortNumber(quint16 port);bool startTransmit();void stopTransmit();int getTransmitProgress();Status getTransmitStatus();signals:void transmitProgress(int progress);void transmitStatus(YmodemFileTransmit::Status status);public slots:void readTimeOut();void writeTimeOut();private:Code callback(Status status, uint8_t* buff, uint32_t* len);uint32_t read(uint8_t* buff, uint32_t len);uint32_t write(uint8_t* buff, uint32_t len);QFile*       file;QTimer*      readTimer;QTimer*      writeTimer;QUdpSocket* udpClient;int      progress;Status   status;uint64_t fileSize;uint64_t fileCount;QString  serverIp;uint16_t serverPort;
};#endif // YMODEMFILETRANSMIT_H

YmodemFileTransmit.cpp

#include "YmodemFileTransmit.h"
#include <QFileInfo>
#include <QNetworkDatagram>
#include <QThread>#define READ_TIME_OUT   (10)
#define WRITE_TIME_OUT  (1000)YmodemFileTransmit::YmodemFileTransmit(QObject* parent) :QObject(parent),file(new QFile),readTimer(new QTimer),writeTimer(new QTimer),udpClient(new QUdpSocket)
{setTimeDivide(499);setTimeMax(5);setErrorMax(999);connect(readTimer, SIGNAL(timeout()), this, SLOT(readTimeOut()));connect(writeTimer, SIGNAL(timeout()), this, SLOT(writeTimeOut()));
}YmodemFileTransmit::~YmodemFileTransmit()
{delete file;delete readTimer;delete writeTimer;delete udpClient;
}void YmodemFileTransmit::setFileName(const QString& name)
{file->setFileName(name);
}void YmodemFileTransmit::setIpAddress(const QString& ip)
{serverIp = ip;
}void YmodemFileTransmit::setPortNumber(quint16 port)
{serverPort = port;
}bool YmodemFileTransmit::startTransmit()
{progress = 0;status   = StatusEstablish;QByteArray array;array.append(0x02);array.append(0x01);array.append(0xFF);array.append(0x07);array.append(0x01);array.append(0x09);array.append(0x03);QHostAddress targetAddr(serverIp);int ret = udpClient->writeDatagram(array, targetAddr, serverPort);if(ret > 0) {QThread::msleep(50);readTimer->start(READ_TIME_OUT);return true;} else {return false;}
}void YmodemFileTransmit::stopTransmit()
{file->close();abort();status = StatusAbort;writeTimer->start(WRITE_TIME_OUT);
}int YmodemFileTransmit::getTransmitProgress()
{return progress;
}Ymodem::Status YmodemFileTransmit::getTransmitStatus()
{return status;
}void YmodemFileTransmit::readTimeOut()
{readTimer->stop();transmit();if((status == StatusEstablish) || (status == StatusTransmit)) {readTimer->start(READ_TIME_OUT);}
}void YmodemFileTransmit::writeTimeOut()
{writeTimer->stop();transmitStatus(status);
}Ymodem::Code YmodemFileTransmit::callback(Status status, uint8_t* buff, uint32_t* len)
{switch(status) {case StatusEstablish:if(file->open(QFile::ReadOnly) == true) {QFileInfo fileInfo(*file);fileSize  = fileInfo.size();fileCount = 0;strcpy((char*)buff, fileInfo.fileName().toLocal8Bit().data());strcpy((char*)buff + fileInfo.fileName().toLocal8Bit().size() + 1, QByteArray::number(fileInfo.size()).data());*len = YMODEM_PACKET_SIZE;YmodemFileTransmit::status = StatusEstablish;transmitStatus(StatusEstablish);return CodeAck;} else {YmodemFileTransmit::status = StatusError;writeTimer->start(WRITE_TIME_OUT);return CodeCan;}case StatusTransmit:if(fileSize != fileCount) {if((fileSize - fileCount) > YMODEM_PACKET_SIZE) {fileCount += file->read((char*)buff, YMODEM_PACKET_1K_SIZE);*len = YMODEM_PACKET_1K_SIZE;} else {fileCount += file->read((char*)buff, YMODEM_PACKET_SIZE);*len = YMODEM_PACKET_SIZE;}progress = (int)(fileCount * 100 / fileSize);YmodemFileTransmit::status = StatusTransmit;transmitProgress(progress);transmitStatus(StatusTransmit);return CodeAck;} else {YmodemFileTransmit::status = StatusTransmit;transmitStatus(StatusTransmit);return CodeEot;}case StatusFinish:file->close();YmodemFileTransmit::status = StatusFinish;writeTimer->start(WRITE_TIME_OUT);return CodeAck;case StatusAbort:file->close();YmodemFileTransmit::status = StatusAbort;writeTimer->start(WRITE_TIME_OUT);return CodeCan;case StatusTimeout:YmodemFileTransmit::status = StatusTimeout;writeTimer->start(WRITE_TIME_OUT);return CodeCan;default:file->close();YmodemFileTransmit::status = StatusError;writeTimer->start(WRITE_TIME_OUT);return CodeCan;}
}uint32_t YmodemFileTransmit::read(uint8_t* buff, uint32_t len)
{QNetworkDatagram datagram =udpClient->receiveDatagram(len);QByteArray array = datagram.data();uint32_t lenArray = array.size();uint32_t lenBuff  = len;uint32_t length = qMin(lenArray, lenBuff);memcpy(buff, array, length);return length;
}uint32_t YmodemFileTransmit::write(uint8_t* buff, uint32_t len)
{QHostAddress targetAddr(serverIp);int ret = udpClient->writeDatagram((char*)buff, len, targetAddr, serverPort);return ret;
}

BootLoader.h

#ifndef BOOTLOADER_H
#define BOOTLOADER_H#include <QWidget>
#include <QUdpSocket>
#include <YmodemFileTransmit.h>QT_BEGIN_NAMESPACE
namespace Ui
{class BootLoader;
}
QT_END_NAMESPACEclass BootLoader : public QWidget
{Q_OBJECTpublic:BootLoader(QWidget* parent = nullptr);~BootLoader();YmodemFileTransmit* ymodemFileTransmit;public slots:void on_pushButtonConnect_clicked();void on_pushButtonBrowse_clicked();void on_pushButtonSend_clicked();void readData();void transmitProgress(int progress);void transmitStatus(YmodemFileTransmit::Status status);private:Ui::BootLoader* ui;bool firemwareTransmitStatus;QUdpSocket* udpClient;
};
#endif // BOOTLOADER_H

BootLoader.cpp

#include "BootLoader.h"
#include "ui_BootLoader.h"
#include <QByteArray>
#include <QDebug>
#include <QNetworkDatagram>
#include <QFileDialog>
#include <QMessageBox>#define SERVER_ADDR "192.168.xxx.xxx"
#define SERVER_PORT 4002BootLoader::BootLoader(QWidget* parent): QWidget(parent), ui(new Ui::BootLoader)
{ui->setupUi(this);this->setWindowTitle(tr("EthernetYmodem"));this->setWindowIcon(QIcon(":/images/main.ico"));ymodemFileTransmit = new YmodemFileTransmit();connect(ymodemFileTransmit, SIGNAL(transmitProgress(int)), this, SLOT(transmitProgress(int)));connect(ymodemFileTransmit, SIGNAL(transmitStatus(YmodemFileTransmit::Status)), this, SLOT(transmitStatus(YmodemFileTransmit::Status)));udpClient = new QUdpSocket(this);connect(udpClient, &QUdpSocket::readyRead, this, &BootLoader::readData);firemwareTransmitStatus = false;ui->pushButtonSend->setEnabled(false);ui->pushButtonConnect->setEnabled(true);
}BootLoader::~BootLoader()
{delete ui;
}void BootLoader::on_pushButtonConnect_clicked()
{QByteArray array;array.append(0x02);array.append(0x01);array.append(0xFF);array.append(0x07);array.append(0x01);array.append(0x09);array.append(0x03);QHostAddress targetAddr(SERVER_ADDR);int ret = udpClient->writeDatagram(array, targetAddr, SERVER_PORT);qDebug()<<"ret"<<ret;
}void BootLoader::on_pushButtonBrowse_clicked()
{QString curPath = QDir::currentPath();ui->lineEditFilePath->setText(QFileDialog::getOpenFileName(this, u8"打开文件", curPath, u8"任意文件 (*.*)"));
}void BootLoader::on_pushButtonSend_clicked()
{udpClient->abort();if(ui->lineEditFilePath->text().isEmpty()) {QMessageBox::warning(this, tr("!!!"), tr("Please select a file!"));return;}if(firemwareTransmitStatus == false) {ymodemFileTransmit->setFileName(ui->lineEditFilePath->text());ymodemFileTransmit->setIpAddress(SERVER_ADDR);ymodemFileTransmit->setPortNumber(SERVER_PORT);if(ymodemFileTransmit->startTransmit() == true) {firemwareTransmitStatus = true;ui->progressBar->setValue(0);ui->pushButtonSend->setText(tr("Cancel"));ui->pushButtonConnect->setEnabled(false);} else {QMessageBox::warning(this, tr("Failure"), tr("File failed to send!"), tr("Closed"));ui->pushButtonSend->setText(tr("Send"));ui->pushButtonConnect->setEnabled(true);}} else {ymodemFileTransmit->stopTransmit();ui->pushButtonSend->setText(tr("Send"));ui->pushButtonConnect->setEnabled(true);}
}void BootLoader::readData()
{while(udpClient->hasPendingDatagrams()) {QNetworkDatagram datagram = udpClient->receiveDatagram();QByteArray receivedData = datagram.data();qDebug() << "Received data:" << receivedData;if(receivedData.size() > 0 && receivedData[0] == (char)0x43) {ui->pushButtonSend->setEnabled(true);ui->pushButtonConnect->setEnabled(false);}}
}void BootLoader::transmitProgress(int progress)
{ui->progressBar->setValue(progress);
}void BootLoader::transmitStatus(Ymodem::Status status)
{switch(status) {case YmodemFileTransmit::StatusEstablish:break;case YmodemFileTransmit::StatusTransmit:break;case YmodemFileTransmit::StatusFinish:firemwareTransmitStatus = false;QMessageBox::information(this, tr("OK"), tr("Upgrade successed!"), QMessageBox::Yes);ui->pushButtonSend->setText(tr("Send"));ui->pushButtonSend->setEnabled(false);ui->pushButtonConnect->setEnabled(true);break;case YmodemFileTransmit::StatusAbort:firemwareTransmitStatus = false;QMessageBox::warning(this, tr("failure"), tr("File failed to send!"), tr("Closed"));break;case YmodemFileTransmit::StatusTimeout:firemwareTransmitStatus = false;QMessageBox::warning(this, tr("failure"), tr("File failed to send!"), tr("Closed"));break;default:firemwareTransmitStatus = false;QMessageBox::warning(this, tr("failure"), tr("File failed to send!"), tr("Closed"));}
}

Ymodem协议源码

源码链接

相关文章:

【嵌入式——QT】QT集成Ymodem协议使用UDP进行传输

【嵌入式——QT】QT集成Ymodem协议使用UDP进行传输 Ymodem协议帧的数据格式帧头包号校验 通讯过程握手信号起始帧数据帧结束帧代码块 Ymodem命令 QT实现YmodemFileTransmit.hYmodemFileTransmit.cppBootLoader.hBootLoader.cppYmodem协议源码 Ymodem协议 帧的数据格式 帧头、…...

python笔记(17)输入输出

一、标准输入与输出简介 Python通过内置的sys模块管理标准输入&#xff08;stdin&#xff09;、标准输出&#xff08;stdout&#xff09;和标准错误&#xff08;stderr&#xff09;。但对大多数简单应用而言&#xff0c;直接使用内置函数就足够了。 二、输入&#xff1a;inpu…...

408数据结构总结复习笔记一:线性表

408数据结构总结复习笔记一&#xff1a;线性表 从现在开始慢慢更新我的考研复习笔记系列吧~ PS&#xff1a;主要是我自己个人复习过程中觉得重点的点&#xff0c;大家仅供参考哈~ 上岸&#xff01;&#xff01;&#xff01;大家一起加油! 顺序表和链表的比较 顺序表链表存取…...

Docker——目录迁移

我们在生产环境中安装Docker时&#xff0c;默认的安装目录是/var/lib/docker&#xff0c;而通常情况下&#xff0c;规划给系统盘的目录一般为50G&#xff0c;该目录是比较小的&#xff0c;一旦容器过多或容器日志过多&#xff0c;就可能出现Docker无法运行的情况&#xff0c;所…...

SpringAMQP-消息转换器

这边发送消息接收消息默认是jdk的序列化方式&#xff0c;发送到服务器是以字节码的形式&#xff0c;我们看不懂也很占内存&#xff0c;所以我们要手动设置一下 我这边设置成json的序列化方式&#xff0c;注意发送方和接收方的序列化方式要保持一致 不然回报错。 引入依赖&#…...

轻松拿下指针(5)

文章目录 一、回调函数是什么二、qsort使用举例三、qsort函数的模拟实现 一、回调函数是什么 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另⼀个函数&#xff0c;当这个指针被⽤来调⽤其所指向的函数 时&#x…...

Nginx反向代理配置

一、介绍 Nginx 的反向代理功能在现代网络架构中扮演着至关重要的角色。首先&#xff0c;它充当了客户端与后端服务器之间的中介。当客户端发送请求时&#xff0c;这些请求先到达 Nginx 服务器&#xff0c;Nginx 会根据预先设定的规则和配置&#xff0c;将请求准确地转发到相应…...

突破编程界限:探索AI编程新境界

文章目录 一、AI编程助手1.1 Baidu Comate智能代码助手1.2 阿里云 通义灵码 二、场景需求三、体验步骤3.1 官网下载3.2 手动下载 四、试用感受4.1 提示4.2 注释生成代码4.3 代码生成4.4 选中生成注释4.5 查看变更&新建文件4.6 调优建议4.7 插件使用 五、结尾推荐 一、AI编程…...

C语言(指针)2

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…...

go学习笔记

1基础搭建 1.1&#xff0c;安装vscode https://code.visualstudio.com/download 64位 1.2&#xff0c;Windows 下搭建Go 开发环境-安装和配置 SDK SDK 的全称(Software Development Kit 软件开发工具包) Go 语言的官网为&#xff1a;golang.org , 因为各种原因&#xff0c;可…...

MacApp自动化测试之Automator初体验

今天我们继续讲Automator的使用。 初体验 启动Automator程序&#xff0c;选择【工作流程】类型。从资源库区域依次将获取指定的URL、从网页中获得文本、新建文本文件三个操作拖进工作流创建区域。 然后修改内容&#xff0c;将获取指定的URL操作中的URL替换成https://www.cnb…...

Vue学习v-html

Vue学习v-html 一、前言1、基本用法2、注意事项 二、总结 一、前言 学习 Vue.js 中的 v-html 指令意味着你想要在你的应用程序中动态地渲染 HTML。这个指令允许你将数据中包含的 HTML 代码直接插入到你的模板中&#xff0c;而不是将其作为纯文本处理。虽然这个功能非常强大&am…...

C++并发:锁

一、前言 C中的锁和同步原语的多样化选择使得程序员可以根据具体的线程和数据保护需求来选择最合适的工具。这些工具的正确使用可以大大提高程序的稳定性和性能&#xff0c;本文讨论了部分锁。 二、std::lock 在C中&#xff0c;std::lock 是一个用于一次性锁定两个或多个互斥…...

Git | git log 和 git status 的区别

如是我闻&#xff1a; git log和git status是Git中的两个非常有用的命令&#xff0c;它们用于不同的目的&#xff0c;并提供不同类型的信息。 git log git log命令用于显示一个或多个分支的提交历史记录。这个命令会列出提交历史&#xff0c;包括每次提交的SHA-1哈希值、提交…...

Django 4.x 智能分页get_elided_page_range

Django智能分页 分页效果 第1页的效果 第10页的效果 带输入框的效果 主要函数 # 参数解释 # number: 当前页码&#xff0c;默认&#xff1a;1 # on_each_side&#xff1a;当前页码前后显示几页&#xff0c;默认&#xff1a;3 # on_ends&#xff1a;首尾固定显示几页&#…...

java-spring 09 下.populateBean (方法成员变量的注入@Autowird,@Resource)

1.在populateBean 方法中的一部分&#xff1a;用于Autowird&#xff0c;Resource注入 // 后处理器已经初始化boolean hasInstAwareBpps hasInstantiationAwareBeanPostProcessors();// 需要依赖检查boolean needsDepCheck (mbd.getDependencyCheck() ! AbstractBeanDefinitio…...

赛氪网携手众机构助力第七届京津冀生态修复实践论坛圆满落幕

近日&#xff0c;由北京生态修复学会联合工业固废网、中国老科协国土资源分会共同主办&#xff0c;赛氪网作为支持单位的第七届京津冀生态修复实践论坛在北京温德姆酒店圆满落幕。本次论坛汇聚了众多行业专家、学者以及企业代表&#xff0c;共同探讨生态修复领域的新技术、新方…...

Naive RAG 、Advanced RAG 和 Modular RAG 简介

简介&#xff1a; RAG&#xff08;Retrieval-Augmented Generation&#xff09;系统是一种结合了检索&#xff08;Retrieval&#xff09;和生成&#xff08;Generation&#xff09;的机制&#xff0c;用于提高大型语言模型&#xff08;LLMs&#xff09;在特定任务上的表现。随…...

Python高级编程-DJango2

Python高级编程-DJango2 没有清醒的头脑&#xff0c;再快的脚步也会走歪&#xff1b;没有谨慎的步伐&#xff0c;再平的道路也会跌倒。 目录 Python高级编程-DJango2 1.显示基本网页 2.输入框的形式&#xff1a; 1&#xff09;文本输入框 2&#xff09;单选框 3&#xff…...

bash脚本 报错:/bin/bash^M:解释器错误: 没有那个文件或目录

bash脚本 报错&#xff1a;/bin/bash^M&#xff1a;解释器错误: 没有那个文件或目录 出现这个问题是因为该脚本文件在windows下编辑过 在windows下&#xff0c;每一行的结尾是\n\r&#xff0c;而在linux下文件的结尾是\n&#xff0c;那么你在windows下编辑过的文件在linux下打…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...

怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)

+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...