如何基于Netty处理粘包、拆包问题?
涉及到相关重要组件:
- ByteToMessageDecoder
- MessageToMessageDecoder
这两个组件都实现了ChannelInboundHandler接口,这说明这两个组件都是用来解码网络上过来的数据的。而他们的顺序一般是ByteToMessageDecoder位于head channel handler的后面,MessageToMessageDecoder位于ByteToMessageDecoder的后面。Netty中,涉及到粘包、拆包的逻辑主要在ByteToMessageDecoder及其实现中。
ByteToMessageDecoder
顾名思义、ByteToMessageDecoder是用来将从网络缓冲区读取的字节转换成有意义的消息对象的,对于源码层面指的说明的一段是下面这部分:
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
try{
while(in.isReadable()) {
intoutSize =out.size();
if(outSize >0) {
fireChannelRead(ctx,out, outSize);
out.clear();
if(ctx.isRemoved()) {
break;
}
outSize =0;
}
intoldInputLength =in.readableBytes();
decode(ctx,in,out);
if(ctx.isRemoved()) {
break;
}
if(outSize ==out.size()) {
if(oldInputLength ==in.readableBytes()) {
break;
}else{
continue;
}
}
if(oldInputLength ==in.readableBytes()) {
thrownewDecoderException(
StringUtil.simpleClassName(getClass()) +
".decode() did not read anything but decoded a message.");
}
if(isSingleDecode()) {
break;
}
}
}catch(DecoderException e) {
throwe;
}catch(Throwable cause) {
thrownewDecoderException(cause);
}
}
为了节省篇幅,我把注释删除掉了,当上面一个channel handler传入的ByteBuf有数据的时候,这里我们可以把in参数看成网络流,这里有不断的数据流入,而我们要做的就是从这个byte流中分离出message,然后把message添加给out。分开将一下代码逻辑:
- 当out中有Message的时候,直接将out中的内容交给后面的channel handler去处理。
- 当用户逻辑把当前channel handler移除的时候,立即停止对网络数据的处理。
- 记录当前in中可读字节数。
- decode是抽象方法,交给子类具体实现。
- 同样判断当前channel handler移除的时候,立即停止对网络数据的处理。
- 如果子类实现没有分理出任何message的时候,且子类实现也没有动bytebuf中的数据的时候,这里直接跳出,等待后续有数据来了再进行处理。
- 如果子类实现没有分理出任何message的时候,且子类实现动了bytebuf中的数据,则继续循环,直到解析出message或者不在对bytebuf中数据进行处理为止。
- 如果子类实现解析出了message但是又没有动bytebuf中的数据,那么是有问题的,抛出异常。
- 如果标志位只解码一次,则退出。
可以知道,如果要实现具有处理粘包、拆包功能的子类,及decode实现,必须要遵守上面的规则,我们以实现处理第一部分的第二种粘包情况和第三种情况拆包情况的服务器逻辑来举例:
对于粘包情况的decode需要实现的逻辑对应于将客户端发送的两条消息都解析出来分为两个message加入out,这样的话callDecode只需要调用一次decode即可。
对于拆包情况的decode需要实现的逻辑主要对应于处理第一个数据包的时候第一次调用decode的时候out的size不变,从continue跳出并且由于不满足继续可读而退出循环,处理第二个数据包的时候,对于decode的调用将会产生两个message放入out,其中两次进入callDecode上下文中的数据流将会合并为一个bytebuf和当前channel handler实例关联,两次处理完毕即清空这个bytebuf。
当然,尽管介绍了ByteToMessageDecoder,用户自己去实现处理粘包、拆包的逻辑还是有一定难度的,Netty已经提供了一些基于不同处理粘包、拆包规则的实现:如DelimiterBasedFrameDecoder、FixedLengthFrameDecoder、LengthFieldBasedFrameDecoder和LineBasedFrameDecoder等等。其中:
DelimiterBasedFrameDecoder是基于消息边界方式进行粘包拆包处理的。
FixedLengthFrameDecoder是基于固定长度消息进行粘包拆包处理的。
LengthFieldBasedFrameDecoder是基于消息头指定消息长度进行粘包拆包处理的。
LineBasedFrameDecoder是基于行来进行消息粘包拆包处理的。
用户可以自行选择规则然后使用Netty提供的对应的Decoder来进行具有粘包、拆包处理功能的网络应用开发。
最后
在通常的高性能网络应用中,客户端通常以长连接的方式和服务端相连,因为每次建立网络连接是一个很耗时的操作。比如在RPC调用中,如果一个客户端远程调用的过程中,连续发起了多次调用,而如果这些调用对应于同一个连接的时候,那么就会出现服务器需要对于这些多次调用消息的粘包拆包问题的处理。如果是你,你会选择哪种策略呢?
如何基于Netty处理粘包、拆包问题?的更多相关文章
- netty 解决粘包拆包问题
netty server TimeServer package com.zhaowb.netty.ch4_3; import io.netty.bootstrap.ServerBootstrap; i ...
- Netty TCP粘包/拆包问题《二》
1.DelimiterBasedFrameDecoder:是以分隔符作为结束标志进行解决粘包/拆包问题 代码: EchoClient:客户端 /* * Copyright 2012 The Netty ...
- Netty TCP粘包/拆包问题《一》
1.使用LineBasedFrameDecoder,StringDecoder解析器进行解决TCP粘包/拆包问题 2.代码搞起: TimeClient:客户端 /* * Copyright 2013- ...
- netty: 解决粘包拆包: 分隔符DelimiterBasedFrameDecoder,定长消息FixedLengthFrameDecoder
DelimiterBasedFrameDecoder 自定义分隔符 给Server发送多条信息,但是server会讲多条信息合并为一条.这时候我们需要对发生的消息指定分割,让client和server ...
- Netty入门系列(2) --使用Netty解决粘包和拆包问题
前言 上一篇我们介绍了如果使用Netty来开发一个简单的服务端和客户端,接下来我们来讨论如何使用解码器来解决TCP的粘包和拆包问题 TCP为什么会粘包/拆包 我们知道,TCP是以一种流的方式来进行网络 ...
- Netty 粘包 & 拆包 & 编码 & 解码 & 序列化 介绍
目录: 粘包 & 拆包及解决方案 ByteToMessageDecoder 基于长度编解码器 基于分割符的编解码器 google 的 Protobuf 序列化介绍 其他的 前言 Netty 作 ...
- TCP粘包/拆包 ByteBuf和channel 如果没有Netty? 传统的多线程服务器,这个也是Apache处理请求的模式
通俗地讲,Netty 能做什么? - 知乎 https://www.zhihu.com/question/24322387 谢邀.netty是一套在java NIO的基础上封装的便于用户开发网络应用程 ...
- Netty中粘包和拆包的解决方案
粘包和拆包是TCP网络编程中不可避免的,无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包和拆包 TCP是个“流”协议,所谓流,就是没有界限的一串 ...
- Netty(三)TCP粘包拆包处理
tcp是一个“流”的协议,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题. 粘包.拆包问题说明 假设客户端分别发送数据包D1和D ...
随机推荐
- 二叉树各种相关操作(建立二叉树、前序、中序、后序、求二叉树的深度、查找二叉树节点,层次遍历二叉树等)(C语言版)
将二叉树相关的操作集中在一个实例里,有助于理解有关二叉树的相关操作: 1.定义树的结构体: typedef struct TreeNode{ int data; struct TreeNode *le ...
- Ubuntu 16.04 安装和配置 Redis
因为发现之前手动安装的 redis 与现有的教程不一样,所以总结统一一下安装的标准步骤. 安装依赖项 为了获取最新版本的 Redis,我们将从源代码进行编译和安装.下载源代码之前,需要先安装一些编译所 ...
- 170509、文本编辑器编写的shell脚本在linux下无法执行的解决方法
今天碰到一个奇怪的问题,编写好的shell脚本再linux上执行一直提示找不到文件或目录,后来想想是文本编辑器的问题,记录下来!!! 1.查看当前文本格式 Notepad++界面中,在右下角有文件格式 ...
- 2018CCPC秦皇岛站
这次去秦皇岛,两个队都打铁回来,真的是蛮耻辱的 可以说是有史以来最差成绩了 其实网络赛就打的不好 两个名额一个是省赛分配的一个是排队排来的 去之前拉了几场之前的CCPC的比赛打 感觉打的都还不错的 热 ...
- Oracle Schema Objects——View
Oracle Schema Objects Oracle视图View 普通视图.物化视图 视图(视图不包含数据,不是段对象,不占用空间,只是一个代码.) 作用: 简化SQL 为安全,不暴露表的名称 视 ...
- Integer.valueof 和 Integer.parseInt
System.out.println(Integer.valueOf("127")==Integer.valueOf("127")); System.out.p ...
- mysql 取当前日期对应的周一或周日
select subdate(curdate(),date_format(curdate(),'%w')-1)//获取当前日期在本周的周一 select subdate(curdate(),date_ ...
- OleDb未指定错误
桌面开发,居然也出这种问题: 1. C#读取Excel“未指定错误” http://www.connectionstrings.com/ http://www.dnetzj.com/Content/2 ...
- 学习Mysql的记录贴 记录的内容是 指令的试用
前言:操作系统是WIn7 64位 旗舰版 ,Mysql的版本是mysql-5.7.10-winx64 是社区版 就是所谓的最后的免费版本. 下载后 解压 然后配置my.ini文件. ××××××× ...
- IP地址与子网掩码的计算
128.0.0.0=1 192.0.0.0=2224.0.0.0=3 240.0.0.0=4 248.0.0.0=5 252.0.0.0=6 254.0.0.0=7 255.0.0.0=8255.12 ...