SunnyUI.FrameDecoder 6.0.0

dotnet add package SunnyUI.FrameDecoder --version 6.0.0
NuGet\Install-Package SunnyUI.FrameDecoder -Version 6.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SunnyUI.FrameDecoder" Version="6.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SunnyUI.FrameDecoder --version 6.0.0
#r "nuget: SunnyUI.FrameDecoder, 6.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SunnyUI.FrameDecoder as a Cake Addin
#addin nuget:?package=SunnyUI.FrameDecoder&version=6.0.0

// Install SunnyUI.FrameDecoder as a Cake Tool
#tool nuget:?package=SunnyUI.FrameDecoder&version=6.0.0

SunnyUI

SunnyUI.FrameDecoder

欢迎交流,QQ群: 56829229 (SunnyUI技术交流群),请给源码项目点个Star吧!!!

开始

Nuget安装最新版本。

Install-Package SunnyUI.FrameDecoder
目的

SunnyUI.FrameDecoder 开源TCP、UDP、串口等流式数据解码库

  • 实现解码自由
  • 底层缓冲区采用数据池实现
  • 通过使用池化缓冲区来消除大型对象堆分配
  • 减少GC次数
  • 缓冲区实现IBufferWrite<T>接口
  • 广泛支持较新的内存相关类型,如Span<byte>, ReadOnlySpan<byte>以及Memory<byte>
  • 避免内存碎片
  • 提供出色的性能
介绍

TCP是面向连接的传输协议,TCP传输的数据是以流的形式,而流数据是没有明确的开始结尾边界,应用层处理接收数据时可能产生半包或者粘包,其主要原因如下:

  • TCP发送方原因:
    因为TCP本身传输的数据包大小就有限制,所以应用发出的消息包过大,TCP会把应用消息包拆分为多个TCP数据包发送出去。
    Negal算法的优化,当应用发送数据包太小,TCP为了减少网络请求次数的开销,它会等待多个消息包一起,打成一个TCP数据包一次发送出去。
  • TCP接收方原因:
    因为TCP缓冲区里的数据都是字符流的形式,没有明确的边界,因为数据没边界,所以应用从TCP缓冲区中读取数据时就没办法指定一个或几个消息一起读,而只能选择一次读取多大的数据流,而这个数据流中就可能包含着某个消息包的一部分数据。

所以为了解决半包或者粘包,得到应用层需要的数据,实现解码自由,开发了此数据解码库,原理也就是给数据流转时给流式数据加上消息边界。在接收数据时,将流式数据通过相应的解码器(FrameDecoder),解码输出完整的帧数据(FrameData)。

UDP是面向消息的,它有边界协议,可以根据消息的格式区分消息的开始和结尾。那为什么还要解码呢?
举个例子,实际项目:FPGA通过千兆网卡将数据以UDP协议输出,其数据实际还是流式数据,需要在UDP接收到的数据中寻找包头和包尾,来截取一段需要的数据,这样数据解码器就有意义了。

串口在数据发送和接收时,根据串口的配置(波特率、奇偶校验等)、串口缓冲设置、数据发送间隔的不匹配,也会出现接收数据时可能产生半包或者粘包。
Modbus-RTU总线是通过时间间隔来判断一帧数据结束的,3.5个字符时间内没有收到新的数据,则认为这一帧数据结束。
普通的串口数据传输,还是要给数据增加消息边界,通过解码器来获取实际需要的数据。

其实不限于TCP、UDP、串口,只要是从流式数据中获取指定格式的数据,都可以用过解码器来实现。

有些网络库,例如:

  • Netty 有FrameDecoder;
  • TouchSocket 有数据处理适配器;
  • HP-Socket 则是通过Pack模式,在发送数据时加上消息边界。
    不一一列举,这些库主要是为了解决TCP数据的的解码。

SunnyUI.FrameDecoder则面向流式数据,不拘泥于数据从哪儿来,通过流式数据的解码器来获取到有用的数据。

软件架构

输入图片说明

流式数据有两种:

  • byte[]:字节数组
  • string:字符串

针对这两种数据,实现了泛型数据缓存: DataCache<T>,其内存分配通过数据池:DataPool<T> 实现
通过DataCache<byte>实现字节数据组缓存,通过DataCache<char>实现字符串缓存

安装教程

动态库应用环境: VS2012及以上均可,支持.Net Framework 4.5+、包括.Net 6、.Net7
推荐通过Nuget安装:NuGet\Install-Package SunnyUI.FrameDecoder
或者通过Nuget搜索:SunnyUI.FrameDecoder 安装。
输入图片说明

使用说明
  • 字节数据帧解码器
  1. DelimiterBasedFrameDecoder 基于分隔符 - 数据帧解码器 数据格式: 输入图片说明

构造函数:

/// <summary>
/// 构造函数
/// </summary>
/// <param name="delimiters">分隔符,最好为多字节,并且数据中不会出现分隔符而造成误解码</param>
/// <param name="maxFrameLength">最大数据长度,仅判断数据长度</param>
public DelimiterBasedFrameDecoder(byte[] delimiters, int maxFrameLength = 0)

示例:

var bts = new byte[] { 1, 2, 3, 0xFF, 0xFB };
var decoder1 = new DelimiterBasedFrameDecoder(new byte[] { 0xFF, 0xFB });
decoder1.OnDecoder += DelimiterBasedFrameDecoder_OnDecoder;
decoder1.Decode(bts);

private static void DelimiterBasedFrameDecoder_OnDecoder(object sender, IByteEventArgs e)
{
    Console.WriteLine("Value: " + ByteHelper.HexString(e.Value));
}

输出:

Value: 010203
  1. FixedLengthFrameDecoder 固定长度 - 数据帧解码器 数据格式: 输入图片说明

构造函数:

/// <summary>
/// 构造函数
/// </summary>
/// <param name="frameLength">帧固定长度</param>
public FixedLengthFrameDecoder(int frameLength)

示例:

var bts = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var decoder0 = new FixedLengthFrameDecoder(3);
decoder0.OnDecoder += FixedLengthFrameDecoder_OnDecoder;
decoder0.Decode(bts);

private static void FixedLengthFrameDecoder_OnDecoder(object sender, IByteEventArgs e)
{
    Console.WriteLine("Value: " + ByteHelper.HexString(e.Value));
}

输出:

Value: 010203
Value: 040506
  1. HeaderDelimiterFrameDecoder 数据头部 - 数据帧解码器 数据格式: 输入图片说明

构造函数:

/// <summary>
/// 构造函数
/// </summary>
/// <param name="headers">数据头部</param>
/// <param name="maxFrameLength">最大数据长度,仅判断数据长度</param>
public HeaderDelimiterFrameDecoder(byte[] headers, int maxFrameLength = 0)

示例:

var bts = new byte[] { 0xFF, 0xFB, 1, 2, 3 };
var decoder2 = new HeaderDelimiterFrameDecoder(new byte[] { 0xFF, 0xFB });
decoder2.OnDecoder += HeaderDelimiterFrameDecoder_OnDecoder;
decoder2.Decode(bts);
decoder2.Decode(bts);

private static void HeaderDelimiterFrameDecoder_OnDecoder(object sender, IByteEventArgs e)
{
    Console.WriteLine("Value: " + ByteHelper.HexString(e.Value));
}

输出:

Value: 010203
  1. HeaderTagLengthValueTailFrameDecoder 数据头部、标签、数据长度、数据、数据尾部 - 数据帧解码器 数据格式: 输入图片说明

构造函数:

/// <summary>
/// 构造函数
/// </summary>
/// <param name="headers">数据头部</param>
/// <param name="tails">数据尾部</param>
/// <param name="tagLength">标签长度</param>
/// <param name="valueLengthType">数据长度类型</param>
/// <param name="valueIsLittleEndian">数据长度字节顺序</param>
/// <param name="isFullLength">数据长度是否包含数据头部、标签、数据长度、数据尾部的长度,false为仅数据长度</param>
/// <param name="maxFrameLength">最大数据长度,仅判断数据长度</param>
public HeaderTagLengthValueTailFrameDecoder(byte[] headers, byte[] tails, int tagLength, ValueLengthType valueLengthType, bool valueIsLittleEndian = true, bool isFullLength = false, int maxFrameLength = 0)

示例:

var bts = new byte[] { 0xFF, 0xFB, 0xAA, 2, 3, 4, 0xF7, 0xF7 };
var decoder3 = new HeaderTagLengthValueTailFrameDecoder(new byte[] { 0xFF, 0xFB }, new byte[] { 0xF7, 0xF7 }, 1, ValueLengthType.Byte);
decoder3.OnDecoder += HeaderTagLengthValueTailFrameDecoder_OnDecoder;
decoder3.Decode(bts);

private static void HeaderTagLengthValueTailFrameDecoder_OnDecoder(object sender, IByteEventArgs e)
{
    var args = (TagLengthValueFrameDataEventArgs)e;
    Console.WriteLine("Tag: " + ByteHelper.HexString(args.Tag) + ", Value: " + ByteHelper.HexString(args.Value));
}

输出:

Tag: AA, Value: 0304
  1. HeaderTailFrameDecoder 数据头部、数据尾部 - 数据帧解码器 数据格式: 输入图片说明

构造函数:

/// <summary>
/// 构造函数
/// </summary>
/// <param name="headers">数据头部</param>
/// <param name="tails">数据尾部</param>
/// <param name="maxFrameLength">最大数据长度,仅判断数据长度</param>
public HeaderTailFrameDecoder(byte[] headers, byte[] tails, int maxFrameLength = 0)

示例:

var bts = new byte[] { 0xFF, 0xFB, 2, 3, 4, 0xF7, 0xF7 };
var decoder4 = new HeaderTailFrameDecoder(new byte[] { 0xFF, 0xFB }, new byte[] { 0xF7, 0xF7 });
decoder4.OnDecoder += HeaderTailFrameDecoder_OnDecoder;
decoder4.Decode(bts);

private static void HeaderTailFrameDecoder_OnDecoder(object sender, IByteEventArgs e)
{
    Console.WriteLine("Value: " + ByteHelper.HexString(e.Value));
}

输出:

Value: 020304
  1. LengthValueFrameDecoder 数据长度、数据 - 数据帧解码器 数据格式: 输入图片说明

构造函数:

/// <summary>
/// 构造函数
/// </summary>
/// <param name="valueLengthType">数据长度类型</param>
/// <param name="valueIsLittleEndian">数据长度字节顺序</param>
/// <param name="isFullLength">数据长度是否包含数据长度的长度,false为仅数据长度</param>
/// <param name="maxFrameLength">最大数据长度,仅判断数据长度</param>
public LengthValueFrameDecoder(ValueLengthType valueLengthType, bool valueIsLittleEndian = true, bool isFullLength = false, int maxFrameLength = 0)

示例:

bts = new byte[] { 2, 3, 4 };
var decoder5 = new LengthValueFrameDecoder(ValueLengthType.Byte);
decoder5.OnDecoder += LengthValueFrameDecoder_OnDecoder;
decoder5.Decode(bts);

private static void LengthValueFrameDecoder_OnDecoder(object sender, IByteEventArgs e)
{
    Console.WriteLine("Value: " + ByteHelper.HexString(e.Value));
}

输出:

Value: 0304
  1. TagLengthValueFrameDecoder 标签、数据长度、数据 - 数据帧解码器 数据格式: 输入图片说明

构造函数:

/// <summary>
/// 构造函数
/// </summary>
/// <param name="tagLength">标签长度</param>
/// <param name="valueLengthType">数据长度类型</param>
/// <param name="valueIsLittleEndian">数据长度字节顺序</param>
/// <param name="isFullLength">数据长度是否包含标签、数据长度的长度,false为仅数据长度</param>
/// <param name="maxFrameLength">最大数据长度,仅判断数据长度</param>
public TagLengthValueFrameDecoder(int tagLength, ValueLengthType valueLengthType, bool valueIsLittleEndian = true, bool isFullLength = false, int maxFrameLength = 0)

示例:

bts = new byte[] { 0xAA, 2, 3, 4 };
var decoder6 = new TagLengthValueFrameDecoder(1, ValueLengthType.Byte);
decoder6.OnDecoder += TagLengthValueFrameDecoder_OnDecoder;
decoder6.Decode(bts);

private static void TagLengthValueFrameDecoder_OnDecoder(object sender, IByteEventArgs e)
{
    var args = (TagLengthValueFrameDataEventArgs)e;
    Console.WriteLine("Tag: " + ByteHelper.HexString(args.Tag) + ", Value: " + ByteHelper.HexString(args.Value));
}

输出:

Tag: AA, Value: 0304
  • 字符串解码器
  1. NMEA0183FrameDecoder NMEA0183 - 数据帧解码器 数据格式: 输入图片说明

示例:

var decoder = new NMEA0183FrameDecoder();
decoder.OnDecoder += NMEA0183Decoder_OnDecoder;
decoder.Decode("4,19.7,M,,,,0000*1F$GPGGA,092204.999,4250.5589,S,147");
decoder.Decode("18.5084,E,1,04,24.4,19.7,M,,,,0000*1F$GPGSA,A,3,01,20,");
decoder.Decode("19,13,,,,,,,,,40.4,24.4,32.2*0A$GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,");
decoder.Decode("069,,13,32,252,45*70$GPVTG,092204.999,42");

public static void NMEA0183Decoder_OnDecoder(object sender, IStringEventArgs e)
{
    Console.WriteLine(e.Value);
}

输出:

$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F
$GPGSA,A,3,01,20,19,13,,,,,,,,,40.4,24.4,32.2*0A
$GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,069,,13,32,252,45*70
  1. LineBasedFrameDecoder 基于换行符 - 数据帧解码器 数据格式: 输入图片说明

示例:

var decoder = new LineBasedFrameDecoder();
decoder.OnDecoder += NMEA0183Decoder_OnDecoder;
decoder.Decode("*AA" + '\r' + '\n' + "$GPGGA");
decoder.Decode("*1F" + '\n' + '\r' + "$GPGSA");
decoder.Decode("*0A" + '\r' + "$GPGSV");
decoder.Decode("*70" + '\n' + "$GPVTG");

public static void NMEA0183Decoder_OnDecoder(object sender, IStringEventArgs e)
{
    Console.WriteLine(e.Value);
}

输出:

*AA
$GPGGA*1F
$GPGSA*0A
$GPGSV*70
参与贡献
  1. Fork 本仓库
  2. 新建 Feat_xxx 分支
  3. 提交代码
  4. 新建 Pull Request
新的解码器

如果有新的解码器建议,请添加Issue。

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Framework net45 is compatible.  net451 was computed.  net452 was computed.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 was computed.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.5

  • .NETFramework 4.7.2

  • net6.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on SunnyUI.FrameDecoder:

Package Downloads
SunnyUI.COM

SunnyUI.COM 串口通讯类库

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on SunnyUI.FrameDecoder:

Repository Stars
RRQM/TouchSocket
TouchSocket是.Net(包括 C# 、VB.Net、F#)的一个整合性的、超轻量级的网络通信框架。包含了 tcp、udp、ssl、http、websocket、rpc、jsonrpc、webapi、xmlrpc等一系列的通信模块。一键式解决 TCP 黏分包问题,udp大数据包分片组合问题等。使用协议模板,可快速实现「固定包头」、「固定长度」、「区间字符」等一系列的数据报文解析。
Version Downloads Last updated
6.0.0 87 4/10/2024
5.3.2 262 1/11/2024
5.3.0 458 10/27/2023
5.2.3 225 10/14/2023
5.2.2 393 8/27/2023
5.2.1 163 8/27/2023
5.2.0 167 8/27/2023
5.1.0 506 8/17/2023
5.0.0 368 8/12/2023
4.3.0 401 7/15/2023
4.2.0 286 5/17/2023
4.1.0 508 4/8/2023
4.0.2 831 1/29/2023
4.0.1 267 1/28/2023
4.0.0 304 1/27/2023
3.0.6 281 1/26/2023
3.0.5 585 1/20/2023
3.0.4 286 1/19/2023
3.0.3 322 1/18/2023
3.0.2 297 1/17/2023
3.0.1 288 1/17/2023
3.0.0 552 1/16/2023
2.0.6 319 1/10/2023
2.0.5 299 1/10/2023
2.0.4 298 1/8/2023
2.0.3 311 1/5/2023
2.0.2 631 1/4/2023
2.0.1 293 12/21/2022
2.0.0 934 12/20/2022
1.2.0 310 12/18/2022
1.1.9 313 12/7/2022
1.1.8 324 12/4/2022
1.1.7 328 12/2/2022
1.1.6 322 11/28/2022
1.1.5 324 11/28/2022
1.1.4 333 11/27/2022
1.1.3 333 11/23/2022
1.1.2 364 11/9/2022
1.1.1 341 11/3/2022
1.1.0 353 11/1/2022
1.0.2 384 10/30/2022
1.0.1 365 10/30/2022
1.0.0 417 10/26/2022