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包

  1. System.Text.Encoding.CodePages
  2.  
  3. Microsoft.Extensions.Configuration.CommandLine
  4.  
  5. Microsoft.Extensions.Configuration.EnvironmentVariables
  6.  
  7. Microsoft.Extensions.Configuration.Json
  8.  
  9. Microsoft.Extensions.Hosting
  10.  
  11. Microsoft.Extensions.Logging.Console

3.dotnetty 通道配置,使用默认的DelimiterBasedFrameDecoder 看看是否能够实现分包

  1. bootstrap.Option(ChannelOption.SoBacklog, ).Handler(new LoggingHandler("SRV-LSTN"))
  2. .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
  3. {
  4. IChannelPipeline pipeline = channel.Pipeline;
  5. pipeline.AddLast(new LoggingHandler("SRV-CONN"));
  6. pipeline.AddLast("framing-dec", new DelimiterBasedFrameDecoder(, false, LineDelimiter()));
  7. pipeline.AddLast("ServerClientHandler", new DBServerClientHandler());
  8. }));
  1. public static IByteBuffer[] LineDelimiter()
  2. {
  3. return new[]
  4. {
  5. Unpooled.WrappedBuffer(new[] { (byte)0x7e, (byte)0x7e }),
  6. Unpooled.WrappedBuffer(new[] { (byte)0x7e }),
  7. };
  8. }

用网络调试工具 发送数据 7E 81 00 00 03 01 82 40 55 60 49 00 00 00 01 04 38 7E ,发现服务器收到数据后,分包数据错误了(一包数据编程2包数据了,数据格式也不对),

更改下分隔符代码,也出现一样的结果

  1. public static IByteBuffer[] LineDelimiter()
  2. {
  3. return new[]
  4. {
  5. Unpooled.WrappedBuffer(new[] { (byte)0x7e }),
  6. Unpooled.WrappedBuffer(new[] { (byte)0x7e }),
  7. };
  8. }
  1. public static IByteBuffer[] LineDelimiter()
  2. {
  3. return new[]
  4. {
  5. Unpooled.WrappedBuffer(new[] { (byte)0x7e}),
  6. };
  7. }

4.使用ByteToMessageDecoder,代码如下

  1. /// <summary>
  2. ///粘包处理 数据包 头和尾 标志 都包含分割 字符串
  3. /// </summary>
  4. public class BeiDouFrameDecoder : ByteToMessageDecoder
  5. {
  6. private int frameFlag = 0x7e;
  7. private int manFrameLength;
  8. private int minFrameLength;
  9. private int delimiters = ;
  10. private IByteBuffer frameDelimiter;
  11.  
  12. /// <summary>
  13. ///
  14. /// </summary>
  15. /// <param name="frameFlag">数据包 标志</param>
  16. /// <param name="maxFrameLength">数据包最大长度</param>
  17. /// <param name="minFrameLength">数据包最小长度</param>
  18. public BeiDouFrameDecoder(byte frameFlag, int maxFrameLength, int minFrameLength)
  19. {
  20. this.frameFlag = frameFlag;
  21. this.manFrameLength = maxFrameLength;
  22. this.minFrameLength = minFrameLength;
  23. frameDelimiter = Unpooled.WrappedBuffer(new[] { frameFlag });
  24. }
  25.  
  26. protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
  27. {
  28. if (input.ReadableBytes <= minFrameLength)//还不够 最小帧的 数据
  29. return;
  30.  
  31. int readLen = -;
  32. //标记
  33. int OriginalReadIndex = input.ReaderIndex;
  34. input.MarkReaderIndex();
  35. if (frameFlag == input.GetByte(OriginalReadIndex))//找到头 第一个字节是头 不改变 ReaderIndex
  36. {
  37. input.SetReaderIndex(OriginalReadIndex + );
  38. readLen = IndexOfEnd(input);
  39. input.ResetReaderIndex();
  40. if (readLen != -)//没有找到尾
  41. {
  42. readLen += delimiters;
  43. if (readLen > manFrameLength || readLen < minFrameLength)
  44. {
  45. input.SkipBytes(readLen);
  46. }
  47. else
  48. {
  49. IByteBuffer frame = input.ReadSlice(readLen);
  50. frame.Retain();
  51. output.Add(frame);
  52. }
  53. }
  54. }
  55. else
  56. {
  57. //找头
  58. int readIndex = -;
  59. int seekReaderIndex = input.ReaderIndex + ;
  60. while (seekReaderIndex < input.WriterIndex)
  61. {
  62. if (frameFlag == input.GetByte(seekReaderIndex))//找到头部
  63. {
  64. readIndex = seekReaderIndex;
  65. break;
  66. }
  67. seekReaderIndex++;
  68. }
  69.  
  70. if (readIndex != -)//找到头
  71. {
  72. if ((input.ReadableBytes - readIndex) < minFrameLength)//可以读取的 数据长度小于最小帧长度,说明还不够一包数据,等下一次再读取
  73. {
  74. input.ResetReaderIndex();//本次跳过 还原ReaderIndex
  75. return;
  76. }
  77.  
  78. input.SetReaderIndex(readIndex + );
  79. readLen = IndexOfEnd(input);
  80. if (readLen == -)//没有找打 尾
  81. {
  82. input.SkipBytes(input.ReadableBytes);//本次跳过 后面的所有字节
  83. }
  84. else if (readLen > manFrameLength || readLen < minFrameLength)//找到帧 但是长度 小于 最小长度 是错误的帧 SkipBytes
  85. {
  86. input.SetReaderIndex(readIndex);
  87. input.SkipBytes(readLen + delimiters);
  88. }
  89. else
  90. {
  91. input.SetReaderIndex(readIndex);
  92. IByteBuffer frame = input.ReadSlice(readLen + delimiters);
  93. frame.Retain();
  94. output.Add(frame);
  95. }
  96. }
  97. }
  98. }
  99.  
  100. private int IndexOfEnd(IByteBuffer haystack)
  101. {
  102. for (int i = haystack.ReaderIndex; i < haystack.WriterIndex; i++)
  103. {
  104. if (haystack.GetByte(i) != frameFlag)
  105. {
  106. continue;
  107. }
  108. else
  109. {
  110. if (i == haystack.WriterIndex)
  111. {
  112. return -;
  113. }
  114. }
  115. //Found the needle from the haystack! 找到
  116. return i - haystack.ReaderIndex;
  117. }
  118. return -;
  119. }
  120.  
  121. //private static int IndexOf(IByteBuffer haystack, IByteBuffer needle)
  122. //{
  123. // for (int i = haystack.ReaderIndex; i < haystack.WriterIndex; i++)
  124. // {
  125. // int haystackIndex = i;
  126. // int needleIndex;
  127. // for (needleIndex = 0; needleIndex < needle.Capacity; needleIndex++)
  128. // {
  129. // if (haystack.GetByte(haystackIndex) != needle.GetByte(needleIndex))
  130. // {
  131. // break;
  132. // }
  133. // else
  134. // {
  135. // haystackIndex++;
  136. // if (haystackIndex == haystack.WriterIndex && needleIndex != needle.Capacity - 1)
  137. // {
  138. // return -1;
  139. // }
  140. // }
  141. // }
  142.  
  143. // if (needleIndex == needle.Capacity)
  144. // {
  145. // // Found the needle from the haystack!
  146. // return i - haystack.ReaderIndex;
  147. // }
  148. // }
  149. // return -1;
  150. //}
  151. }

修改通道代码

  1. bootstrap.Option(ChannelOption.SoBacklog, ).Handler(new LoggingHandler("SRV-LSTN"))
  2. .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
  3. {
  4. IChannelPipeline pipeline = channel.Pipeline;
  5. pipeline.AddLast(new LoggingHandler("SRV-CONN"));
  6. pipeline.AddLast("BeiDouFrameDecoder", new BeiDouFrameDecoder(0x7e, , ));
  7. pipeline.AddLast("ServerClientHandler", new DBServerClientHandler());
  8. }));

测试结果

水平有限,欢迎指正。谢谢。

DotNetty 使用ByteToMessageDecoder 国家部标808协议封装的更多相关文章

  1. 基于Java Netty框架构建高性能的部标808协议的GPS服务器

    使用Java语言开发一个高质量和高性能的jt808 协议的GPS通信服务器,并不是一件简单容易的事情,开发出来一段程序和能够承受数十万台车载接入是两码事,除去开发部标808协议的固有复杂性和几个月长周 ...

  2. 基于Java Mina框架的部标808服务器设计和开发

    在开发部标GPS平台中,部标808GPS服务器是系统的核心关键,决定了部标平台的稳定性和行那个.Linux服务器是首选,为了跨平台,开发语言选择Java自不待言. 我们为客户开发的部标服务器基于Min ...

  3. 出租车Jt/T 905协议与部标1078协议融合的网约车视频监控平台

    出租车jt/t 905协议,是jt/t 808协议的一个变种,设计者将部标808协议拿过来,并不是单纯的增加网约车相关的指令集,而且对原有的指令如定位0×0200指令也进行了修改,经过一通剧烈的修改, ...

  4. 基于Redis构建10万+终端级的高性能部标JT808协议的Gps网关服务器(转)

    原文地址:http://www.jt808.com/?p=1282 在开发一个大规模的部标GPS监控平台的时候,就算我们花费再多的时间设计和规划,我们也并不能准确的预测出自己未来的车载终端接入量有多大 ...

  5. 基于Html5+HLS协议播放符合部标1078协议的实时流媒体视频

    由于现在主流的部标GPS和1077视频监控平台,都是BS架构,在网页上播放视频,早期的很多平台用的都是ActiveX控件的形式,依赖于IE浏览器,需要降低浏览器的安全设置,而且非常难用.同时由于win ...

  6. 路由器配置深入浅出—路由器接口PPP协议封装及PAP和CHAP验证配置

    知识域: 是针对点对点专线连接的接口的二层封装协议配置 PPP的PAP和CHAP验证,cpt支持,不一定要在gns3上做实验. 路由器出厂默认是hdlc封装,修改为ppp封装后,可以采用pap验证或者 ...

  7. 开源 DotNetty 实现的 Modbus TCP/IP 协议

    本项目的目的是为了学习 DotNetty 与 Modbus 协议,参考 modjn 实现功能 0x01: Read Coils (读取线圈/离散量输出状态) 0x02: Read Discrete I ...

  8. 如何利用UDP协议封装一个数据包

    在如何封装一个数据包上,是一个非常细致的问题,而利用UDP协议来封装的话,是比较简单,让我们一步步来分析典型的TCP/IP协议.一般来说一个典型的一个数据包,包括以太网MAC头+网络层IP数据头+传输 ...

  9. 基于JT/T 1078协议设计和开发部标视频服务器

    交通部与2016年10月份推出了JT/T 1078-2016标准,全称是<道路运输车辆卫星定位系统视频通信协议>.该标准将改变以往两客一危车辆的视频监控设备通信协议都是设备厂商私有协议的局 ...

随机推荐

  1. FileUpload控件实现单按钮图片自动上传并带预览显示

    FileUpload控件实现单按钮图片自动上传并带预览显示 1.实现原理:   FileUpload控件默认不支持服务端的ONCHANGE事件,此时用一种变通的方法借用客户端的onchange事件,调 ...

  2. 【校招面试 之 C/C++】第7题 C++构造函数不能是虚函数的原因

    1.虚拟函数调用只需要“部分的”信息,即只需要知道函数接口,而不需要对象的具体类型.但是构建一个对象,却必须知道具体的类型信息.如果你调用一个虚拟构造函数,编译器怎么知道你想构建是继承树上的哪种类型呢 ...

  3. day5:vcp考试

    Q81. An administrator needs to recover disk space on a previously-used thin provisioned virtual disk ...

  4. Java动态代理机制详解(类加载,JDK 和CGLIB,Javassist,ASM)

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  5. centos一键安装lnmp成功后无法访问ip(解决办法)

    自己搞了个服务器 (我的服务器网络类型是 专有网络)如下图点击 配置规则 进入到 进.出端口规则配置 点击添加安全组规则 如图所配置  添加完成后 就如下面所示 (配置完成后 通过ip就已经可以访问了 ...

  6. DB2错误码

    SQL语句成功完成 01xxx SQL语句成功完成,但是有警告 + 未限定的列名被解释为一个有相互联系的引用 + 动态SQL语句用分号结束 + 没有找到满足SQL语句的行 + 用DATA CAPTUR ...

  7. Linux多线程服务端编程 使用muduo C++网络库 学习笔记 日志log

    代码来自陈硕开源代码库 muduo中 地址是https://github.com/chenshuo/muduo #pragma once #include <string> #define ...

  8. 19. Fight over Fox-hunting 猎狐引发的冲突

    . Fight over Fox-hunting 猎狐引发的冲突 ① Foxes and farmers have never got on well.These small dog-like ani ...

  9. Django入门与实践-第21章:迁移(完结)

    http://127.0.0.1:8000/boards/1/ python manage.py migrate #boards/models.py class Topic(models.Model) ...

  10. 主机和docker容器拷贝文件

    从主机复制到容器sudo docker cp host_path containerID:container_path 从容器复制到主机sudo docker cp containerID:conta ...