netty 自定义协议粘包拆包解决

目前所在的项目中需要与电力设备的采集终端进行通讯.

相关的协议为376.1,根据规划及相关的参考数据,最终决定使用netty作为scoket框架.

解决相关的高并发问题.

在实现的过程中,由于硬件端不可控,可能是很多的家的设备,且实际的网络情况不是特别稳定。所以兼容性尤其显得重要。

这里就需要解决netty的自定义协议的粘包拆包。

 

TCP粘包/拆包解决办法

1-定长消息,例如每个报文长度固定,不够补空格

2-使用回车换行符分割,在包尾加上分割符,例如Ftp协议

3-消息分割,头为长度(消息总长度或消息体长度),通常头用一个int32表示

4-复杂的应用层协议

最终的解决代码附上:

1. 解码器继承 ByteToMessageDecoder

 

protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
// 可读长度必须大于基本长度
if (byteBuf.readableBytes() > HEADER_SIZE) {
 //超过最大长度
 if (byteBuf.readableBytes() > 16391) {
 byteBuf.skipBytes(byteBuf.readableBytes());
 }
 //寻找包头
 while (true){
 //记录数据包开始的指针位置
 byteBuf.markReaderIndex();
 int head=byteBuf.readByte()& 0xFF;
 //判断是否是属于包头
 if(head == HEAD){
 break;
 }
 //不是包头
 //指针复位
 byteBuf.resetReaderIndex();
 // 缓存指针向后移动一个字节
 byteBuf.readByte();
 //判断当前缓存是否依然满足一条指令的最小长度
 if(byteBuf.readableBytes() < HEADER_SIZE){
 return;
 }
 }
 //成功找到包头
 byte lengthA = byteBuf.readByte();
 byte lengthB = byteBuf.readByte();
 byte lengthC = byteBuf.readByte();
 byte lengthD = byteBuf.readByte();
 //比较是否相等
 if(lengthC==lengthA&&lengthD==lengthB){
 int lenB= lengthB & 0xFF;
 int lenA= lengthA & 0xFF;
 //颠倒
 String lentCode=Integer.toHexString(lenB)+Integer.toHexString(lenA);
 //转换为二进制
 String binLen=Integer.toBinaryString(Integer.valueOf(lentCode,16));
 //转换为十进制
 int msgLength=Integer.valueOf(binLen.substring(0,binLen.length()-2),2)+8;

 //复位到最后一次标记的地方
 byteBuf.resetReaderIndex();
 //判断当前数据是否已经到齐
 if(byteBuf.readableBytes() < msgLength){
 //重新读取
 //等待消息完成
 System.out.println(new Date()+" "+Thread.currentThread().getName()+" 长度不对 当前长度为: " + byteBuf.readableBytes()+" 需求长度为:"+msgLength);
 return;
 }
 //读取消息内容
 byte[] bytes = new byte[msgLength];
 byteBuf.readBytes(bytes);
 //构造对象
 list.add( new Message(bytes));

 }
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注