Java自身的序列化机制,就是只需要序列化的POJO对象实现Java.io.Serializable接口,根据实际情况生成序列ID,这个类就能够通过java.io.ObjectInput和java.io.ObjectOutput序列化和反序列化。
服务端:Netty服务端接收到客户端的用户订购请求消息
订购消息
package NettySerialization;
import java.io.Serializable;
/**
* Created by L_kanglin on 2017/6/23.
* 订购请求POJO类定义
*/
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 + '\'' +
'}';
}
}
服务端接收到请求消息,对用户名进行合法性校验。如果合法,则构造订购成功的应答消息返回给客户端。订购消息如下:
package NettySerialization;
import java.io.Serializable;
/**
* Created by L_kanglin on 2017/6/23.
*/
public class SubscribeResp implements Serializable{
private static final long serialVersionUID = 1L;
//订购编号
private int subReqID;
//订购结果:0 表示成功
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 NettySerialization;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
* Created by L_kanglin on 2017/6/13.
*/
public class SubReqServer {
public void bind(int port) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectDecoder(1024 * 1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new SubReqServerHandler());
}
});
//绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
//等待服务端监听端口关闭
f.channel().closeFuture().sync();
} finally {
//优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
int port=8080;
if(args !=null&& args.length>0){
port=Integer.valueOf(args[0]);
}
new SubReqServer().bind(port);
}
}
ObjectDecoder,负责对实现Serializable的POJO对象进行解码;
使用weakCachingConcurrentResolver创建线程安全的WeakReferenceMap对类加载器进行缓存,它支持多线程并发访问,当虚拟机内存不足时,会释放缓存中的内存,防止内存泄漏。为了防止异常码流和解码错位导致的内存溢出,这里将单个对象最大序列化后的字节数组长度设置为1M。
ObjectEncoder可以在消息发送时自动将实现Serializable的POJO对象进行编码,因此用户无须亲自对对象进行手工序列化,只需要关注自己的业务逻辑处理即可。对象序列化和反序列化都由Netty的对象编码器解决。
将订购处理handler SubReqServerHandler添加到ChannelPipeline的尾部用于业务逻辑处理。
package NettySerialization;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/**
* Created by L_kanglin on 2017/6/23.
*/
public class SubReqServerHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
SubscribeReq req= (SubscribeReq) msg;
if("LiKanglin".equalsIgnoreCase(req.getUserName())){
System.out.println("Service accept client subscribe req : [" + req.toString() + "]");
ctx.writeAndFlush(resp(req.getSubReqID()));
}
}
private SubscribeResp resp(int subReqID){
SubscribeResp resp=new SubscribeResp();
resp.setSubReqID(subReqID);
resp.setRespCode(0);
resp.setDesc("Netty is lovaly");
return resp;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close(); //关闭异常,关闭链路
}
}
客户端设计思路:
(1)创建客户端时,将Netty对象解码器和编码器添加到ChannelPipeline;
(2)链路被激活的时候构造订购请求消息发送,为了检验Netty的Java序列化功能是否支持TCP粘包和拆包,客户端一次构造10条订购请求,最后一次性发给服务端
(3)客户端订购处理handler将接收到的订购响应消息打印出来。
具体代码实现如下:
package NettySerializationClient;
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;
/**
* Created by L_kanglin on 2017/6/23.
*/
public class SubReqClient {
public void connnect(int port,String host) throws InterruptedException {
//配置客户端NIO线程组
EventLoopGroup group =new NioEventLoopGroup();
try{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectDecoder(1024, ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new SubReqClientHandler());
}
});
//发起异步连接操作
ChannelFuture f =b.connect(host,port).sync();
//等待客户端链路关闭
f.channel().closeFuture().sync();
}finally {
//优雅退出,释放NIO线程组
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
int port=8080;
if(args!=null && args.length>0){
port=Integer.valueOf(args[0]);
}
new SubReqClient().connnect(port,"127.0.0.1");
}
}
package NettySerializationClient;
import NettySerialization.SubscribeReq;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/**
* Created by L_kanglin on 2017/6/23.
*/
public class SubReqClientHandler extends ChannelHandlerAdapter {
public SubReqClientHandler() {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for(int i=0;i<10;i++){
ctx.write(subReq(i));
}
ctx.flush();
}
private SubscribeReq subReq(int i){
SubscribeReq req = new SubscribeReq();
req.setAddress("武汉市华中科技大学");
req.setPhoneNumber("155XXXXXXXXXXXXXX");
req.setProductName("Netty In Action");
req.setSubReqID(i);
req.setUserName("LiKanglin");
return req;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Receive server response : [" + 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();
}
}
ClassResolvers.cacheDisabled(this.getClass().getClassLoader()))禁止对类加载器进行缓存。
服务端运行如下:
Service accept client subscribe req : [SubscribeReq{subReqID=0, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
Service accept client subscribe req : [SubscribeReq{subReqID=1, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
Service accept client subscribe req : [SubscribeReq{subReqID=2, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
Service accept client subscribe req : [SubscribeReq{subReqID=3, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
Service accept client subscribe req : [SubscribeReq{subReqID=4, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
Service accept client subscribe req : [SubscribeReq{subReqID=5, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
Service accept client subscribe req : [SubscribeReq{subReqID=6, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
Service accept client subscribe req : [SubscribeReq{subReqID=7, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
Service accept client subscribe req : [SubscribeReq{subReqID=8, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
Service accept client subscribe req : [SubscribeReq{subReqID=9, userName='LiKanglin', productName='Netty In Action', phoneNumber='155XXXXXXXXXXXXXX', address='武汉市华中科技大学'}]
客户端运行如下:
Receive server response : [SubscribeResp{subReqID=0, respCode=0, desc='Netty is lovaly'}]
Receive server response : [SubscribeResp{subReqID=1, respCode=0, desc='Netty is lovaly'}]
Receive server response : [SubscribeResp{subReqID=2, respCode=0, desc='Netty is lovaly'}]
Receive server response : [SubscribeResp{subReqID=3, respCode=0, desc='Netty is lovaly'}]
Receive server response : [SubscribeResp{subReqID=4, respCode=0, desc='Netty is lovaly'}]
Receive server response : [SubscribeResp{subReqID=5, respCode=0, desc='Netty is lovaly'}]
Receive server response : [SubscribeResp{subReqID=6, respCode=0, desc='Netty is lovaly'}]
Receive server response : [SubscribeResp{subReqID=7, respCode=0, desc='Netty is lovaly'}]
Receive server response : [SubscribeResp{subReqID=8, respCode=0, desc='Netty is lovaly'}]
Receive server response : [SubscribeResp{subReqID=9, respCode=0, desc='Netty is lovaly'}]