在设计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={}]]

基于Protostuff实现的Netty编解码器的更多相关文章

  1. RPC基于http协议通过netty支持文件上传下载

    本人在中间件研发组(主要开发RPC),近期遇到一个需求:RPC基于http协议通过netty支持文件上传下载 经过一系列的资料查找学习,终于实现了该功能 通过netty实现文件上传下载,主要在编解码时 ...

  2. 【Netty】(9)---Netty编解码器

    Netty编解码器 在了解Netty编解码之前,先了解Java的编解码: 编码(Encode)称为序列化, 它将对象序列化为字节数组,用于网络传输.数据持久化或者其它用途. 解码(Decode)称为反 ...

  3. Netty编解码器(理论部分)

    背景知识 在了解Netty编解码之前,先回顾一下JAVA的编解码: 编码(Encode):在java中称之为序列化,把内存中易丢失的数据结构或对象状态转换成另一种可存储(存储到磁盘),可在网络间传输的 ...

  4. 什么是Netty编解码,Netty编解码器有哪些?Protostuff怎么使用?

    哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,昨天下雨没怎么上街上 ...

  5. 基于Java Mina 和Netty 通信框架的JT/T809转发服务器设计

    Apache MINA 是 Apache 组织的一个开源项目,为开发高性能和高可用性的网络应用程序提供了非常便利的框架. 也是Java开发者的一个福利(.NET目前还没有类似封装的这么好的基础sock ...

  6. Netty源码分析之自定义编解码器

    在日常的网络开发当中,协议解析都是必须的工作内容,Netty中虽然内置了基于长度.分隔符的编解码器,但在大部分场景中我们使用的都是自定义协议,所以Netty提供了  MessageToByteEnco ...

  7. 手写一个类SpringBoot的HTTP框架:几十行代码基于Netty搭建一个 HTTP Server

    本文已经收录进 : https://github.com/Snailclimb/netty-practical-tutorial (Netty 从入门到实战:手写 HTTP Server+RPC 框架 ...

  8. 微言netty:不在浮沙筑高台

    1. 写作缘起 几年前,我在一家农业物联网公司,负责解决其物联网产品线.我们当时基于.net平台打造了一套实时数据采集系统,可以把数以百万级的传感器传送回来的数据采集入库并根据这些数据进行建模.在搭建 ...

  9. Netty学习记录-入门篇

    你如果,缓缓把手举起来,举到顶,再突然张开五指,那恭喜你,你刚刚给自己放了个烟花. 模块介绍 netty-bio: 阻塞型网络通信demo. netty-nio: 引入channel(通道).buff ...

随机推荐

  1. 使用xshell从远程服务器下载文件到本地

    XSHELL工具上传文件到Linux以及下载文件到本地(Windows) Xshell很好用,然后有时候想在windows和linux上传或下载某个文件,其实有个很简单的方法就是rz,sz.首先你的L ...

  2. ssh 免密码登录(设置后仍需输密码的原因及解决方法)

    按操作步骤操作不行的最可能原因是:文件权限,不论是a机还是要访问的b机的文件的权限都要改成对应的700,600:必须严格是这个权限,否则会出现设置免密fail的情况. serverA 免密码登录到 s ...

  3. etcd 启动错误

    Apr 26 16:17:25 ceph-0 etcd: f281dc69fb4dd3d8 became candidate at term 3574Apr 26 16:17:25 ceph-0 et ...

  4. VueJs第1天

    Vue.js是一个轻巧的.高性能.可组件化的MVVM库. Vue是一套用于构建用户界面的渐进式框架 渐进增强(progressive enhancement):针对低版本浏览器进行构建页面,保证最基本 ...

  5. ubuntu学习笔记

    Linux操作系统 locale –a查看支持语言 ls查看目录 ls .l / 查看根目录 apt-get –h 安装软件看帮助信息 sudo apt-get inatall packge 安装包 ...

  6. Log4j2 设置控制台打印彩色日志

    https://www.baidu.com/s?ie=utf-8&tn=02003390_20_hao_pg&wd=%E5%8D%9A%E5%AE%A2%E5%9B%AD%E7%99% ...

  7. 剑指offer:顺时针打印矩阵

    问题描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数 ...

  8. heartbeat 非联网安装(通过配置本地yum文件库安装heartbeat)

    软件环境:centos6.5 一.下载rpm包 首先找一台联网的centos6.5机器 安装epel扩展源: yum install -y epel-release 安装yum-plugin-down ...

  9. Operation not permitted

    centos7 下,修改文件夹的权限时,报了这么一个错误.linux 下,此法依然奏效. 错误日志: chmod: changing permissions of '/opt/apps/images/ ...

  10. CSS实现水平垂直居中的1010种方式

    转载自:CSS实现水平垂直居中的1010种方式 划重点,这是一道面试必考题,很多面试官都喜欢问这个问题,我就被问过好几次了 要实现上图的效果看似很简单,实则暗藏玄机,本文总结了一下CSS实现水平垂直居 ...