基于Protostuff实现的Netty编解码器
在设计netty的编解码器过程中,有许多组件可以选择,这里由于咱对Protostuff比较熟悉,所以就用这个组件了。由于数据要在网络上传输,所以在发送方需要将类对象转换成二进制,接收方接收到数据后,需要将二进制转换成类对象,由于这个操作在之前的文章中有讲解过:网络传输数据序列化工具Protostuff,所以可以翻看我之前的文章来查看具体的实践方法:public class SerializeUtil {
private static class SerializeData{
private Object target;
}
@SuppressWarnings("unchecked")
public static byte[] serialize(Object object) {
SerializeData serializeData = new SerializeData();
serializeData.target = object;
Class<SerializeData> serializeDataClass = (Class<SerializeData>) serializeData.getClass();
LinkedBuffer linkedBuffer = LinkedBuffer.allocate(1024 * 4);
try {
Schema<SerializeData> schema = RuntimeSchema.getSchema(serializeDataClass);
return ProtostuffIOUtil.toByteArray(serializeData, schema, linkedBuffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
linkedBuffer.clear();
}
}
@SuppressWarnings("unchecked")
public static <T> T deserialize(byte[] data, Class<T> clazz) {
try {
Schema<SerializeData> schema = RuntimeSchema.getSchema(SerializeData.class);
SerializeData serializeData = schema.newMessage();
ProtostuffIOUtil.mergeFrom(data, serializeData, schema);
return (T) serializeData.target;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
但是,上面只是普通的操作Util,如何让数据能够在netty上进行传输呢?
在netty中,如果想发送数据出去,那么需要将数据转换成二进制,然后通过网络传送出去,他提供了MessageToByteEncoder的操作类,用户需要继承此类,然后实现encode方法就可以了。来看看我们如何将我们写好的SerializeUtil操作类集成进去:public class NettyMessageEncoder extends MessageToByteEncoder<NettyMessage> {
@Override
protected void encode(ChannelHandlerContext ctx, NettyMessage msg, ByteBuf out) throws Exception {
out.writeBytes(SerializeUtil.serialize(msg));
}
}
如上代码所示,我们就准备好了一个基于Protostuff组件实现的编码类了。编码后的数据,被添加到ByteBuf缓冲区后,被发送出去。
那么如何来实现解码器呢?public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder{
public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
}
@Override
public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
try {
byte[] dstBytes = new byte[in.readableBytes()];
//in.getBytes(in.readerIndex(), dstBytes);
//切记这里一定要用readBytes,不能用getBytes,否则会导致readIndex不能向后移动,从而导致netty did not read anything but decoded a message.错误
in.readBytes(dstBytes,0,in.readableBytes());
NettyMessage nettyMessage = SerializeUtil.deserialize(dstBytes, NettyMessage.class);
return nettyMessage;
} catch (Exception e) {
System.out.println("exception when decoding: " + e);
return null;
}
}
}
如上代码所示。一般情况下,需要继承netty中的ByteToMessageDecoder操作类来实现,但是考虑到这样的话需要用户自己来处理粘包拆包问题,比较麻烦,所以我们就继承自netty中为我们准备好的LengthFieldBasedFrameDecoder来进行,由于此decoder具有处理粘包拆包的功能,而且其继承自ByteToMessageDecoder类,所以就省去了我们处理粘包拆包的逻辑。
需要注意的是,在进行解码的过程中,我们首先需要从缓冲区读取数据到byte数组中,然后需要将readerIndex标记往后移动,如果读完后不移动的话,会报netty did not read anything but decoded a message的错误,而且这个错误在你运行的时候并不会抛出来,非常隐蔽,要不是细细的调试客户端,根本不能发觉此错误的存在。
所以从上面代码可以看出,ByteBuf.getBytes,只是单纯的读取缓存区数据,并不会将readerIndex后移。但是ByteBuf.readBytes则会将readerIndex后移。这点必须重视。
最后,我们将这两个实现类放到handler执行容器中即可。 channel.pipeline().addLast("nettyMessageDecoder", new NettyMessageDecoder(1024 * 1024, 4, 4));
channel.pipeline().addLast("nettyMessageEncoder", new NettyMessageEncoder());
channel.pipeline().addLast("readTimeoutHandler", new ReadTimeoutHandler(50));
channel.pipeline().addLast("loginAuthResponseHandler", new LoginAuthResponseHandler());
channel.pipeline().addLast("heartBeatHandler", new HeartBeatResponseHandler());
最后启动服务,我们就可以看到我们的编解码器正常跑起来了:Login is ok: Netty Message [header=Header [crcCode=-1410399999,length=0,sessionId=0,type=4,priority=0,attachment={}]]
Client send heart beat message to server : ----> Netty Message [header=Header [crcCode=-1410399999,length=0,sessionId=1344,type=5,priority=0,attachment={}]]
Client receive server heartbeat message : ---> Netty Message [header=Header [crcCode=-1410399999,length=0,sessionId=0,type=6,priority=0,attachment={}]]
欢迎工作一到五年的Java工程师朋友们加入Java程序员开发: 854393687
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间"来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!
后在线教育时代下,英语学习的硬核装备来了曾几何时,在各路资本加持和推动下,线下线上教育培训类公司狂飙突进,在线教育更是扶摇直上。但是,在7月24日发布了双减政策,让原本倚重K12业务的教培行业,纷纷加速转型,使教育领域的
区块链解决数字时代的不平等平等和不平等是一个亘古常谈的话题,平等需求是下层对上层的呐喊,就像钱钟书围城一书中的墙外人和墙内人一样,城内的人觉得不公,想去城外,城外的人认为不等,想进城内。人社会时代总是一个矛
国家电网已建成覆盖176个城市的高速公路快充网络新能源汽车的发展离不开充电站等配套设施的建设,而目前我国快充网络的覆盖面正在逐步扩大中。近日,中国国家电网有限公司董事长辛保安表示,国家电网已建成覆盖176个城市的高速公路快充网络
蔚来启动ES8挪威用户试驾将于9月交付太平洋汽车网行业频道日前,我们从官方获悉,蔚来汽车在挪威正式启动ES8车型用户试驾会。根据此前消息,蔚来ES8将于今年9月在挪威正式交付,届时蔚来换电站也将同步上线。目前,蔚来AP
平时叫自动驾驶,出了事叫辅助驾驶?销售宣传中混淆概念成常态如今,自动驾驶是一个既熟悉又陌生的词汇。它频繁出现在人们的视野中,也是很多汽车营销人员口中的核心技术,但对于民众而言,对它的认识又知之甚少。销售人员一会是辅助驾驶,一会又是自动驾驶
如果地球的氧气含量突然增加一倍,会发生什么?答如果地球的氧气增加一倍,昆虫和节肢动物会朝着大体型方向进化一亿年,人类就骑着皮皮虾出行吧!目前地球表面的平均含氧量为21,这个指的是氧气的体积百分比,不是质量百分比如果含氧量增加
黎明职业大学杼研科技团队在癌症检测技术上取得突破性进展近期,黎明职业大学智能制造应用技术国家级协同创新中心科学研究处创新创业学院智能制造工程学院商学院联合杼研科技项目团队历经多年的探索,成功突破技术壁垒,在太赫兹光检测芯片上取得进展,
肿瘤早筛行业大洗牌!数字化医疗时代正在到来整个肿瘤早筛行业生态体系正在进行一场大变革2020年,突然其来的新冠肺炎疫情,将原本就关乎国计民生的医疗器械产业推到最显眼的位置。5G远程超声机器人全自动核酸检测分析仪一系列先进医
小米11T系列曝光或支持120W快充9月1日消息,近日外媒91mobiles曝光了将在海外市场推出的小米11T系列(包括小米11T及小米11TPro)的外观渲染图。小米11T系列正面为居中打孔直屏设计,背面摄像头模组
元宇宙互联网进化的下一阶段财士计划我要上头条加密货币比特币超话如果你对加密货币世界并不陌生,那么你一定听过元宇宙这个词汇。它似乎是个近期才在加密货币领域兴起的时髦热词,不过作为一种概念,它早在几十年前便已存
Windows11正式版将于10月5日上线免费升级但暂不支持安卓应用9月1日消息,微软昨晚宣布,将于2021年10月5日正式开始推送Windows11操作系统。届时符合条件的Windows10PC将免费升级到Windows11,预装Windows1