记一次解决netty半包问题的经历
最近学习了netty,想写一个简单的rpc,结果发现发送消息时遇到难题了,网上搜了一下,这种情况是半包问题和粘包问题,主要是出现在并发高一些的时候。
talk is cheap
客户端编码:
protected void encode(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
encode0(channelHandlerContext,o,byteBuf);
}
private void encode0(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
if(o instanceof UserInfo){
byte[] data = Serializition.serialize((UserInfo) o,UserInfo.class);
byteBuf.writeBytes(data);
}
}
服务端解码:
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
myDecode(channelHandlerContext,byteBuf,list);
}
public void myDecode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list){
int len = byteBuf.readableBytes();
byte[] data = new byte[len];
byteBuf.readBytes(data);
UserInfo userInfo = Serializition.deSerialize(data,UserInfo.class);
list.add(userInfo);
}
这是最初版本的,一开始以为只要读出来反序列化成对象就ok了,进行了简单的测试发现没问题,但客户端发送频繁一些服务端就开始报错:
警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.DecoderException: java.lang.RuntimeException: Reading from a byte array threw an IOException (should never happen).
分析一下发现对于来自同一个远程连接来说,服务端只会分配一个bytebuf来接收消息(这里使用的是UnpooledDirectByteBuf),这个bytebuf容量是动态扩增的,如果当前的长度不够用来存储新的消息就会自动扩展。当客户端发送不频繁时,服务端有足够的时间来做准备接收和处理消息,不会出现问题。但客户端频繁发送时就会出现问题了,如上,服务端的可读的字节超过了一个对象,读取后下一个对象反序列化就会出现问题。
解决思路:
1.每次发送定长的消息,不够就补全,服务端设置对应的长度(但这样有问题:如果这样做客户端会发送很多无用信息,浪费性能,而且不知道设置多大的长度合适)
2.使用netty自带的编码和解码器,如使用/r/n标志符解码,这就要继承MessageDecoder了,也就是字符解码,即先将消息在字节--字符串--对象将转换(有点浪费效率,而且万一内容中有对应的分隔符就会出问题)
3.每次发送消息前先获取对象字节数组的长度(我最开始使用的方法,后来在网上也找到别人一样的思路)
客户端:
protected void encode(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
encode1(channelHandlerContext,o,byteBuf);
}
private void encode1(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
if(o instanceof UserInfo){
byte[] data = Serializition.serialize((UserInfo) o,UserInfo.class);
byteBuf.writeInt(data.length);
byteBuf.writeBytes(data);
}
}
服务端:
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
myDecode1(channelHandlerContext,byteBuf,list);
}
public void myDecode1(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list){
if(byteBuf.readableBytes()>4){
int len = byteBuf.readInt();
byte[] data = new byte[len];
byteBuf.readBytes(data);
UserInfo userInfo = Serializition.deSerialize(data,UserInfo.class);
list.add(userInfo);
}
}
这就看起来简单了 数据流是 |int|bytes|int|bytes,但实际情况还是发生了问题,还是出现了一样的问题。异常原因是服务端实例化数组长度后可读字节不够,原因是发送时客户端是分包发送的。
因此我在这个方法的基础上增加了一个条件:如果可读字节数不够就保存已创建好的字节数组,等下一次字节数够时使用
private volatile int len=0;
protected void decode5(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
int length =len>0?len:(byteBuf.readableBytes()>=4?byteBuf.readInt():0);
if(byteBuf.readableBytes()>=length&&length>0) {
byte[] data = new byte[length];
byteBuf.readBytes(data);
UserInfo userInfo = Serializition.deSerialize(data, UserInfo.class);
list.add(userInfo);
//bytes.put(length, data);
len=0;
}else {
len = length;
}
}
经过测试,问题得到解决。
记一次解决netty半包问题的经历的更多相关文章
- 通过大量实战案例分解Netty中是如何解决拆包黏包问题的?
TCP传输协议是基于数据流传输的,而基于流化的数据是没有界限的,当客户端向服务端发送数据时,可能会把一个完整的数据报文拆分成多个小报文进行发送,也可能将多个报文合并成一个大报文进行发送. 在这样的情况 ...
- Netty 粘包/拆包应用案例及解决方案分析
熟悉TCP变成的可以知道,无论是客户端还是服务端,但我们读取或者发送消息的时候,都需要考虑TCP底层粘包/拆包机制,下面我们先看一下TCP 粘包/拆包和基础知识,然后模拟一个没有考虑TCP粘包/拆包导 ...
- Http 调用netty 服务,服务调用客户端,伪同步响应.ProtoBuf 解决粘包,半包问题.
实际情况是: 公司需要开发一个接口给新产品使用,需求如下 1.有一款硬件设备,客户用usb接上电脑就可以,但是此设备功能比较单一,所以开发一个服务器程序,辅助此设备业务功能 2.解决方案,使用Sock ...
- netty解决粘包半包问题
前言:开发者用到TCP/IP交互时,偶尔会遇到粘包或者半包的数据,这种情况有时会对我们的程序造成严重的影响,netty框架为解决这种问题提供了若干框架 1. LineBasedFrameDecoder ...
- c# socket 解决粘包,半包
处理原理: 半包:即一条消息底层分几次发送,先有个头包读取整条消息的长度,当不满足长度时,将消息临时缓存起来,直到满足长度再解码 粘包:两条完整/不完整消息粘在一起,一般是解码完上一条消息,然后再判断 ...
- Netty 粘包/半包原理与拆包实战
Java NIO 粘包 拆包 (实战) - 史上最全解读 - 疯狂创客圈 - 博客园 https://www.cnblogs.com/crazymakercircle/p/9941658.html 本 ...
- socket编程 TCP 粘包和半包 的问题及解决办法
一般在socket处理大数据量传输的时候会产生粘包和半包问题,有的时候tcp为了提高效率会缓冲N个包后再一起发出去,这个与缓存和网络有关系. 粘包 为x.5个包 半包 为0.5个包 由于网络原因 一次 ...
- Netty使用LineBasedFrameDecoder解决TCP粘包/拆包
TCP粘包/拆包 TCP是个”流”协议,所谓流,就是没有界限的一串数据.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TC ...
- Netty - 粘包和半包(上)
在网络传输中,粘包和半包应该是最常出现的问题,作为 Java 中最常使用的 NIO 网络框架 Netty,它又是如何解决的呢?今天就让我们来看看. 定义 TCP 传输中,客户端发送数据,实际是把数据写 ...
随机推荐
- vue笔记未整理
全局组件 局部组件 子组件传值到父组件 父子组件传值 watch跟计算属性差不多,都会有缓存,计算属性优先 计算属性get set 对象 数组 对象 数组 不复用 改变数组 直接修改数组,页面没变化 ...
- 【P2577】 午餐
题目简述 THU ACM小组一行N个人去食堂吃饭,计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭.每个人打完饭后立刻开 ...
- [BJOI2019]光线[递推]
题意 题目链接 分析 令 \(f_i\) 表示光线第一次从第一块玻璃射出第 \(i\) 块玻璃的比率. 令 \(g_i\) 表示光线射回第 \(i\) 块玻璃,再射出第 \(i\) 块玻璃的比率. 容 ...
- Elastic Stack-Kibana使用介绍(七)
一.前言 主要来讲述一下Kibana使用以及上生产时候的一些配置,要是大家对这块比较感兴趣我到时候也可以在结合Grafana做一些图表方面的介绍,后面等介绍完Beats以后我去阿里云租几台机器 ...
- npm包--rimraf
含义 rimraf 包的作用:以包的形式包装rm -rf命令,用来删除文件和文件夹的,不管文件夹是否为空,都可删除. 安装 npm install rimraf --save-dev 使用 const ...
- 记录一次无厘头的粗心失误——java后台报错:Unknown column 'xxx' in 'field list'
原因: sql文件马虎,直接用错了仓库.用的不是程序调用的仓库.而自己pojo和mapper还是采用Mybatis的逆向工程生成的.当时搞得很无厘头. 解决方案: sql用到程序指定的仓库就行啦. 总 ...
- 微信内分享第三方H5链接无法使用内置浏览器打开的解决方案
很多朋友在微信内想分享转发H5链接的时候都会很容易碰到H5链接在微信内无法打开或在微信内无法打开app下载页的情况.通常这种情况微信会给个提示 “已停止访问该网址” ,那么导致这个情况的因素有哪些呢, ...
- Cnario Player 接入视频采集卡采集外部音视频信号测试
测试产品 型号: TC-D56N1-30P采集卡 参数: 1* HDMI 1.4输入, PCIe 接口为PCI-Express x4(Gen2), 最高支持4096x2160@30Hz, 支持1920 ...
- 分布式唯一ID生成方案是什么样的?(转)
一.前言 分布式系统中我们会对一些数据量大的业务进行分拆,如:用户表,订单表.因为数据量巨大一张表无法承接,就会对其进行分库分表. 但一旦涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题, ...
- React Navigation & React Native & React Native Navigation
React Navigation & React Native & React Native Navigation React Navigation https://facebook. ...