DotNetty 使用ByteToMessageDecoder 國家部標808協議封裝


DotNetty 開源地址 https://github.com/Azure/DotNetty

 個人博客地址   http://www.dncblogs.cn/Blog/ShowBlog/70

1.國家部標808協議格式

808協議.jpg

標識位
采用 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}),
            };
        }

網絡調試工具.jpg接收數據.jpg

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());
                    }));

測試結果

分包.png

水平有限,歡迎指正。謝謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM