简介

在之前的文章中我们提到了,对于NioSocketChannel来说,它不接收最基本的string消息,只接收ByteBuf和FileRegion。但是ByteBuf是以二进制的形式进行处理的,对于程序员来说太不直观了,处理起来也比较麻烦,有没有可能直接处理java简单对象呢?本文将会探讨一下这个问题。

decode和encode

比如我们需要直接向channel中写入一个字符串,在之前的文章中,我们知道这是不可以的,会报下面的错误:

DefaultChannelPromise@57f5c075(failure: java.lang.UnsupportedOperationException: unsupported message type: String (expected: ByteBuf, FileRegion))

也就说ChannelPromise只接受ByteBuf和FileRegion,那么怎么做呢?

既然ChannelPromise只接受ByteBuf和FileRegion,那么我们就需要把String对象转换成ByteBuf即可。

也就是说在写入String之前把String转换成ByteBuf,当要读取数据的时候,再把ByteBuf转换成String。

我们知道ChannelPipeline中可以添加多个handler,并且控制这些handler的顺序。

那么我们的思路就出来了,在ChannelPipeline中添加一个encode,用于数据写入的是对数据进行编码成ByteBuf,然后再添加一个decode,用于在数据写出的时候对数据进行解码成对应的对象。

encode,decode是不是很熟悉?对了,这就是对象的序列化。

对象序列化

netty中对象序列化是要把传输的对象和ByteBuf直接互相转换,当然我们可以自己实现这个转换对象。但是netty已经为我们提供了方便的两个转换类:ObjectEncoder和ObjectDecoder。

先看ObjectEncoder,他的作用就是将对象转换成为ByteBuf。

这个类很简单,我们对其分析一下:

public class ObjectEncoder extends MessageToByteEncoder<Serializable> {
private static final byte[] LENGTH_PLACEHOLDER = new byte[4]; @Override
protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
int startIdx = out.writerIndex(); ByteBufOutputStream bout = new ByteBufOutputStream(out);
ObjectOutputStream oout = null;
try {
bout.write(LENGTH_PLACEHOLDER);
oout = new CompactObjectOutputStream(bout);
oout.writeObject(msg);
oout.flush();
} finally {
if (oout != null) {
oout.close();
} else {
bout.close();
}
} int endIdx = out.writerIndex(); out.setInt(startIdx, endIdx - startIdx - 4);
}
}

ObjectEncoder继承了MessageToByteEncoder,而MessageToByteEncoder又继承了ChannelOutboundHandlerAdapter。为什么是OutBound呢?这是因为我们是要对写入的对象进行转换,所以是outbound。

首先使用ByteBufOutputStream对out ByteBuf进行封装,在bout中,首先写入了一个LENGTH_PLACEHOLDER字段,用来表示stream中中Byte的长度。然后用一个CompactObjectOutputStream对bout进行封装,最后就可以用CompactObjectOutputStream写入对象了。

对应的,netty还有一个ObjectDecoder对象,用于将ByteBuf转换成对应的对象,ObjectDecoder继承自LengthFieldBasedFrameDecoder,实际上他是一个ByteToMessageDecoder,也是一个ChannelInboundHandlerAdapter,用来对数据读取进行处理。

我们看下ObjectDecoder中最重要的decode方法:

    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
ByteBuf frame = (ByteBuf) super.decode(ctx, in);
if (frame == null) {
return null;
} ObjectInputStream ois = new CompactObjectInputStream(new ByteBufInputStream(frame, true), classResolver);
try {
return ois.readObject();
} finally {
ois.close();
}
}

上面的代码可以看到,将输入的ByteBuf转换为ByteBufInputStream,最后转换成为CompactObjectInputStream,就可以直接读取对象了。

使用编码和解码器

有了上面两个编码解码器,直接需要将其添加到client和server端的ChannelPipeline中就可以了。

对于server端,其核心代码如下:

//定义bossGroup和workerGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(
// 添加encoder和decoder
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new PojoServerHandler());
}
}); // 绑定端口,并准备接受连接
b.bind(PORT).sync().channel().closeFuture().sync();

同样的,对于client端,我们其核心代码如下:

EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(
// 添加encoder和decoder
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new PojoClientHandler());
}
}); // 建立连接
b.connect(HOST, PORT).sync().channel().closeFuture().sync();

可以看到上面的逻辑就是将ObjectEncoder和ObjectDecoder添加到ChannelPipeline中即可。

最后,就可以在客户端和浏览器端通过调用:

ctx.write("加油!");

直接写入字符串对象了。

总结

有了ObjectEncoder和ObjectDecoder,我们就可以不用受限于ByteBuf了,程序的灵活程度得到了大幅提升。

本文的例子可以参考:learn-netty4

本文已收录于 http://www.flydean.com/08-netty-pojo-buf/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

netty系列之:使用POJO替代buf的更多相关文章

  1. 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?

    [读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...

  2. Netty 系列之 Netty 高性能之道 高性能的三个主题 Netty使得开发者能够轻松地接受大量打开的套接字 Java 序列化

    Netty系列之Netty高性能之道 https://www.infoq.cn/article/netty-high-performance 李林锋 2014 年 5 月 29 日 话题:性能调优语言 ...

  3. Netty系列(四)TCP拆包和粘包

    Netty系列(四)TCP拆包和粘包 一.拆包和粘包问题 (1) 一个小的Socket Buffer问题 在基于流的传输里比如 TCP/IP,接收到的数据会先被存储到一个 socket 接收缓冲里.不 ...

  4. Netty 系列(三)Netty 入门

    Netty 系列(三)Netty 入门 Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更多请参考:Netty Github 和 Netty中文 ...

  5. 6. 彤哥说netty系列之Java NIO核心组件之Buffer

    --日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第六篇. 简介 上一章我们一起学习了Java NIO的核心组件Channel,它可以看作是实体与实体之间的连接,而且需要与Buffer交 ...

  6. netty系列之:netty架构概述

    目录 简介 netty架构图 丰富的Buffer数据机构 零拷贝 统一的API 事件驱动 其他优秀的特性 总结 简介 Netty为什么这么优秀,它在JDK本身的NIO基础上又做了什么改进呢?它的架构和 ...

  7. netty系列之:自定义编码和解码器要注意的问题

    目录 简介 自定义编码器和解码器的实现 ReplayingDecoder 总结 简介 在之前的系列文章中,我们提到了netty中的channel只接受ByteBuf类型的对象,如果不是ByteBuf对 ...

  8. Netty 系列目录

    Netty 系列目录 二 Netty 源码分析(4.1.20) 1.1 Netty 源码(一)Netty 组件简介 2.1 Netty 源码(一)服务端启动 2.2 Netty 源码(二)客户端启动 ...

  9. 1. 彤哥说netty系列之开篇(有个问卷调查)

    你好,我是彤哥,本篇是netty系列的第一篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 本文主要讲述netty系列的整体规划,并调查一下大家喜欢的学习方式. 知识点 ne ...

随机推荐

  1. count、counta函数巧妙运用于合并单元格填充序号

    函数运用: 1.COUNT(value1,value2, ...)      value1 是必需参数. 要计算其中数字的个数的第一项.单元格引用或区域.      value2, ... 为可选参数 ...

  2. jenkins pipeline构建后发送邮件通知

    jenkins pipeline构建后发送邮件通知 mail配置 进入系统配置 找到最下边的邮件通知 创建任务 Pipeline片段 post { always { bat "" ...

  3. 教你几招HASH表查找的方法

    摘要:根据设定的哈希函数 H(key) 和所选中的处理冲突的方法,将一组关键字映象到一个有限的.地址连续的地址集 (区间) 上,并以关键字在地址集中的"象"作为相应记录在表中的存储 ...

  4. XCTF 3rd-GCTF-2017 hackme

    一.查壳的老生常谈了..2分的题目就不多bb了. 二..elf文件,拖入ida,直接查找字符串,找到对应的函数 三.直接分析: 这里讲道理我当时很懵逼,因为进去这个函数后,发现伪码非常复杂,这里困了挺 ...

  5. CA和SSL证书介绍

    一.什么是CA? CA是证书的签发机构,它是公钥基础设施(Public Key Infrastructure,PKI)的核心.CA是负责签发证书.认证证书.管理已颁发证书的机关.CA 拥有一个证书(内 ...

  6. MySQL | Xtrabackup 安装

    rpm方式安装 # xtrabackup 2.4.8 安装包 wget https://www.percona.com/downloads/XtraBackup/Percona-XtraBackup- ...

  7. WIN10家庭版 访问WINXP 共享打印机

    WIN10家庭版 1.安装对应的打印机驱动 2.打开WIN10计算机---在地址栏中输入:\\计算机XP名称,显示对应的共享资源,直接选择即可.如果无法访问则进行如下第三步 3.设置过程 开始 -设置 ...

  8. C语言:伪代码

    伪代码(Pseudocode)是一种算法描述语言.使用伪代码的目的是为了使被描述的算法可以容易地以任何一种编程语言(C, Java, Pascal)实现.因此,伪代码必须结构清晰,代码简单,可读性好, ...

  9. Java异常情况

    从网上了解了这些Java异常,遇到过一些,大部分还没遇到: 1. SQLException:操作数据库异常类. 2. ClassCastException:数据类型转换异常. 3. NumberFor ...

  10. java集合(1)-概述

    Java集合类是一种特别有用的工具类,可用于存储数量不等的对象,并可以实现常用的数据结构,如栈,队列等,此外Java集合还可以用于保存具有映射关系的关联数组.java集合大致可分为Set,List,Q ...