DotNetty 開源地址 https://github.com/Azure/DotNetty
個人博客地址 http://www.dncblogs.cn/Blog/ShowBlog/70
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()); }));
測試結果
水平有限,歡迎指正。謝謝。