十、编解码技术--Java序列化

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'}]

你可能感兴趣的:(netty)