java网络编程之Netty编解码技术(六)

java网络编程之Netty编解码技术(六)

概念理解

      编解码技术,说白了就是Java序列化技术,序列化目的就两个,第一进行网络传输,第二对象持久化。虽然我们可以使用java进行对象序列化,Netty去传输,但是Java序列化的硬伤太多,比如Java序列化没法跨语言、序列化后码流太大、序列化性能太低等等。JBoss Marshalling序列化的速度是JDK的3倍。

主流的编解码框架:

1、JBoss的Marshalling包

2、Google的Protobuf

3、基于 Protobuf 的Kyro

4、MessagePack框架

每个编解码的框架都不难,下面我主要是通过JBoss来讲解,需要了解其他框架的可以去百度。

JBoos Marshalling

      JBoos Marshalling是一个java对象系列化的包,对JDK默认的序列化框架进行了优化,但又保持跟java.io.Serializable接口的兼容,同时增加了一些可调的参数和附加特征。

类库:jboss-marshalling-1.3.0 jboss-marshalling-serial-1.3.0

下载地址:https://www.jboss.org/jbossmarshalling/downloads

JBoos Marshalling与Netty结合后进行序列化对象的代码编写非常简单,下面我会一一介绍。

实体类

      首先,需要两个实体类,实体类都需要实现Serializable接口,Req类是用来做客户端的请求的, Resp类是用来做服务端返回给客户端的响应。

Req类代码如下:

public class Req implements Serializable{

    private static final long  SerialVersionUID = 1L;

    private String id ;
    private String name ;
    private String requestMessage ;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getRequestMessage() {
        return requestMessage;
    }
    public void setRequestMessage(String requestMessage) {
        this.requestMessage = requestMessage;
    }
}

Resp类代码如下:

public class Resp implements Serializable{

    private static final long serialVersionUID = 1L;

    private String id;
    private String name;
    private String responseMessage;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getResponseMessage() {
        return responseMessage;
    }
    public void setResponseMessage(String responseMessage) {
        this.responseMessage = responseMessage;
    }
}

这里代码没什么好说的

Jboss Marshalling 编解码工具类

在编写之前必须先导入Jboss需要的包

public final class MarshallingCodeCFactory {

    /**
     * 创建Jboss Marshalling解码器MarshallingDecoder
     * @return MarshallingDecoder
     */
    public static MarshallingDecoder buildMarshallingDecoder() {
        //1
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        //2
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        //3
        UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
        //4
        MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);
        return decoder;
    }

    /**
     * 创建Jboss Marshalling编码器MarshallingEncoder
     * @return MarshallingEncoder
     */
    public static MarshallingEncoder buildMarshallingEncoder() {
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
        //5
        MarshallingEncoder encoder = new MarshallingEncoder(provider);
        return encoder;
    }
}

1、首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。

2、创建了MarshallingConfiguration对象,配置了版本号为5

3、根据marshallerFactory和configuration创建provider

4、构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度

5、构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组

个人感觉不难都是一些固定的写法,记住每一步要做什么就好。

服务端


public class Server {

    public static void main(String[] args) throws Exception{

        EventLoopGroup pGroup = new NioEventLoopGroup();
        EventLoopGroup cGroup = new NioEventLoopGroup();

        ServerBootstrap b = new ServerBootstrap();
        b.group(pGroup, cGroup)
         .channel(NioServerSocketChannel.class)
         .option(ChannelOption.SO_BACKLOG, 1024)
         //1
         .handler(new LoggingHandler(LogLevel.INFO))
         .childHandler(new ChannelInitializer<SocketChannel>() {
            protected void initChannel(SocketChannel sc) throws Exception {
                //2
                sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                //3
                sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                sc.pipeline().addLast(new ServerHandler());
            }
        });

        ChannelFuture cf = b.bind(8765).sync();

        cf.channel().closeFuture().sync();
        pGroup.shutdownGracefully();
        cGroup.shutdownGracefully();

    }
}

这里其实没什么改动,跟我上一篇文章的Netty流数据的传输处理差不多,

1、设置日志(这里需要自己导入log4j.jar) 当然你也可以不设置打印日记

2、这是我们写的Jboss Marshalling工具类的解码器MarshallingDecoder

3、这是我们写的Jboss Marshalling工具类的编码器MarshallingEncoder

其它代码说明可以去看我的Netty第一个程序的那篇文章。

服务端业务处理


public class ServerHandler extends ChannelHandlerAdapter{

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Req req = (Req)msg;
        System.out.println("Server : " + req.getId() + ", " + req.getName() + ", " + req.getRequestMessage());
        Resp resp = new Resp();
        resp.setId(req.getId());
        resp.setName("resp" + req.getId());
        resp.setResponseMessage("响应内容" + req.getId());
        ctx.writeAndFlush(resp);//.addListener(ChannelFutureListener.CLOSE);
    }


}

这里业务逻辑主要是将客户端传过来的信息进行打印,然后将数据传入返回的实体类Resp,将实体类Resp返回给客户端

客户端

public class Client {


    public static void main(String[] args) throws Exception{

        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        b.group(group)
         .channel(NioSocketChannel.class)
         .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel sc) throws Exception {
                //1  解码
                sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                //2  编码
                sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                sc.pipeline().addLast(new ClientHandler());
            }
        });

        ChannelFuture cf = b.connect("127.0.0.1", 8765).sync();

        //3
        for(int i = 0; i < 5; i++ ){
            Req req = new Req();
            req.setId("" + i);
            req.setName("pro" + i);
            req.setRequestMessage("数据信息" + i);
            cf.channel().writeAndFlush(req);
        }

        cf.channel().closeFuture().sync();
        group.shutdownGracefully();
    }
}

这里编解码跟服务端一样,不再详细说,不懂可以回头看看服务端的。

3、for循环向服务端发送五条消息

客户端业务处理


public class ClientHandler extends ChannelHandlerAdapter{

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            Resp resp = (Resp)msg;
            System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " + resp.getResponseMessage());          
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

}

这里很简单,没什么好说的了。

好!到这目前所有代码都编写完了来测试一下效果。

服务端启动打印如图:

这里写图片描述

客户端启动打印如图:

这里写图片描述

客户端启动后服务端的打印:

这里写图片描述

源代码:https://github.com/hfbin/Thread_Socket/tree/master/Socket/serializable

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33524158/article/details/78626208

猜你喜欢

转载自blog.csdn.net/aa1358075776/article/details/81207389