在设计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. Lamda Expression

    Expression<Func<Student, bool>> filter=s=>s.Name.Contains("a") && s ...

  2. css3 box-flex

    应用地址:http://www.jb51.net/css/467291.html box-flex是css3新添加的盒子模型属性,它的出现打破了我们经常使用的浮动布局,实现垂直等高.水平均分.按比例划 ...

  3. java中的getStackTrace和printStackTrace的区别

    getStackTrace()返回的是通过getOurStackTrace方法获取的StackTraceElement[]数组,而这个StackTraceElement是ERROR的每一个cause ...

  4. springboot打成war包找不到文件

    项目在eclipse运行是可以的,但是打成war包到线上报错: FileNotFoundException: class path resource [static/apiclient_cert.p1 ...

  5. 2.2JAVA基础复习——JAVA语言的基础组成运算符和语句

    JAVA语言的基础组成有: 1.关键字:被赋予特殊含义的单词. 2.标识符:用来标识的符号. 3.注释:用来注释说明程序的文字. 4.常量和变量:内存存储区域的表示. 5.运算符:程序中用来运算的符号 ...

  6. Java运行环境

    Java 开发环境配置 在本章节中我们将为大家介绍如何搭建Java开发环境. Windows 上安装开发环境 Linux 上安装开发环境 安装 Eclipse 运行 Java Cloud Studio ...

  7. linux 分析java 线程状态

    将线程3117 的线程消息放到文件dump17中 jstack 13492 > dump17 分析线程 grep java.lang.Thread.State dump17 | awk '{pr ...

  8. 进行分支切换时,出现error的修复方法

    进行分支切换时,出现如此错误,导致无法正常切换:error: The following untracked working tree files would be overwritten by ch ...

  9. redis4.0 cluster搭建

    cd /root/tools wget http://pnxcvm0bq.bkt.clouddn.com/redis-4.0.9.tar.gz tar -zxvf redis-4.0.9.tar.gz ...

  10. 安装ceph设置镜像源

    当安装ceph时 卡在这的时候ceph会替换成自己的镜像源 所以这个时候  赶紧再打开一个终端 替换成国内的清华大学的镜像源 就不会被替换回去了 ceph.repo 文件 [Ceph] name=Ce ...