Netty编解码技术之Marshalling

Marshaling

首先我这里准备了一张图,通过Marshalling模拟客户端像服务端发送数据包的过程。(该图是随便找的,图片内容与本文探讨的内容无关)
在这里插入图片描述

1.添加依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.12.Final</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>25.1-jre</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.7.1</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling</artifactId>
            <version>1.3.0.CR9</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling-serial</artifactId>
            <version>1.3.0.CR9</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.60</version>
        </dependency>
    </dependencies>


2.创建请求对象和响应对象

public class RequestData implements Serializable {
    
    
    private String id;
    private String name;
    private String requestMessage;
    private byte[] attachment;

    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;
    }

    public byte[] getAttachment() {
    
    
        return attachment;
    }

    public void setAttachment(byte[] attachment) {
    
    
        this.attachment = attachment;
    }
}
public class ResponseData implements Serializable {
    
    

    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;
    }
}

3.创建Marshalling编解码器

public final class MarshallingCodeCFactory {
    
    

    /**
     * 创建Jboss Marshalling解码器
     * @return MarshallingDecoder
     */
    public static MarshallingDecoder buildMarshallingDecoder(){
    
    
        //首先通过Marshalling工具类的方法获取Marshalling实例对象,参数serial标识创建的是java序列化工厂
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        //创建MarshallingConfiguration对象,配置版本号为5
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);

        //根据marshallerFactory和configuration创建provider
        UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);

        //构建Netty的MarshallingDecoder对象,俩参数分别为provider和单个消息序列化后的最大长度
        return new MarshallingDecoder(provider, 1024 * 1024);
    }

    /**
     * 创建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);

        //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
        return new MarshallingEncoder(provider);
    }
}

4.编写Client与Server对应的Handler

public class ClientHandler extends ChannelInboundHandlerAdapter {
    
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg){
    
    
        try{
    
    
            ResponseData rd = (ResponseData) msg;
            System.out.println("输出服务器端响应内容:" + rd.getId());
        }finally {
    
    
            ReferenceCountUtil.release(msg);
        }
    }
}
public class ServerHandler extends ChannelInboundHandlerAdapter {
    
    

    @Override
    public void channelRead(ChannelHandlerContext ctx,Object msg) throws IOException {
    
    

        //接受request请求 并进行业务处理
        RequestData rd = (RequestData) msg;

        System.out.println("id:" + rd.getId()+
                ",name:"+ rd.getName() +
                ",msg"+rd.getRequestMessage());


        //还原压缩后的字节数组
        byte[] data = ZipUtil.unGZip(rd.getAttachment());

        String path = System.getProperty("user.dir") +
                File.separatorChar + "receive" +
                File.separatorChar +
                "prometheus.jpeg";
        FileOutputStream fos = new FileOutputStream(path);
        fos.write(data);
        fos.close();

        //回送响应数据 完成ack
        ResponseData responseData = new ResponseData();
        responseData.setId("response " + rd.getId());
        responseData.setName("response " + rd.getName());
        responseData.setResponseMessage("响应信息");

        ctx.writeAndFlush(responseData);

    }
}

5.客户端与服务端代码

public class Client {
    
    
    public static void main(String[] args) throws InterruptedException, IOException {
    
    
        NioEventLoopGroup wGroup = new NioEventLoopGroup();

        Bootstrap b = new Bootstrap();
        b.group(wGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
    
    
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
    
    
                        sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                        sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                        sc.pipeline().addLast(new ClientHandler());
                    }
                });
        ChannelFuture cf = b.connect("127.0.0.1", 8765).sync();

        Channel channel = cf.channel();
        for (int i = 0; i < 100; i++) {
    
    

            RequestData rd = new RequestData();
            rd.setId(""+i);
            rd.setName("我是消息"+i);
            rd.setRequestMessage("内容"+i);
            String path = System.getProperty("user.dir") +
                    File.separatorChar + "source" +
                    File.separatorChar +
                    "prometheus.jpeg";
            File file = new File(path);

            FileInputStream fis = new FileInputStream(file);
            byte[] data = new byte[fis.available()];
            fis.read(data);
            fis.close();
            rd.setAttachment(ZipUtil.gZip(data));
            channel.writeAndFlush(rd);
        }
        cf.channel().closeFuture().sync();
        wGroup.shutdownGracefully();
    }
}
public class Server {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    
        EventLoopGroup bGroup = new NioEventLoopGroup(1);
        EventLoopGroup wGroup = new NioEventLoopGroup();//默认CPU核心数*2 感兴趣伙伴可以看看源码

        ServerBootstrap sb = new ServerBootstrap();

        sb.group(bGroup,wGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childHandler(new ChannelInitializer<SocketChannel>() {
    
    
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
    
    
                        sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                        sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                        sc.pipeline().addLast(new ServerHandler());

                    }
                });
        ChannelFuture cf = sb.bind(8765).sync();

        cf.channel().closeFuture().sync();

        bGroup.shutdownGracefully();
        wGroup.shutdownGracefully();
    }
}

6.涉及的工具类

public class ZipUtil {
    
    
    /**
     * 压缩GZip
     *
     * @param data
     * @author wcl
     * @return
     */
    public static byte[] gZip(byte[] data) {
    
    
        byte[] b = null;
        try {
    
    
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            GZIPOutputStream gzip = new GZIPOutputStream(bos);
            gzip.write(data);
            gzip.finish();
            gzip.close();
            b = bos.toByteArray();
            bos.close();
        } catch (Exception ex) {
    
    
            ex.printStackTrace();
        }
        return b;
    }
    /**
     * 解压GZip
     *
     * @param data
     * @author taoyi
     * @return
     */
    public static byte[] unGZip(byte[] data) {
    
    
        byte[] b = null;
        try {
    
    
            ByteArrayInputStream bis = new ByteArrayInputStream(data);
            GZIPInputStream gzip = new GZIPInputStream(bis);
            byte[] buf = new byte[1024];
            int num = -1;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while ((num = gzip.read(buf, 0, buf.length)) != -1) {
    
    
                baos.write(buf, 0, num);
            }
            b = baos.toByteArray();
            baos.flush();
            baos.close();
            gzip.close();
            bis.close();
        } catch (Exception ex) {
    
    
            ex.printStackTrace();
        }
        return b;
    }
}

7.查看结果

在这里插入图片描述

在这里插入图片描述
经过上述实验,我们不难发现,Client通过Marshalling顺利的将我们的数据包发送给了Server,并且Server也收到消息并完成了ACK响应。

在这里插入图片描述
并且图片也已经输出了。

总结

实现消息通信往往都是要经过两次处理,大致流程是这样的:
在这里插入图片描述
它的核心思路就是把Java对象转换成二进制数据以便网络传输,最后编解码器进行编解码的操作。

猜你喜欢

转载自blog.csdn.net/qq_45455361/article/details/121847090