网络编程-编码与解码(Protobuf)

编码与解码

下面的文字都来自于极客时间
为什么要编解码呢?因为计算机数据传输的是二进制的字节数据
解码:字节数据 --> 字符串(字符数据)
编码:字符串(字符数据)–> 字节数据

我们在编写网络应用程序的时候需要注意 codec (编解码器),因为数据在网络中传输的都是二进制字节
码数据,而我们拿到的目标数据往往不是字节码数据。因此在发送数据时就 需要编码,接收数据时就需
要解码。
codec 的组成部分有两个:decoder(解码器)和 encoder(编码器)。

  • encoder 负责把业务数据转换成字节码数据
  • decoder 负责把字节码数据转换成业务数据
    其实 Java 的序列化技术就可以作为 codec 去使用,但是它的硬伤太多:
  1. 无法跨语言,这应该是 Java 序列化最致命的问题了
  2. 序列化后的体积太大,是二进制编码的 5 倍多
  3. 序列化性能太低

Netty 自身提供了一些 编解码器,如下:

  • StringEncoder对字符串数据进行编码
  • ObjectEncoder对 Java 对象进行编码

Netty 本身自带的 ObjectDecoder 和 ObjectEncoder 可以用来实现 POJO 对象或各种业务对象的编码和解码,但其内部使用的仍是 Java 序列化技术,所以在某些场景下不适用。对于 POJO 对象或各种业务对象要实现编码和解码,我们需要更高效更强的技术。由此引出:Google 的 Protobuf。

Google 的 Protobuf

Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,特点如下:支持跨平台、多语言(支持目前大多数语言,例如 C++、C#、Java、python 等)高性能,高可靠性。
使用 protobuf 编译器能自动生成代码,Protobuf 是将类的定义使用.proto 文件进行描述,然后通过 protoc.exe 编译器根据.proto 自动成.java 文件在使用 Netty 开发时,经常会结合 Protobuf 作为 codec (编解码器)去使用,具体用法如下所示。
使用步骤:

  1. 第一步:将传递数据的实体类生成【基于构建者模式设计】
  2. 第二步:配置编解码器
  3. 第三步:传递数据使用生成后的实体类

使用案例

1 引入依赖

<dependency>
   <groupId>com.google.protobuf</groupId>
   <artifactId>protobuf-java</artifactId>
   <version>3.21.11</version>
</dependency>

定义protoc文件,是生成具体类的描述:
语法规则:https://www.topgoer.com/%E5%BE%AE%E6%9C%8D%E5%8A%A1/Protobuf%E8%AF%AD%E6%B3%95.html

syntax = "proto3";
option java_outer_classname = "BookMessageDB";
message Book{
int32 id = 1;
string name = 2;
}

2 安装插件
在这里插入图片描述
GenProtobuf是生产插件。Protobuf是一定要安装的。
配置GenProtobuf:
在这里插入图片描述
做如下配置:
在这里插入图片描述

特别注意:这个版本要和你引入的依赖版本一致,否则会出先有些方法或类找不到报红的情况
在这里插入图片描述

public class AIOServer {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 解码
                        ch.pipeline().addLast("decoder", new ProtobufDecoder(BookMessage.Book.getDefaultInstance()));
                        ch.pipeline().addLast(new NettyServerHandler());
                    }
                });
        System.out.println("============服务器启动");
        b.bind(9999).sync();

//        bossGroup.shutdownGracefully();
//        workGroup.shutdownGracefully();
    }
}
public class AIOClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 设置编码器
                        ch.pipeline().addLast("encoder", new ProtobufEncoder());
                        ch.pipeline().addLast(new NettyClientHandler());
                    }
                });
        System.out.println();
        ChannelFuture connect = bootstrap.connect(new InetSocketAddress(9999));
        connect.channel().closeFuture().sync();
    }
}
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf=(ByteBuf) msg;
        System.out.println("client msg:"+buf.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        BookMessage.Book book =
                BookMessage.Book.newBuilder().setId(1).setName("beyound").build();
        ctx.writeAndFlush(book);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        BookMessage.Book book=(BookMessage.Book)msg;
        System.out.println("receive book msg:"+ book.getName());
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
       ctx.writeAndFlush(Unpooled.copiedBuffer("response", Charset.defaultCharset()));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}
  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
protobufProtocol Buffers)是一种轻量级的数据序列化格式,用于网络传输和存储数据。它提供了一种简单而高效的方法来将结构化数据编码为二进制格式,并在不同平台之间进行传输和解析。 在网络传输中使用protobuf,首先需要定义数据的结构和字段类型。这可以通过编写一个.proto文件来完成,其中定义了消息的字段和类型。然后,使用protobuf编译器将.proto文件编译成适合所使用语言的代码。 在发送端,将要发送的数据按照protobuf定义的结构进行编码,并将其作为二进制数据发送给接收端。接收端收到数据后,可以使用相同的protobuf定义结构进行解码,并还原为原始数据。 protobuf具有以下优点: 1. 高效性:protobuf使用二进制编码,相比于文本格式(如XML、JSON),它的编码解码速度更快,占用更小的网络带宽。 2. 可扩展性:protobuf支持向已定义消息中添加新字段,而不会破坏已有代码的兼容性。这使得系统能够逐步演化而不需要对所有组件进行修改。 3. 跨平台支持:由于protobuf是一种语言无关的格式,可用于多种编程语言,如C++、Java、Python等。 4. 易于使用:protobuf提供了简单易用的API,使得编码解码数据变得简单。 需要注意的是,虽然protobuf网络传输方面具有优势,但也需要进行版本管理,以确保发送端和接收端使用的是相同的.proto文件和相同版本的protobuf库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值