DotNetty 使用ByteToMessageDecoder 国家部标808协议封装
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, ).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(, 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 = ;
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 = -;
//标记
int OriginalReadIndex = input.ReaderIndex;
input.MarkReaderIndex();
if (frameFlag == input.GetByte(OriginalReadIndex))//找到头 第一个字节是头 不改变 ReaderIndex
{
input.SetReaderIndex(OriginalReadIndex + );
readLen = IndexOfEnd(input);
input.ResetReaderIndex();
if (readLen != -)//没有找到尾
{
readLen += delimiters;
if (readLen > manFrameLength || readLen < minFrameLength)
{
input.SkipBytes(readLen);
}
else
{
IByteBuffer frame = input.ReadSlice(readLen);
frame.Retain();
output.Add(frame);
}
}
}
else
{
//找头
int readIndex = -;
int seekReaderIndex = input.ReaderIndex + ;
while (seekReaderIndex < input.WriterIndex)
{
if (frameFlag == input.GetByte(seekReaderIndex))//找到头部
{
readIndex = seekReaderIndex;
break;
}
seekReaderIndex++;
} if (readIndex != -)//找到头
{
if ((input.ReadableBytes - readIndex) < minFrameLength)//可以读取的 数据长度小于最小帧长度,说明还不够一包数据,等下一次再读取
{
input.ResetReaderIndex();//本次跳过 还原ReaderIndex
return;
} input.SetReaderIndex(readIndex + );
readLen = IndexOfEnd(input);
if (readLen == -)//没有找打 尾
{
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 -;
}
}
//Found the needle from the haystack! 找到
return i - haystack.ReaderIndex;
}
return -;
} //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, ).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, , ));
pipeline.AddLast("ServerClientHandler", new DBServerClientHandler());
}));
测试结果

水平有限,欢迎指正。谢谢。
DotNetty 使用ByteToMessageDecoder 国家部标808协议封装的更多相关文章
- 基于Java Netty框架构建高性能的部标808协议的GPS服务器
使用Java语言开发一个高质量和高性能的jt808 协议的GPS通信服务器,并不是一件简单容易的事情,开发出来一段程序和能够承受数十万台车载接入是两码事,除去开发部标808协议的固有复杂性和几个月长周 ...
- 基于Java Mina框架的部标808服务器设计和开发
在开发部标GPS平台中,部标808GPS服务器是系统的核心关键,决定了部标平台的稳定性和行那个.Linux服务器是首选,为了跨平台,开发语言选择Java自不待言. 我们为客户开发的部标服务器基于Min ...
- 出租车Jt/T 905协议与部标1078协议融合的网约车视频监控平台
出租车jt/t 905协议,是jt/t 808协议的一个变种,设计者将部标808协议拿过来,并不是单纯的增加网约车相关的指令集,而且对原有的指令如定位0×0200指令也进行了修改,经过一通剧烈的修改, ...
- 基于Redis构建10万+终端级的高性能部标JT808协议的Gps网关服务器(转)
原文地址:http://www.jt808.com/?p=1282 在开发一个大规模的部标GPS监控平台的时候,就算我们花费再多的时间设计和规划,我们也并不能准确的预测出自己未来的车载终端接入量有多大 ...
- 基于Html5+HLS协议播放符合部标1078协议的实时流媒体视频
由于现在主流的部标GPS和1077视频监控平台,都是BS架构,在网页上播放视频,早期的很多平台用的都是ActiveX控件的形式,依赖于IE浏览器,需要降低浏览器的安全设置,而且非常难用.同时由于win ...
- 路由器配置深入浅出—路由器接口PPP协议封装及PAP和CHAP验证配置
知识域: 是针对点对点专线连接的接口的二层封装协议配置 PPP的PAP和CHAP验证,cpt支持,不一定要在gns3上做实验. 路由器出厂默认是hdlc封装,修改为ppp封装后,可以采用pap验证或者 ...
- 开源 DotNetty 实现的 Modbus TCP/IP 协议
本项目的目的是为了学习 DotNetty 与 Modbus 协议,参考 modjn 实现功能 0x01: Read Coils (读取线圈/离散量输出状态) 0x02: Read Discrete I ...
- 如何利用UDP协议封装一个数据包
在如何封装一个数据包上,是一个非常细致的问题,而利用UDP协议来封装的话,是比较简单,让我们一步步来分析典型的TCP/IP协议.一般来说一个典型的一个数据包,包括以太网MAC头+网络层IP数据头+传输 ...
- 基于JT/T 1078协议设计和开发部标视频服务器
交通部与2016年10月份推出了JT/T 1078-2016标准,全称是<道路运输车辆卫星定位系统视频通信协议>.该标准将改变以往两客一危车辆的视频监控设备通信协议都是设备厂商私有协议的局 ...
随机推荐
- python笔记之循环控制
学习python的第一个例子,while循环中嵌套if-else语句,一个猜年龄的例子 #案例1,实现循环猜年龄 # my_age = 12 # while True: # guess_age1 = ...
- 复制带随机指针的链表 · Copy List with Random Pointer
[抄题]: 给出一个链表,每个节点包含一个额外增加的随机指针可以指向链表中的任何节点或空的节点. 返回一个深拷贝的链表. [思维问题]: [一句话思路]: 完完全全地复制,否则不好操作. 1-> ...
- VS Access DataSet 插入
在使用vs2008+access数据库,然后又使用了数据集,这时候插入操作遇到了问题,各种乱七八糟.各种头疼的问题就不说了,现在说找到的解决方法: 在xsd文件中插入TableAdapter后,会自动 ...
- springmvc中Controller方法的返回值
1.1 返回ModelAndView controller方法中定义ModelAndView对象并返回,对象中可添加model数据.指定view. 1.2 返回void 在controller方法形参 ...
- Spring框架之log日志的使用
1.Spring框架也需要引入日志相关的jar包 * 在spring-framework-3.0.2.RELEASE-dependencies/org.apache.commons/com.sprin ...
- BZOJ2424 [HAOI2010]订货 - 费用流
题解 (非常裸的费用流 题意有一点表明不清: 该月卖出的商品可以不用算进仓库里面. 然后套上费用流模板 代码 #include<cstring> #include<queue> ...
- How to Set Up an Rsync Daemon on Your Linux Server
Introduction This tutorial will take you through setting up an rsync daemon on your Linux server. Yo ...
- 用Vue-cli生成vue+webpack的项目模板怎么设置为vue1.0版本?
用Vue-cli生成vue+webpack的项目模板 $ npm install -g vue-cli $ vue init webpack my-project $ cd my-project $ ...
- 【Java】JavaWeb文件上传和下载
文件上传和下载在web应用中非常普遍,要在jsp环境中实现文件上传功能是非常容易的,因为网上有许多用java开发的文件上传组件,本文以commons-fileupload组件为例,为jsp应用添加文件 ...
- phalapi框架where条件查询
// WHERE name = 'dogstar' AND age = 18 $user->where(array('name' => 'dogstar', 'age' => 18) ...
