DotNetty 开源地址 https://github.com/Azure/DotNetty
1.国家部标808协议格式
标识位
采用 0x7e 表示,若校验码、消息头以及消息体中出现 0x7e,则要进行转义处理,转义
规则定义如下:
0x7e <————> 0x7d 后紧跟一个 0x02;
0x7d <————> 0x7d 后紧跟一个 0x01。
转义处理过程如下:
发送消息时:消息封装——>计算并填充校验码——>转义;
接收消息时:转义还原——>验证校验码——>解析消息。
示例:
发送一包内容为 0x30 0x7e 0x08 0x7d 0x55 的数据包,则经过封装如下:0x7e 0x30 7d 0x02 0x08 0x7d0x01 0x55 0x7
2.建立一个控制台程序
添加NuGet包
System.Text.Encoding.CodePages Microsoft.Extensions.Configuration.CommandLine Microsoft.Extensions.Configuration.EnvironmentVariables Microsoft.Extensions.Configuration.Json Microsoft.Extensions.Hosting Microsoft.Extensions.Logging.Console
3.dotnetty 通道配置,使用默认的DelimiterBasedFrameDecoder 看看是否能够实现分包
bootstrap.Option(ChannelOption.SoBacklog, 100).Handler(new LoggingHandler("SRV-LSTN")) .ChildHandler(new ActionChannelInitializer<IChannel>(channel => { IChannelPipeline pipeline = channel.Pipeline; pipeline.AddLast(new LoggingHandler("SRV-CONN")); pipeline.AddLast("framing-dec", new DelimiterBasedFrameDecoder(1024, false, LineDelimiter())); pipeline.AddLast("ServerClientHandler", new DBServerClientHandler()); }));
public static IByteBuffer[] LineDelimiter() { return new[] { Unpooled.WrappedBuffer(new[] { (byte)0x7e, (byte)0x7e }), Unpooled.WrappedBuffer(new[] { (byte)0x7e }), }; }
用网络调试工具 发送数据 7E 81 00 00 03 01 82 40 55 60 49 00 00 00 01 04 38 7E ,发现服务器收到数据后,分包数据错误了(一包数据编程2包数据了,数据格式也不对),
更改下分隔符代码,也出现一样的结果
public static IByteBuffer[] LineDelimiter() { return new[] { Unpooled.WrappedBuffer(new[] { (byte)0x7e }), Unpooled.WrappedBuffer(new[] { (byte)0x7e }), }; }
public static IByteBuffer[] LineDelimiter() { return new[] { Unpooled.WrappedBuffer(new[] { (byte)0x7e}), }; }
4.使用ByteToMessageDecoder,代码如下
/// <summary> ///粘包处理 数据包 头和尾 标志 都包含分割 字符串 /// </summary> public class BeiDouFrameDecoder : ByteToMessageDecoder { private int frameFlag = 0x7e; private int manFrameLength; private int minFrameLength; private int delimiters = 2; private IByteBuffer frameDelimiter; /// <summary> /// /// </summary> /// <param name="frameFlag">数据包 标志</param> /// <param name="maxFrameLength">数据包最大长度</param> /// <param name="minFrameLength">数据包最小长度</param> public BeiDouFrameDecoder(byte frameFlag, int maxFrameLength, int minFrameLength) { this.frameFlag = frameFlag; this.manFrameLength = maxFrameLength; this.minFrameLength = minFrameLength; frameDelimiter = Unpooled.WrappedBuffer(new[] { frameFlag }); } protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output) { if (input.ReadableBytes <= minFrameLength)//还不够 最小帧的 数据 return; int readLen = -1; //标记 int OriginalReadIndex = input.ReaderIndex; input.MarkReaderIndex(); if (frameFlag == input.GetByte(OriginalReadIndex))//找到头 第一个字节是头 不改变 ReaderIndex { input.SetReaderIndex(OriginalReadIndex + 1); readLen = IndexOfEnd(input); input.ResetReaderIndex(); if (readLen != -1)//没有找到尾 { readLen += delimiters; if (readLen > manFrameLength || readLen < minFrameLength) { input.SkipBytes(readLen); } else { IByteBuffer frame = input.ReadSlice(readLen); frame.Retain(); output.Add(frame); } } } else { //找头 int readIndex = -1; int seekReaderIndex = input.ReaderIndex + 1; while (seekReaderIndex < input.WriterIndex) { if (frameFlag == input.GetByte(seekReaderIndex))//找到头部 { readIndex = seekReaderIndex; break; } seekReaderIndex++; } if (readIndex != -1)//找到头 { if ((input.ReadableBytes - readIndex) < minFrameLength)//可以读取的 数据长度小于最小帧长度,说明还不够一包数据,等下一次再读取 { input.ResetReaderIndex();//本次跳过 还原ReaderIndex return; } input.SetReaderIndex(readIndex + 1); readLen = IndexOfEnd(input); if (readLen == -1)//没有找打 尾 { input.SkipBytes(input.ReadableBytes);//本次跳过 后面的所有字节 } else if (readLen > manFrameLength || readLen < minFrameLength)//找到帧 但是长度 小于 最小长度 是错误的帧 SkipBytes { input.SetReaderIndex(readIndex); input.SkipBytes(readLen + delimiters); } else { input.SetReaderIndex(readIndex); IByteBuffer frame = input.ReadSlice(readLen + delimiters); frame.Retain(); output.Add(frame); } } } } private int IndexOfEnd(IByteBuffer haystack) { for (int i = haystack.ReaderIndex; i < haystack.WriterIndex; i++) { if (haystack.GetByte(i) != frameFlag) { continue; } else { if (i == haystack.WriterIndex) { return -1; } } //Found the needle from the haystack! 找到 return i - haystack.ReaderIndex; } return -1; } //private static int IndexOf(IByteBuffer haystack, IByteBuffer needle) //{ // for (int i = haystack.ReaderIndex; i < haystack.WriterIndex; i++) // { // int haystackIndex = i; // int needleIndex; // for (needleIndex = 0; needleIndex < needle.Capacity; needleIndex++) // { // if (haystack.GetByte(haystackIndex) != needle.GetByte(needleIndex)) // { // break; // } // else // { // haystackIndex++; // if (haystackIndex == haystack.WriterIndex && needleIndex != needle.Capacity - 1) // { // return -1; // } // } // } // if (needleIndex == needle.Capacity) // { // // Found the needle from the haystack! // return i - haystack.ReaderIndex; // } // } // return -1; //} }
修改通道代码
bootstrap.Option(ChannelOption.SoBacklog, 100).Handler(new LoggingHandler("SRV-LSTN")) .ChildHandler(new ActionChannelInitializer<IChannel>(channel => { IChannelPipeline pipeline = channel.Pipeline; pipeline.AddLast(new LoggingHandler("SRV-CONN")); pipeline.AddLast("BeiDouFrameDecoder", new BeiDouFrameDecoder(0x7e, 2048, 12)); pipeline.AddLast("ServerClientHandler", new DBServerClientHandler()); }));
测试结果
水平有限,欢迎指正。谢谢。