前言

何为编解码,通俗的来说,我们需要将一串文本信息从A发送到B并且将这段文本进行加工处理,如:A将信息文本信息编码为2进制信息进行传输。B接受到的消息是一串2进制信息,需要将其解码为文本信息才能正常进行处理。

上章我们介绍的Netty如何解决拆包和粘包问题,就是运用了解码的这一功能。

java默认的序列化机制

使用Netty大多是java程序猿,我们基于一切都是对象的原则,经常会将对象进行网络传输,那么对于序列化操作肯定大家都是非常熟悉的。

一个对象是不能直接进行网络I/O传输的,jdk默认是将对象转换为可存储的字节数组来进行网络操作。基于JDK默认的序列化机制可以避免操作底层的字节数组,从而提升开发效率。

jdk默认的序列化机制虽然能给程序猿带来极大的方便,但是它也带来了许多问题:

  1. 无法跨语言。
  2. 序列化后的码流太大,会给网络传输带来极大的开销。
  3. 序列化的性能太低,对于高性能的网络架构是极其不友好的。

主流的编解码框架

  1. Google的Protobuf。
  2. Facebok的Thrift。
  3. Jboss Marshalling
  4. MessagePack

这几类编解码框架都有各自的特点,有兴趣的童鞋可以自己对其进行研究。

我们这里主要对MessagePack进行讲解。

MessagePack简介

MessagePack是一个高效的二进制序列化框架,它像JSON一样支持不同的语言间的数据交换,并且它的性能更快,序列化之后的码流也更小。

它的特点如下:

  1. 编解码高效,性能高
  2. 序列化之后的码流小,利于网络传输或存储
  3. 支持跨语言

MessagePack Java Api的使用

  1. 首先导包
<!-- https://mvnrepository.com/artifact/org.msgpack/msgpack -->
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack</artifactId>
<version>0.6.12</version>
</dependency>
  1. 使用API进行编码和解码
List<String> nameList = new ArrayList<String>();
nameList.add("Tom");
nameList.add("Jack");
MessagePack messagePack = new MessagePack();
//开始序列化
byte[] raw = messagePack.write(nameList);
//使用MessagePack的模版,来接序列化后的字节数组转换为List
List<String> deNameList = messagePack.read(raw,Templates.tList(Templates.TString));
System.out.println(deNameList.get(0));
System.out.println(deNameList.get(1));
System.out.println(deNameList.get(2));

Netty中如何使用MessagePack

编码器的实现

public class MsgpackEncoder extends MessageToByteEncoder {

    @Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
MessagePack msgpack = new MessagePack();
//使用MessagePack对要发送的数据进行序列化
byte[] raw = msgpack.write(msg);
out.writeBytes(raw); } }

解码器的实现

public class MsgpackDecoder extends MessageToMessageDecoder<ByteBuf> {

    @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
//从msg中获取需要解码的byte数组
final int length = msg.readableBytes();
byte[] b = new byte[length];
msg.getBytes(msg.readerIndex(), b,0,length);
//使用MessagePack的read方法将其反序列化成Object对象,并加入到解码列表out中
MessagePack msgpack = new MessagePack();
out.add(msgpack.read(b));
} }

实现该编码器和解码器的Netty服务端

public class NettyServer {
public void bind(int port) throws Exception {
EventLoopGroup bossGruop = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGruop, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// TODO Auto-generated method stub
socketChannel.pipeline()
//添加支持粘包、拆包解码器,意义:从头两个字节解析出数据的长度,并且长度不超过1024个字节
.addLast("frameDecoder",new LengthFieldBasedFrameDecoder(1024, 0, 2,0,2))
//反序列化解码器
.addLast("msgpack decoder",new MsgpackDecoder())
//添加支持粘包、拆包编码器,发送的每个数据都在头部增加两个字节表消息长度
.addLast("frameEncoder",new LengthFieldPrepender(2))
//序列化编码器
.addLast("msgpack encoder",new MsgpackEncoder()
//后续自己的业务逻辑
.addLast(new ServerHandler());
}
});
try {
ChannelFuture future = bootstrap.bind(port).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGruop.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}

实现该编码器和解码器的Netty客户端

public class NettyClient {
private void bind(int port, String host) {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// TODO Auto-generated method stub
socketChannel.pipeline()
.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2))
.addLast("msgpack decoder", new MsgpackDecoder())
.addLast("frameEncoder", new LengthFieldPrepender(2))
.addLast("msgpack encoder", new MsgpackEncoder())
.addLast(new ClientHandler());
}
});
try {
ChannelFuture f = b.connect(host, port).sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}

可以看出客户端的代码与服务端基本相同,所以啊,如果能熟练掌握Netty,今后在自己的项目中运用上定制化编解码的传输,将会是一件十分简单的活路。

总结

无论是之前解决粘包拆包问题,还是这里的使用序列化框架来进行编解码。我相信读者学习到这里,对于Netty的使用都有了较为全面的了解。其实Netty帮我们解决了很多底层棘手问题,如客户端断连、句柄泄漏和消息丢失等等。所以我们才能十分简单开发出一个稳定的网络通讯项目。

Netty入门系列(3) --使用Netty进行编解码的操作的更多相关文章

  1. Netty入门系列(1) --使用Netty搭建服务端和客户端

    引言 前面我们介绍了网络一些基本的概念,虽然说这些很难吧,但是至少要做到理解吧.有了之前的基础,我们来正式揭开Netty这神秘的面纱就会简单很多. 服务端 public class PrintServ ...

  2. Netty入门系列(2) --使用Netty解决粘包和拆包问题

    前言 上一篇我们介绍了如果使用Netty来开发一个简单的服务端和客户端,接下来我们来讨论如何使用解码器来解决TCP的粘包和拆包问题 TCP为什么会粘包/拆包 我们知道,TCP是以一种流的方式来进行网络 ...

  3. Netty 源码 ChannelHandler(四)编解码技术

    Netty 源码 ChannelHandler(四)编解码技术 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一.拆包与粘 ...

  4. [Netty] - Netty入门(最简单的Netty客户端/服务器程序)

    Java中的NIO是一种解决阻塞式IO问题的基本技术,但是NIO的编写对java程序员是有比较高的要求的.那么Netty就是一种简化操作的一个成熟的网络IO编程框架.这里简单介绍一个程序,代码是< ...

  5. Netty入门1之----认识Netty

      Netty 什么是Netty? Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架. Netty 是一个广泛使用的 Java ...

  6. [转]Netty入门(最简单的Netty客户端/服务器程序)

    Java中的NIO是一种解决阻塞式IO问题的基本技术,但是NIO的编写对java程序员是有比较高的要求的.那么Netty就是一种简化操作的一个成熟的网络IO编程框架.这里简单介绍一个程序,代码是< ...

  7. Netty入门一:何为Netty

    先了解java的网络编程 Netty为何支持高并发 netty是基于java的nio非阻塞通信,而原始的阻塞通信无法满足高并发.下面我们通过两幅图来简要说明 BIO: 这种模式下一个线程处理一个连接, ...

  8. Spark RDD/Core 编程 API入门系列之动手实战和调试Spark文件操作、动手实战操作搜狗日志文件、搜狗日志文件深入实战(二)

    1.动手实战和调试Spark文件操作 这里,我以指定executor-memory参数的方式,启动spark-shell. 启动hadoop集群 spark@SparkSingleNode:/usr/ ...

  9. Java入门系列-23-NIO(使用缓冲区和通道对文件操作)

    NIO 是什么 java.nio全称java non-blocking(非阻塞) IO(实际上是 new io),是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(bo ...

随机推荐

  1. sort()函数到底是怎样进行数字排序的

    很多人会用sort(),并不见得知道它具体是怎样给数字排序的.其实不知道也行,会用就可以,感兴趣的可以来看看. var numberArray = [2,4,1,3]; numberArray.sor ...

  2. Node.js学习笔记(2):基本模块

    Node.js学习笔记(2):基本模块 模块 引入模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在No ...

  3. nokogiri

    Nokogiri的用法我推荐三篇非常给力的文章:http://ruby.bastardsbook.com/chapters/html-parsing/http://ruby.bastardsbook. ...

  4. 获取原生的DOM方式,DIY脚手架,vue-cli的使用

    一 . 获取原生的DOM的方式 在js中,我们可以通过id, class 或者标签获取DOM元素,vue中也为我们提供了获取原生DOM的方法,就是给标签或者组件添加 ref 属性,通过 this.$r ...

  5. 《高性能Javascript》 Summary(二)

    第四章.算法和流程控制 Algorithms And Flow Control 原因:代码整体结构是执行速度的决定因素之一.代码量少不一定运行速度快,代码量多不一定运行速度慢.性能损失与组织代码和具体 ...

  6. <关于JSP技术>运行机制及语法概述(附对本次同济校内ACM选拔赛决赛的吐槽)

    (一)JSP运行的机制 JSP是一种建立在Servlet规范功能之上的动态网页技术,它们都是在通常的网页文件中嵌入脚本代码,用于产生动态内容,不过和ASP不同的是JSP文件中嵌入的是Java代码和JS ...

  7. java.sql.SQLException: Access denied for user 'somebody'@'localhost' (using password: YES)

    用mybatis和spring整合时出现了一个错误: 我是在IntelliJ IDEA上整合Mybatis和Spring的,运行测试用例出现了如上错误. 红色的马赛克部分是我的名字. 问题是,我的数据 ...

  8. LightOJ 1132 Summing up Powers:矩阵快速幂 + 二项式定理

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1132 题意: 给定n.k,求(1K + 2K + 3K + ... + NK) % 2 ...

  9. Es6 学习笔记(一)数组扩展

    扩展运算符 ... 1.数组的扩展运算符将一个数组转换成一个逗号分隔的参数序列 console.log(...[1,2,3])   //1,2,3 ['a', 'b',...[1,3]]   //a, ...

  10. 分享知识-快乐自己:Struts2文件上传及文件下载

    1)Struts2单文件上传 action:类文件 package com.mlq.action; import com.opensymphony.xwork2.ActionSupport; impo ...