基于Java提供的对象输入/输出流ObjectInputStream和ObjectOutputStream,可以直接把Java对象作为可存储的字节数组写入文件,也可以传输到网络上。对程序员来说,基于JDK默认的序列号机制可以避免操作底层的字节数组,从而提高开发效率。
相信大多数Java程序员接触到的第一种序列化或者编解码技术就是Java默认序列化,只需要序列化的POJO对象实现java.io.Serializable接口,根据实际情况生成序列ID,这个类就能通过java.io.ObjectInput和java.io.ObjectOutput序列化和反序列化。
1、服务端开发
(1)在服务端ChannelPipeline中添加ObjectDecoder解码器。
(2)在服务端ChannelPipeline中添加ObjectEncoder编码器。
(3)需要序列化的POJO类实现Serializable接口。
(4)构造服务端处理类,处理客户端请求。
请求POJO类
package com.serial.java.pojo; import java.io.Serializable; /** * * @author leeka * @notice * 1、实现Serializable接口 * 2、生成默认的序列化ID * 3、重写toString()方法,方便输出 */ public class SubscribeReq implements Serializable { private static final long serialVersionUID = 1L; private int subReqID; private String userName; private String productName; private String phoneNumber; private String address; public int getSubReqID() { return subReqID; } public void setSubReqID(int subReqID) { this.subReqID = subReqID; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "SubscribeReq [subReqID=" + subReqID + ", userName=" + userName + ", productName=" + productName + ", phoneNumber=" + phoneNumber + ", address=" + address + "]"; } }
响应POJO类
package com.serial.java.pojo; import java.io.Serializable; /** * * @author leeka * @notice * 1、实现Serializable接口 * 2、生成默认的序列化ID * 3、重写toString()方法,方便输出 */ public class SubscribeResp implements Serializable{ private static final long serialVersionUID = 1L; private int subReqID; private int respCode; private String desc; public int getSubReqID() { return subReqID; } public void setSubReqID(int subReqID) { this.subReqID = subReqID; } public int getRespCode() { return respCode; } public void setRespCode(int respCode) { this.respCode = respCode; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } @Override public String toString() { return "SubscribeResp [subReqID=" + subReqID + ", respCode=" + respCode + ", desc=" + desc + "]"; } }
服务端入口
package com.serial.java; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; public class SubReqServer { public void bind(int port)throws Exception{ //配置服务端NIO线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try{ ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(new ObjectDecoder(1024*1024//为防止异常码流和解码错位导致的内存溢出,将对象序列化后的最大字节数组长度设为1M , ClassResolvers.weakCachingConcurrentResolver(//创建线程安全的WeakReferenceMap对类加载器进行缓存 this.getClass().getClassLoader()))) .addLast(new ObjectEncoder())//对实现了Serializable的POJO对象进行编码 .addLast(new SubReqServerHandler()); } }); //绑定端口,同步等待成功 ChannelFuture f = b.bind(port).sync(); //等待服务端监听端口关闭 f.channel().closeFuture().sync(); }finally{ //退出时释放资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception{ int port = 8084; if(args!=null && args.length > 0){ port = Integer.valueOf(args[0]); } new SubReqServer().bind(port); } }
自定义服务端处理类
package com.serial.java; import com.serial.java.pojo.SubscribeReq; import com.serial.java.pojo.SubscribeResp; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; public class SubReqServerHandler extends ChannelHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { SubscribeReq req = (SubscribeReq) msg; if("leeka".equalsIgnoreCase(req.getUserName())){ System.out.println("service accept client subscribe req:["+ req +"]"); ctx.writeAndFlush(resp(req.getSubReqID())); } } private SubscribeResp resp(int subReqID){ SubscribeResp resp = new SubscribeResp(); resp.setSubReqID(subReqID); resp.setRespCode(0); resp.setDesc("Netty book order succeed, 3 days later, sent to the designated address"); return resp; } // @Override // public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { // ctx.flush(); // } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //super.exceptionCaught(ctx, cause); ctx.close(); } }
2、客户端开发
(1)将Netty对象编码器和解码器添加到ChannelPipeline。
(2)客户端一次构造十个请求,最后一次性发送给服务端。
(3)自定义客户端处理类打印服务端响应的消息。
客户端入口
package com.serial.java; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; public class SubReqClient { public void connect(int port,String host)throws Exception{ //配置客户端NIO线程组 EventLoopGroup group = new NioEventLoopGroup(); try{ Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(new ObjectDecoder(1024 , ClassResolvers.cacheDisabled(//禁止对类加载器进行缓存 this.getClass().getClassLoader()))) .addLast(new ObjectEncoder())//对实现了Serializable的POJO对象进行编码 .addLast(new SubReqClientHandler()); }; }); //发起异步连接操作 ChannelFuture f = b.connect(host,port).sync(); //等待客户端链路关闭 f.channel().closeFuture().sync(); }finally{ //退出,释放资源 group.shutdownGracefully(); } } public static void main(String[] args)throws Exception { int port = 8084; if(args!=null && args.length > 0){ port = Integer.valueOf(args[0]); } new SubReqClient().connect(port, "127.0.0.1"); } }
客户端处理类
package com.serial.java; import java.util.logging.Logger; import com.serial.java.pojo.SubscribeReq; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; public class SubReqClientHandler extends ChannelHandlerAdapter { private static final Logger logger = Logger.getLogger(SubReqClientHandler.class.getName()); public SubReqClientHandler() { } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { for (int i = 0; i < 10; i++) { ctx.write(req(i)); } ctx.flush(); } private SubscribeReq req(int i){ SubscribeReq r = new SubscribeReq(); r.setSubReqID(i); r.setAddress("浙江省杭州市西湖区"); r.setPhoneNumber("1516844444"+i); r.setProductName("Netty权威指南系列"+i); r.setUserName("leeka"); return r; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("receive server response:["+msg+"]"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.warning("unexpected exception from downstream:"+ cause.getMessage()); ctx.close(); } }
Java序列化虽然很方便,但也有它的缺点: (1)无法跨语言;(2)序列化后码流大;(3)序列化性能低。目前业界主流的编解码框架有Google的Protobuf、Facebook的Thrift、Jboss的Marshalling等,大家不妨结合这些主流的编解码框架进行编解码操作。
OVER
相关推荐
jboss marshalling 1.3.0 Java 对象序列化 netty 编解码
通常我们也习惯将编码(Encode)称为序列化(serialization),它将...1.2.1.Java序列化相信大多数Java程序员接触到的第一种序列化或者编解码技术就是Java默认提供的序列化机制,需要序列化的Java对象只需要实现java.i
相比于传统基于Java序列化+BIO(同步阻塞IO)的通信框架,性能提升了8倍多。 事实上,我对这个数据并不感到惊讶,根据我5年多的NIO编程经验,通过选择合适的NIO框架,加上高性能的压缩二进制编解码技术,精 心的设计...
NettyDemo Netty使用实例,对象传递调用; LineBasedFrameDecoder + 消息中得换行符;解决TCP 粘包问题; Java序列化方案编解码对比;对象调用方案
09.第九课自定义序列化协议之自定义序列化协议 10、第十课自定义数据包协议 11.第十一课粘包分包分析,如何避免socket攻击 12.分析设计一个聊天室的小项目 二、java NIO,AIO编程视频教程 1、java NIO,AIO编程_01...
1)Java序列化 2)业界主流的编解码框架 Thrift Protobuf 3) Websocket 5)Netty协议栈功能设计 6)Netty源码分析 ByteBuf工作原理 Channel, Unsafe ChannelPipline, ChannelHandler EventLoop, EventLoopGroup ...
Netty ...中级篇:编解码技术和常用的序列化框架(protobuf /java/Marshalling) 高级篇:Http协议开发; Netty 协议栈开发(数据结构定义,消息编解码,握手安全认证,心跳检测等); WebSocket等
用Netty实现一个简单的RPC框架,基本上rpc主要的知识点都涉及到了,包括协议的定义,序列化反序列化,动态代理,Spring自动装配,Netty编解码器等。可以通过这个项目加强对Netty的学习掌握,也可以加深对RPC的理解。...
消息网络传输除了JDK原生的对象序列化方式,还支持目前主流的编码解码器:kryo、hessian。 Netty网络模型采用主从Reactor线程模型,提升RPC服务器并行吞吐性能。 多线程模型采用guava线程库进行封装。 NettyRPC 1.0 ...
通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从...
通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从...
实现序列化和编解码 拆包粘包的实现 互聊 群聊 心跳 线程池的使用 自定义线程池 Mysql BinLog 原理: 数据库为了主从复制结构和容灾,都会有一份提交日志 (commit log),通过解析这份日志, 理论上说可以获取到每次...
A simple rpc framework based on netty4tinyrpc moduleclient 用于与server服务提供方通信codec 用于消费方,服务提供方数据流的编解码entity 用于消费方,服务提供方通信的实体proxy 负责对指定接口生成代理...
为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生。 为了让中间件开发者能将更多的精力放在产品功能特性实现...
IDLE 事件处理2、协议框架 ( protocol-skeleton )命令与命令处理器编解码处理器心跳触发器3、私有协议定制实现 - RPC 通信协议 ( protocol-implementation )RPC 通信协议的设计灵活的反序列化时机控制请求处理超时 ...