Netty序列化深入理解与使用

Netty序列化深入理解与使用

简介

Netty是基于NIO实现的Java网络编程框架,它也是目前使用最为广泛的Java网络编程框架之一。在进行远程通信时,往往需要将对象进行序列化、反序列化后再进行传输,Netty自带了一些序列化方式,比如JDK序列化、Protobuf等,使用Netty内置的序列化方式可以方便地实现对象的传输。本文将详细讲解Netty序列化的使用、原理以及如何自定义序列化方式。

Netty内置的序列化方式

JDK序列化

JDK序列化是最简单、最常用的序列化方式之一。在序列化时,Java对象会被转换为二进制流,可以在网络中进行传输,收到数据后再将二进制流转换为Java对象。使用JDK序列化时,需要将对象类实现Serializable接口。

public class User implements Serializable {

    private String name;
    private int age;
    private List<String> hobbies;

    // Getter 和 Setter 省略
}

在Netty中使用JDK序列化,需要在ChannelPipeline中加入后续的处理器(如编码器和解码器)。

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new ObjectEncoder());
        pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
        pipeline.addLast(new MyServerHandler());
    }
}

Protobuf序列化

Protobuf是谷歌公司开源的一种高效的序列化协议。相比于JDK序列化,Protobuf序列化更加高效,可读性更强,编写方式也更加友好。使用Protobuf序列化,需要定义.proto文件并使用插件生成对应的Java文件。

proto文件示例:

syntax = "proto3";

message User {
    string name = 1;
    int32 age = 2;
    repeated string hobbies = 3;
}

生成的Java文件中包含了定义的message类。

在Netty中使用Protobuf序列化需要进行两个步骤:

  1. 定义将要序列化的类以及对应的.proto文件
  2. ChannelPipeline中加入后续的处理器(如编码器和解码器)
public class MyServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        // 定义将要序列化的类以及对应的proto文件路径
        pipeline.addLast(new ProtobufEncoder());
        pipeline.addLast(new ProtobufDecoder(UserProto.User.getDefaultInstance()));

        pipeline.addLast(new MyServerHandler());
    }
}

自定义序列化方式

Netty提供了自定义序列化方式的接口,用户可以根据自己的需求编写自己的序列化、反序列化的逻辑。

编写Encoder

我们需要编写一个实现了MessageToByteEncoder接口的编码器,将Java对象转换为二进制数据。

public class CustomEncoder extends MessageToByteEncoder<User> {

    @Override
    protected void encode(ChannelHandlerContext ctx, User msg, ByteBuf out) throws Exception {
        byte[] nameBytes = msg.getName().getBytes(Charset.forName("utf-8"));
        out.writeInt(nameBytes.length);
        out.writeBytes(nameBytes);
        out.writeInt(msg.getAge());
        out.writeInt(msg.getHobbies().size());
        for (String hobby : msg.getHobbies()) {
            byte[] hobbyBytes = hobby.getBytes(Charset.forName("utf-8"));
            out.writeInt(hobbyBytes.length);
            out.writeBytes(hobbyBytes);
        }
    }
}

编写Decoder

我们需要编写一个实现了ByteToMessageDecoder接口的解码器,将二进制数据还原为Java对象。

public class CustomDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int nameLength = in.readInt();
        byte[] nameBytes = new byte[nameLength];
        in.readBytes(nameBytes);
        String name = new String(nameBytes, Charset.forName("utf-8"));

        int age = in.readInt();

        int hobbyCount = in.readInt();
        List<String> hobbies = new ArrayList<>(hobbyCount);
        for (int i = 0; i < hobbyCount; i++) {
            int hobbyLength = in.readInt();
            byte[] hobbyBytes = new byte[hobbyLength];
            in.readBytes(hobbyBytes);
            hobbies.add(new String(hobbyBytes, Charset.forName("utf-8")));
        }

        out.add(new User(name, age, hobbies));
    }
}

使用自定义的序列化方式

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        // 使用我们上面编写的自定义Encoder和Decoder
        pipeline.addLast(new CustomEncoder());
        pipeline.addLast(new CustomDecoder());

        pipeline.addLast(new MyServerHandler());
    }
}

示例

以下是一个简单的使用自定义序列化方式的示例。

1. 客户端发送User对象

public class MyClient {

    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new MyClientInitializer());

            ChannelFuture f = b.connect("localhost", 8888).sync();
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}
public class MyClientHandler extends SimpleChannelInboundHandler<User> {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        List<String> hobbies = new ArrayList<>();
        hobbies.add("reading");
        hobbies.add("coding");
        ctx.writeAndFlush(new User("Alice", 18, hobbies));
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, User msg) throws Exception {
        // do nothing
    }
}
public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        // 使用自定义编码器和解码器
        pipeline.addLast(new CustomEncoder());
        pipeline.addLast(new CustomDecoder());

        pipeline.addLast(new MyClientHandler());
    }
}

2. 服务端接收User对象

public class MyServer {

    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new MyServerInitializer())
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture f = b.bind(8888).sync();
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}
public class MyServerHandler extends SimpleChannelInboundHandler<User> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, User msg) throws Exception {
        System.out.println("Received user: " + msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

总结

以上是Netty序列化深入理解与使用的完整攻略。本文讲解了Netty内置的序列化方式、自定义序列化方式的实现以及两个示例。希望能够对读者对Netty序列化有更深的理解和掌握。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Netty序列化深入理解与使用 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Java实现五子棋AI算法

    Java实现五子棋AI算法完整攻略 简介 五子棋是中国传统的一款棋类游戏,游戏规则简单易懂,但是能够考验玩家的智慧和战略。在实现五子棋AI算法的过程中,涉及到很多算法和技术,如极大极小值算法、启发式搜索、Alpha-Beta剪枝等等。下面将介绍如何使用Java实现五子棋AI算法。 实现过程 1. 棋盘的表示 首先需要定义棋盘的表示。一般使用二维数组来表示棋盘…

    Java 2023年5月19日
    00
  • JVM内置函数Intrinsics介绍

    关于“JVM内置函数Intrinsics介绍”的完整攻略,我会从以下几个方面进行讲解: Intrinsics是什么以及作用 Intrinsics的分类 Intrinsics的使用 示例说明 Intrinsics是什么以及作用 Intrinsics(内置函数)是一种Java虚拟机的内部实现机制。在编写Java代码时,我们有时会使用一些高性能的代码段,如数学运算…

    Java 2023年5月26日
    00
  • Java Apache POI报错“IOException”的原因与解决办法

    “IOException”是Java的Apache POI类库中的一个异常,通常由以下原因之一引起: 文件错误:如果文件无法读取或写入,则可能会出现此异常。例如,可能会尝试读取不存在的文件或尝试写入只读文件。 以下是两个实例: 例1 如果文件无法读取或写入,则可以尝试使用正确的文件路径以解决此问题。例如,在Java中,可以使用以下代码: FileInputS…

    Java 2023年5月5日
    00
  • 深入浅解正则表达式在Java中的使用

    深入浅解正则表达式在Java中的使用 正则表达式是用来处理文本的强大工具,它可以用来搜索、替换以及过滤文本。Java内置了对正则表达式的支持,可以使用Java中的regex包来使用正则表达式。 正则表达式的语法 正则表达式是一种模式匹配语言,它用简洁的语法来描述一定的字符模式。下面是一些常用的正则表达式语法规则: 字符组 字符组用来匹配多个字符中的任意一个。…

    Java 2023年5月23日
    00
  • java8 集合求差集、并集、交集的实例

    下面是关于“java8 集合求差集、并集、交集的实例”的完整攻略。 1. 集合的概念 在java中,集合就是对象的容器,允许我们将多个对象存储在一起,并提供了不同的方法来操作这些对象。集合又分为List、Set和Map三种,分别用于存储不同类型的数据。 2. 集合的求差集、并集、交集 在java中,我们可以使用Set接口提供的方法来求集合间的差集、并集和交集…

    Java 2023年5月19日
    00
  • Spring Security自定义认证器的实现代码

    下面是Spring Security自定义认证器的实现的完整攻略,包含了两个示例。 1. 自定义认证器简介 Spring Security是一个强大的安全框架,可以帮助我们实现各种安全功能。其中认证是Spring Security最基本的功能之一,它可以防止未经授权的用户访问受保护的资源,保护应用程序的安全。 Spring Security默认提供了基于用户…

    Java 2023年5月20日
    00
  • Mybatis-Plus批量插入用法详解

    Mybatis-Plus批量插入用法详解 什么是Mybatis-Plus? Mybatis-Plus 是一个 Mybatis 的增强工具,在 Mybatis 的基础上进行了简单的封装,使其用起来更加方便和简洁。它提供了一系列的增强功能,诸如自动化 CRUD 操作、分页、排序、关联查询等功能,可以大大提高开发效率和代码质量。 Mybatis-Plus批量插入的…

    Java 2023年5月20日
    00
  • SpringBoot中异常处理实战记录

    接下来我就详细讲解一下“SpringBoot中异常处理实战记录”的完整攻略。 一、背景介绍 在SpringBoot的开发中,异常处理是必不可少的环节。在程序的运行过程中,可能会遇到各种异常,如数据库连接异常、空指针异常、参数异常等,这些异常如果没有有效的处理,会导致系统运行出错,甚至直接崩溃。因此,合理的异常处理是保证系统稳定性和高效性的重要一环。 二、异常…

    Java 2023年5月27日
    00
合作推广
合作推广
分享本页
返回顶部