Netty--JDK序列化编解码传输对象
使用JDK序列化不需要额外的类库,只需要实现Serializable即可,但是序列化之后的码流只有Java才能反序列化,所以它不是跨语言的,另外由于Java序列化后码流比较大,效率也不高,所以在RPC中很少使用,本文只是做学习之用。
编解码器:
public class JdkDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
final int length = byteBuf.readableBytes();
final byte[] b = new byte[length];
byteBuf.getBytes(byteBuf.readerIndex(), b, 0, length);
ByteArrayInputStream bis = new ByteArrayInputStream(b);
ObjectInputStream ois = new ObjectInputStream(bis);
list.add(ois.readObject());
ois.close();
}
}
public class JdkEncoder extends MessageToByteEncoder<Object> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(o);
oos.flush();
byteBuf.writeBytes(bos.toByteArray());
bos.close();
oos.close();
}
}
---
传输对象:
public class Person implements Serializable{
private int age;
private String name;
private boolean man;
private List<String> list;
private Date birth;
private Person son;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
。。。
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", man=" + man +
", list=" + list +
", birth=" + birth +
", son=" + son +
'}';
}
}
---
Server端:
public class EchoServer {
public static void main(String[] args) {
new EchoServer().bind(8080);
}
public void bind(int port) {
//配置服务端的线程组,一个用于服务端接收客户端连接,另一个进行SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//ServerBootstrap用于启动NIO服务端的辅助启动类
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
//.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
//ch.pipeline().addLast(new MsgpackDecoder());
ch.pipeline().addLast(new JdkDecoder());
//在报文前增加2个字节,写消息长度
ch.pipeline().addLast(new LengthFieldPrepender(2));
//ch.pipeline().addLast(new MsgpackEncoder());
ch.pipeline().addLast(new JdkEncoder());
ch.pipeline().addLast(new EchoServerHandler());
}
});
//绑定端口,sync为同步阻塞方法,等待绑定成功,ChannelFuture用于异步操作的通知回调
ChannelFuture future = bootstrap.bind(port).sync();
System.out.println("server started");
//等待服务端监听端口关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("server shuting down");
//释放线程资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
int count = 0;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof Person){
Person p = (Person)msg;
Q.p(p.toString());
}else {
System.out.println("The server received(" + count++ + "): " + msg);
}
ctx.writeAndFlush(msg);//异步发送
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
---
Client端:
public class EchoClient {
public static void main(String[] args) {
new EchoClient().connect("127.0.0.1", 8080);
}
public void connect(String host, int port) {
//配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
//ch.pipeline().addLast(new MsgpackDecoder());
ch.pipeline().addLast(new JdkDecoder());
ch.pipeline().addLast(new LengthFieldPrepender(2));
//ch.pipeline().addLast(new MsgpackEncoder());
ch.pipeline().addLast(new JdkEncoder());
ch.pipeline().addLast(new EchoClientHandler());
}
});
//发起异步连接操作,同步等待连接成功
ChannelFuture future = bootstrap.connect(host, port).sync();
System.out.println("client started");
//等待客户端链路关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("client shuting down");
//释放NIO线程组
group.shutdownGracefully();
}
}
}
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
private int count = 0;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
List l = new ArrayList<String>();
l.add("abc");
l.add("123");
Person p = new Person();
p.setName("luangeng");
p.setMan(true);
p.setBirth(new Date());
p.setList(l);
for (int i = 0; i < 10; i++) {
p.setAge(i);
ctx.write(p);
}
ctx.flush();
}
//服务端返回应答信息后调用
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof Person){
Person p = (Person)msg;
Q.p(p.toString());
}else {
Q.p(count++ + " client get: " + msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
---
执行结果:
client started
Person{age=0, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Person{age=1, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Person{age=2, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Person{age=3, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Person{age=4, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Person{age=5, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Person{age=6, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Person{age=7, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Person{age=8, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Person{age=9, name='luangeng', man=true, list=[abc, 123], birth=Wed Nov 22 21:18:48 CST 2017, son=null}
Netty也提供了基于JDK的编解码器,可以直接使用,比如:
channel.pipeline()
.addLast(new LengthFieldBasedFrameDecoder(65535, 0, 4, 0, 4))
.addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())))
.addLast(new LengthFieldPrepender(4))
.addLast(new ObjectEncoder())
.addLast(new ServerHandler());
---
MessagePack工具:
MessagePack是与JSON数据格式类似的二进制序列化格式,更快更小,并且是跨语言的,用于在多个语言之间交换数据。使用MessagePack实现的编解码器如下:
public class MsgpackEncoder extends MessageToByteEncoder<Object> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
MessagePack mp = new MessagePack();
byte[] raw = mp.write(o);
byteBuf.writeBytes(raw);
}
}
public class MsgpackDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
final int length = byteBuf.readableBytes();
final byte[] b = new byte[length];
byteBuf.getBytes(byteBuf.readerIndex(), b, 0, length);
MessagePack mp = new MessagePack();
list.add(mp.read(b));
}
}
---
使用这种编解码后,服务端和客户端接收到的对象都不能转换为Person对象。
end
Netty--JDK序列化编解码传输对象的更多相关文章
- Netty对常用编解码的支持
参考文献:极客时间傅健老师的<Netty源码剖析与实战>Talk is cheap.show me the code! Netty对编解码的支持 打开Netty的源码,它对很多的编码器都提 ...
- JDK Base64编解码1.7和1.8的坑
场景 对接一个第三方api接口,其中签名部分用的是JDK8的编码.我们线上采用JDK7,导致项目无法编译 替换编解码部分为1.7的代码,然后签名又不对 所以坑就在这里,结论,1.7的编解码有换行符导致 ...
- 【转】Netty系列之Netty编解码框架分析
http://www.infoq.com/cn/articles/netty-codec-framework-analyse/ 1. 背景 1.1. 编解码技术 通常我们也习惯将编码(Encode)称 ...
- Netty系列之Netty编解码框架分析
1. 背景 1.1. 编解码技术 通常我们也习惯将编码(Encode)称为序列化(serialization),它将对象序列化为字节数组,用于网络传输.数据持久化或者其它用途. 反之,解码(Decod ...
- (中级篇 NettyNIO编解码开发)第七章-java序列化
相信大多数Java程序员接触到的第一种序列化或者编解码技术就是.Java的默认序列化,只需要序列化的POJO对象实现java.io.Serializable接口,根据实际情况生成序列ID,这个类就能够 ...
- 从零开始实现简单 RPC 框架 7:网络通信之自定义协议(粘包拆包、编解码)
当 RPC 框架使用 Netty 通信时,实际上是将数据转化成 ByteBuf 的方式进行传输. 那如何转化呢?可不可以把 请求参数 或者 响应结果 直接无脑序列化成 byte 数组发出去? 答:直接 ...
- (中级篇 NettyNIO编解码开发)第八章-Google Protobuf 编解码-2
8.1.2 Protobuf编解码开发 Protobuf的类库使用比较简单,下面我们就通过对SubscrjbeReqProto进行编解码来介绍Protobuf的使用. 8-1 Protob ...
- JAVA基础4---序列化和反序列化深入整理(JDK序列化)
一.什么是序列化和反序列化? 序列化:将对象状态信息转化成可以存储或传输的形式的过程(Java中就是将对象转化成字节序列的过程) 反序列化:从存储文件中恢复对象的过程(Java中就是通过字节序列转化成 ...
- netty: 编解码之jboss marshalling, 用marshalling进行对象传输
jboss marshalling是jboss内部的一个序列化框架,速度也十分快,这里netty也提供了支持,使用十分方便. TCP在网络通讯的时候,通常在解决TCP粘包.拆包问题的时候,一般会用以下 ...
随机推荐
- CentOS7 修改默认时区为 北京时间
首先同步时间 yum install -y ntpdate ntpdate -u cn.pool.ntp.org 然后设置中国时区(北京时间) timedatectl set-timezone Asi ...
- 03_模拟DVD
package com.entity; import java.text.SimpleDateFormat; import java.util.Date; public class DVD { pri ...
- 定位CPU高问题三把斧
1.top -H -p PID 查看对应进程的哪个线程占用CPU过高 2.printf "%x\n" tid 将需要的线程ID转换为16进制格式 3.jstack pid &g ...
- 数据库连接错误:CannotAcquireResourceException: A ResourcePool could not acquire a resource from its primary factory or source.
学习Spring整合Hibernate的知识,新建一个工程,代码结构如下: 按如下步骤整合: 代码如下: hibernate.cfg.xml: <?xml version="1.0&q ...
- android 城市选择
我们在开发过程中兰冕会有选着城市地点等东西,这些都是常用的东西,所以我也就将他封装起来了先来看看效果吧 1.首先看下项目的结构: 2.看下整体的项目效果 三:主ativity private Cont ...
- 现代前端技术解析:Web前端技术基础
最近几年,越来越多的人投入到前端大军中:时至至今,前端工程师的数量仍然不能满足企业的发展需求:与此同时,互联网应用场景的复杂化提高了对前端工程师能力的要求,一部分初期前端工程师并不能胜任企业的工作 ...
- angular2.0学习日记1
使用NG2之前需要安装node以及Npm环境,并到node下下载ng2所需要得文件,具体配置请到https://angular.cn/docs/ts/latest/quickstart.html按照提 ...
- Selenium+PhantomJS使用初体验
抓取使用Ajax技术完成的网页内容时可以使用Selenium+PhantomJS技术 1.pip install selenium 2.下载Phantomjs不需要用pip 武汉科技大学首页有一块 ...
- 使用pjax时点击浏览器刷新按钮仅加载内容页的解决办法
pjax可以实现ajax的局部刷新功能,同时能改变地址栏的URL,因此支持浏览器的后退和前进功能. 但是,在使用中,若没有正确使用,仍然会出现一些问题. 比如,我们在使用pjax后,能够在不加载整个页 ...
- 每天一个linux命令(磁盘):【转载】du 命令
Linux du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的. 1.命令格式: du [选项][文件] 2.命令功能 ...