Netty4自带编解码器详解
前言
本篇文章是Netty专题的第五篇,前面四篇文章如下:
作为一个高性能的异步、NIO通信框架,编解码框架是Netty的重要组成部分。
从网络中读取消息,需要经过解码,将二进制的数据报转换成应用层协议消息,才能够被应用逻辑识别。同样的道理,客户端发送给服务器的消息,也需要经过编码转换成二进制字节数组(Netty就是ByteBuf)才能够发送到网络对端。编码和解码功能是NIO框架必不可少的一部分。
Netty为了降低用户的开发难度,对原始的NIO进行封装,提供了常用的功能和API,屏蔽了底层的实现细节。对于不想了解底层实现的用户,使用Netty自带的编解码器非常容易,都能够快速上手,提高开发效率。
Netty在这方面做得非常好,对编解码功能,提供了通用的编解码框架可以让用户扩展,又提供了常用的一些编解码类让用户直接使用。
Netty自带的编解码功能列表如下:
- String
- Protobuf
- Base64
- Object
- 其他等等…..
本篇文章只讲解我列出来的几个,还有一些像粘包的解码器我们后面单独写文章进行讲解。
String编解码
String编解码在Netty中对应的类是io.netty.handler.codec.string.StringEncoder和io.netty.handler.codec.string.StringDecoder,提供字符串数据的传输编解码工作。
关于String编解码的使用这边不做过多讲解,可以参考我的《高性能NIO框架Netty入门篇》中的使用。
Protobuf编解码
Protobuf编解码在Netty中对应的类是io.netty.handler.codec.protobuf.ProtobufDecoder和io.netty.handler.codec.protobuf.ProtobufEncoder,提供基于Protobuf序列化的数据传输编解码工作。
关于Protobuf编解码的使用这边不做过多讲解,可以参考我的《高性能NIO框架Netty-整合Protobuf高性能数据传输》中的使用。
Base64编解码
base64的使用需要在String的基础上,不然消息是无法直接传递。
服务端
/**
* Base64编解码示例
* @author yinjihuan
*
*/
public class Base64EncoderAndDecoderServer {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast("base64Decoder", new Base64Decoder());
ch.pipeline().addLast("base64Encoder", new Base64Encoder());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.err.println("server:" + msg.toString());
ctx.writeAndFlush(msg.toString() + "你好");
}
});
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
ChannelFuture f = bootstrap.bind(2222).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
客户端
/**
* Base64编解码示例
* @author yinjihuan
*
*/
public class Base64EncoderAndDecoderClient {
public static void main(String[] args) {
EventLoopGroup workerGroup = new NioEventLoopGroup();
Channel channel = null;
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast("base64Decoder", new Base64Decoder());
ch.pipeline().addLast("base64Encoder", new Base64Encoder());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.err.println("client:" + msg.toString());
}
});
}
});
ChannelFuture f = b.connect("127.0.0.1", 2222).sync();
channel = f.channel();
channel.writeAndFlush("hello yinjihuan");
} catch(Exception e) {
e.printStackTrace();
}
}
}
Object编解码
Object编解码在Netty中对应的类是io.netty.handler.codec.serialization.ObjectEncoder和io.netty.handler.codec.serialization.ObjectDecoder,提供基于对象序列化的数据传输编解码工作。
之前我们在《高性能NIO框架Netty-对象传输》中通过自定义编码器来实现了PO对象的传输,今天就用Netty自带的Object来进行编解码工作。
传输对象
public class ObjectMessage implements Serializable {
private static final long serialVersionUID = -7543514952950971498L;
private String id;
private String content;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
服务端
/**
* Object编解码示例
* @author yinjihuan
*
*/
public class ObjectEncoderAndDecoderServer {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(
this.getClass().getClassLoader()
)));
ch.pipeline().addLast("encoder", new ObjectEncoder());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ObjectMessage m = (ObjectMessage) msg;
System.err.println("server:" + m.getContent());
}
});
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
ChannelFuture f = bootstrap.bind(2222).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
客户端
/**
* Object编解码示例
* @author yinjihuan
*
*/
public class ObjectEncoderAndDecoderClient {
public static void main(String[] args) {
EventLoopGroup workerGroup = new NioEventLoopGroup();
Channel channel = null;
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(
this.getClass().getClassLoader()
)));
ch.pipeline().addLast("encoder", new ObjectEncoder());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ObjectMessage m = (ObjectMessage) msg;
System.err.println("client:" + m.getContent());
}
});
}
});
ChannelFuture f = b.connect("127.0.0.1", 2222).sync();
channel = f.channel();
ObjectMessage m = new ObjectMessage();
m.setContent("hello yinjihuan");
channel.writeAndFlush(m);
} catch(Exception e) {
e.printStackTrace();
}
}
}
源码参考:https://github.com/yinjihuan/netty-im
- HERD--GCC宏
- C++拷贝构造函数(深拷贝,浅拷贝)
- 安装glog和gflags
- FFmpeg_3.2.4+SDL_2.0.5学习(1)音视频解码帧及显示/播放数据
- FFmpeg_3.2.4+SDL_2.0.5学习(2)视频同步基础
- ubuntu17.04更换主题
- ubuntu17.04新安装之后的软件准备
- 打造一流编辑器vimplus
- ffmpeg+sdl播放类
- 拉丁猪文字游戏
- 在Windows Mobile的控制台应用中使用Notification
- vim使用经验积累
- Spring+Mybatis+Maven+Mysql编程实战
- LD_LIBRARY_PATH和LIBRARY_PATH的区别
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- iOS开发之CryptoKit
- koroFileHeader更新日志
- 掌握浏览器重绘(reflow)重排(repaint)-前端进阶
- var和let/const的区别
- 刷新/关闭页面之前发送请求
- Web Beacon 刷新/关闭页面之前发送请求
- 解决django无法访问本地static文件(js,css,img)网页里js,cs都加载不了
- python3 logging日志封装实例
- 解决Python中报错TypeError: must be str, not bytes问题
- H5 notification浏览器桌面通知
- Android线程池控制并发数多线程下载
- Android progressbar实现带底部指示器和文字的进度条
- js 调用栈机制与ES6尾调用优化介绍
- Android Fragment实现列表和内容联动
- 前端中等算法-无重复字符的最长子串