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

C# 设计一个可变长度的数据通信协议编码和解码代码。

设计一个可变长度的数据通信协议编码和解码代码。
要有本机ID字段,远端设备ID字段,指令类型字段,数据体字段,校验字段。其中一个要求是,每次固定收发八个字节,单个数据帧超过八个字节需要分包收发。对接收的数据帧要先存入环形缓存区,解码函数需要对环形缓存区中的协议数据持续解码,直到没有数据。解析出的数据最后逐个列出来,验证对错。对于存在的丢包问题,要求有重发机制。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace edcode_s
{/** Packet 类表示一个数据包,* 包含本地ID、远程ID、命令类型、数据长度和数据内容等字段。*/class Packet{public byte LocalID;public byte RemoteID;public byte CommandType;public byte DataLength;public byte[] Data = new byte[255];public ushort CRC;}/** RingBuffer 类实现了一个环形缓冲区,* 用于存储发送和接收的数据。* 它有一个固定大小的缓冲区,* 通过添加和读取操作来管理数据。*/class RingBuffer{private const int BufferSize = 1024;private byte[] buffer = new byte[BufferSize];private int start = 0;private int end = 0;/** Add方法* 将给定的字节数组data中的数据按顺序添加到循环缓冲区中,* 直到填满整个缓冲区或达到指定的length长度。*/public void Add(byte[] data, int length){for (int i = 0; i < length; i++){buffer[end] = data[i];end = (end + 1) % BufferSize;}}/** Read方法* 从循环缓冲区中读取指定长度的数据,* 并将其存储到给定的字节数组中,* 从指定的偏移量开始存储。* 如果没有读取到任何数据(即缓冲区为空),* 则返回0;否则返回1表示成功读取了数据。*/public int Read(byte[] data, int offset, int length){if (IsEmpty())return 0;for (int i = 0; i < length; i++){data[offset + i] = buffer[start];start = (start + 1) % BufferSize;}return 1;}public bool IsEmpty(){return start == end;}}/** Protocol 类是通信协议的核心部分,* 负责发送和接收数据包。* 它使用 RingBuffer 来存储发送和接收的数据。*/class Protocol{private const int PacketSize = 8;private RingBuffer ringBuffer = new RingBuffer();private Random rand = new Random();/** SendPacket 方法将一个 Packet 对象转换为字节数组,并计算CRC校验码,* 然后将数据分块发送到缓冲区中。* 如果模拟了数据包丢失的情况,则会重新发送数据包。* */public void SendPacket(Packet packet){int totalLength = 4 + packet.DataLength + 2;    byte[] buffer = new byte[totalLength];buffer[0] = packet.LocalID;buffer[1] = packet.RemoteID;buffer[2] = packet.CommandType;buffer[3] = packet.DataLength;Array.Copy(packet.Data, 0, buffer, 4, packet.DataLength);ushort crc = ComputeCRC(buffer, totalLength - 2);//变量crc转换为字节数组,并将该字节数组复制到buffer字节数组中。//totalLength - 2 表示从 buffer 数组的索引位置 totalLength - 2 开始粘贴数据BitConverter.GetBytes(crc).CopyTo(buffer, totalLength - 2);for (int i = 0; i < totalLength; i += PacketSize){int chunkSize = Math.Min(PacketSize, totalLength - i);byte[] chunk = new byte[chunkSize];Array.Copy(buffer, i, chunk, 0, chunkSize);if (!SimulatePacketLoss(0.1f)) // 10% packet loss rate{ringBuffer.Add(chunk, chunkSize);}else{Console.WriteLine("Packet loss occurred, resending...");ResendPacket(chunk, chunkSize);}}}/** DecodePacket 方法从缓冲区中读取数据,* 解析出数据包的各个字段,* 并进行CRC校验。如果校验成功,则返回 true,否则返回 false。*/public bool DecodePacket(out Packet packet){packet = new Packet();if (ringBuffer.IsEmpty())return false;byte[] header = new byte[4];ringBuffer.Read(header, 0, 4);packet.LocalID = header[0];packet.RemoteID = header[1];packet.CommandType = header[2];packet.DataLength = header[3];packet.Data = new byte[packet.DataLength];ringBuffer.Read(packet.Data, 0, packet.DataLength);ushort receivedCRC;byte[] crcBytes = new byte[2];ringBuffer.Read(crcBytes, 0, 2);receivedCRC = BitConverter.ToUInt16(crcBytes, 0);byte[] fullPacket = new byte[4 + packet.DataLength];Array.Copy(header, 0, fullPacket, 0, 4);//将一个数组packet.Data的从0位置开始的长度为packet.DataLength的内容复制到另一个数组fullPacket的4位置。Array.Copy(packet.Data, 0, fullPacket, 4, packet.DataLength);ushort computedCRC = ComputeCRC(fullPacket, fullPacket.Length);if (receivedCRC != computedCRC){Console.WriteLine("CRC error");return false;}return true;}/** ResendPacket 方法将数据包重新添加到缓冲区中,以便重新发送。*/private void ResendPacket(byte[] data, int length){ringBuffer.Add(data, length);}/** SimulatePacketLoss 方法根据给定的丢包率随机决定是否模拟数据包丢失。*/private bool SimulatePacketLoss(float lossRate){return rand.NextDouble() < lossRate;//生成一个0到1之间的随机小数,并将其与lossRate进行比较。}/** ComputeCRC 方法计算给定数据的CRC校验码(CRC-16)。*/private ushort ComputeCRC(byte[] data, int length){/** 变量crc初始化为0xFFFF。然后使用两个嵌套的循环来遍历数据并计算CRC值。* 外层循环遍历整个数据数组,内层循环处理每个字节的每一位。* 在内层循环中,首先检查crc的最低位是否为1。* 如果是1,则将crc右移一位并与0xA001进行异或操作;* 否则,直接将crc右移一位。*///CRC寄存器被初始化0xFFFF。ushort crc = 0xFFFF;for (int i = 0; i < length; i++){//将数据的第一个字节(8位)与CRC寄存器的当前值进行异或运算,并将结果存回CRC寄存器。crc ^= data[i];//根据最低有效位(LSB)是否为0来决定下一步操作。//如果LSB为0,则直接将CRC寄存器的内容右移一位;//如果LSB为1,则在右移一位后,//还需要与一个特定的16位多项式(如0xA001)进行异或运算。for (int j = 0; j < 8; j++){if ((crc & 1) != 0)crc = (ushort)((crc >> 1) ^ 0xA001);elsecrc >>= 1;}}return crc;}}/** Program 类是程序的入口点,创建一个 Protocol 对象,* 构造一个 Packet 对象,然后发送该数据包,* 并尝试解码接收到的数据包。*/class Program{static void Main(string[] args){Protocol protocol = new Protocol();Packet packet = new Packet();packet.LocalID = 1;packet.RemoteID = 2;packet.CommandType = 0x01;packet.Data = new byte[] { 0x11, 0x22, 0x33, 0x44, 0x55,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xaa,0xbb,0xff };packet.DataLength = byte.Parse(packet.Data.Length.ToString());protocol.SendPacket(packet);if (protocol.DecodePacket(out Packet received)){Console.WriteLine("Packet received successfully:");Console.WriteLine($"Local ID: {received.LocalID}");Console.WriteLine($"Remote ID: {received.RemoteID}");Console.WriteLine($"Command Type: {received.CommandType:X2}");Console.WriteLine($"Data Length: {received.DataLength}");Console.Write("Data: ");for (int i = 0; i < received.DataLength; i++){Console.Write($"{received.Data[i]:X2} ");}Console.WriteLine();}else{Console.WriteLine("Failed to decode packet or CRC error occurred.");}Console.ReadKey();}}}

相关文章:

C# 设计一个可变长度的数据通信协议编码和解码代码。

设计一个可变长度的数据通信协议编码和解码代码。 要有本机ID字段&#xff0c;远端设备ID字段&#xff0c;指令类型字段&#xff0c;数据体字段&#xff0c;校验字段。其中一个要求是&#xff0c;每次固定收发八个字节&#xff0c;单个数据帧超过八个字节需要分包收发。对接收的…...

【MATLAB库函数系列】MATLAB库函数pwelch之功率谱估计的详解及实现

功率谱估计 由于实际信号通常是非定常的,我们只能假设其在10ms的时间段内是定常的,并在此基础上对短的定常信号求PSD或者能谱。 窗函数的作用就是将原始的信号分割成一段段可以计算PSD和能谱的短信号,并且保证了周期结构的连续性、避免了频谱泄漏。不同的窗函数具有不同的…...

科技出海|百分点科技智慧政务解决方案亮相非洲展会

近日&#xff0c;华为非洲全联接大会在南非约翰内斯堡举办&#xff0c;吸引政府官员行业专家、思想领袖、生态伙伴等2,000多人参会&#xff0c;百分点科技作为华为云生态合作伙伴&#xff0c;重点展示了智慧政务解决方案&#xff0c;发表《Enable a Smarter Government with Da…...

Prometheus 云原生 - Prometheus 数据模型、Metrics 指标类型、Exporter 相关

目录 开始 Prometheus 数据类型 简单理解 时序样本 格式 和 命名要求 Metrics 指标类型 Counter 计数器 Gauge Histogram Summary Exporter 相关 概述 Exporter 类型 Exporter 规范 开始 Prometheus 数据类型 简单理解 a&#xff09;安装好 Prometheus 后会暴露…...

Qt窗口程序整理汇总

到今日为止&#xff0c;通过一个个案例的实验&#xff0c;逐步熟悉了 Qt6下 窗体界面开发的&#xff0c;将走过的路&#xff0c;再次汇总整理。 Qt Splash样式的登录窗https://blog.csdn.net/castlooo/article/details/140462768 Qt实现MDI应用程序https://blog.csdn.net/cast…...

简单实现一个本地ChatGPT web服务(langchain框架)

简单实现一个本地ChatGPT 服务&#xff0c;用到langchain框架&#xff0c;fastapi,并且本地安装了ollama。 依赖安装&#xff1a; pip install langchain pip install langchain_community pip install langchain-cli # langchain v0.2 2024年5月最新版本 pip install bs4 pi…...

Elasticsearch-多边形范围查询(8.x)

目录 一、字段设计 二、数据录入 三、查询语句 四、Java代码实现 开发版本详见&#xff1a;Elasticsearch-经纬度查询(8.x-半径查询)_es经纬度范围查询-CSDN博客 一、字段设计 PUT /aoi_points {"mappings": {"properties": {"location": {…...

Kotlin Misk Web框架

Kotlin Misk Web框架 1 Misk 框架介绍2 Misk/SpringBoot 框架对比3 Misk 添加依赖/配置3.1 build.gradle.kts3.2 settings.gradle.kts3.3 gradle.properties 4 Misk 请求接口5 Misk 程序模块6 Misk 主服务类7 Misk 测试结果 1 Misk 框架介绍 Misk 是由 Square 公司开发的一个开…...

【设计模式之美】【建造型】工厂模式:通过面向接口编程思路,串起业务流程

文章目录 一. 简单工厂&#xff08;Simple Factory&#xff09;第一种简单工厂&#xff1a;面向接口编程与工厂类&#xff1a;划分功能职责第二种&#xff1a;单例简单工厂&#xff1a;节省内存和对象创建的时间 二. 工厂方法&#xff08;Factory Method&#xff09;&#xff1…...

AI算法19-偏最小二乘法回归算法Partial Least Squares Regression | PLS

偏最小二乘法回归算法简介 算法概述 偏最小二乘法模型可分为偏最小二乘回归模型和偏最小二乘路径模型。其中偏最小二乘回归模型是一种新型的多元统计方法&#xff0c;它集中了主成分分析、典型相关分析和线性回归的特点&#xff0c;特别在解决回归中的共线性问题具有无可比拟…...

live555关于RTSP协议交互流程

RTP在和h264 RTP在和h265 RTP载荷AAC live555关于RTSP协议交互流程 live555的核心数据结构值之闭环双向链表 live555 rtsp服务器实战之createNewStreamSource 概要 rtsp在交互的过程中用到很多协议:tcp,udp,rtp,rtcp,sdp等协议&#xff1b;该篇文章主要分析在live555中这些…...

Centos7 安装私有 Gitlab

在 CentOS 7上&#xff0c;下面的命令也会在系统防火墙中打开 HTTP、HTTPS 和 SSH 访问。这是一个可选步骤&#xff0c;如果您打算仅从本地网络访问极狐GitLab&#xff0c;则可以跳过它。 sudo yum install -y curl policycoreutils-python openssh-server perl sudo systemct…...

浅谈数学模型在UGC/AIGC游戏数值配置调参中的应用(AI智能体)

浅谈数学模型在UGC/AIGC游戏数值配置调参中的应用 ygluu 卢益贵 关键词&#xff1a;UGC、AIGC、AI智能体、大模型、数学模型、游戏数值调参、游戏策划 一、前言 在策划大大群提出《游戏工厂&#xff1a;AI&#xff08;AIGC/ChatGPT&#xff09;与流程式游戏开发》讨论之后就…...

第T5周:使用TensorFlow实现运动鞋品牌识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 文章目录 一、前期工作1.设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;2. 导入数据3. 查看数据 二、数据预处理1、加载数据2、数据可视化3、再…...

网络编程学习之tcp

按下*&#xff08;星号&#xff09;可以搜索当前光标下的单词。 Tcp编程的过程 打开网络设备 Bind&#xff1a;给服务地址把ip号和端口号连接进去 Tcp是有状态的 Listen是进入监听状态&#xff0c;看有没有客户端来连接服务器 Tcp比udp消耗过多资源 Upd类似于半双工&#…...

前端XMLHttpRequest、Fetch API、Axios实现文件上传、下载方法及后端Spring文件服务器处理方法

前言 本文总结Web应用开发中文件上传、下载的方法&#xff0c;即从前端表单输入文件并封装表单数据&#xff0c;然后请求后端服务器的处理过程&#xff1b;从基础的JavaScript中XmlHttpRequest对象、Fetch API实现上传、下载进行说明&#xff0c;并给出了前端常用的axios库的请…...

STM32智能交通监测系统教程

目录 引言环境准备智能交通监测系统基础代码实现&#xff1a;实现智能交通监测系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;交通监测与管理问题解决方案与优化收尾与总结 1. 引言 智能交通监测系统通…...

【利用Selenium+autoIt实现文件上传】

利用Selenium+autoIt实现文件上传 利用Selenium+autoIT实现文件上传autoIt脚本制作转换成exe文件java代码运行部分利用Selenium+autoIT实现文件上传 当你看到这篇文章时,证明你遇到了和我一样的难题。正常情况下我们利用selenium完全可以实现表单的提交和文件上传等操作。但当…...

python join

1、join函数 *.join(seq) 以*作为分隔符&#xff0c;将seq所有的元素合并为一个新的字符串 seq ABDWDPO new_seq list(.joint(seq)) # ABDWDPO #[A, B, D, W, D, P, O]...

cython加速python代码

python这个语言在使用的层面上看几乎没有缺点&#xff0c;简单易学&#xff0c;语法简单&#xff0c;唯一的弱点就是慢&#xff0c; 当然了万能的python社区是给了解决方法的&#xff0c;那就是cython 使用Cython可以显著提升Python代码的执行效率&#xff0c;特别是在涉及到数…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...