Netty二、Netty编解码

1. 编解码技术

1.1 netty自身提供的编解码器

  1. StringDecoder StringEncoder 字符串解码编码器
  2. ObjectDecoder ObjectEncoder 对象解码编码器
  1. 底层都基于 JAVA 序列化,从 JDK 1.1 版本就提供,只需实现 java.io.Serializable并生成序列ID即可。
  2. JAVA序列化的目的,网络传输及持久化。
  3. Java序列化从JDK 1.1版本就己经提供,它不需要添加额外的类库,只需实现 java.io.Serializable并生成序列ID即可,但是在远程服务调用(RPC)时,很少宜接使用Java序列化进行消息的编解码和传输, 这又是什么原因呢?
  1. 无法跨语言,编码后其他语言无法正常解码
  2. 编码后的码流太大
  3. 性能差

1.2 业界主流的编解码框架

  1. MessagePack 编解码

MessagePack是一个高效的二进制序列化框架,它像JSON 一样支持不同语言间的数 据交换,但是它的性能更快,序列化之后的码流也更小。

  • 编解码高效,性能高;

  • 序列化之后的码流小;

  • 支持跨语言。

  1. Google Protobuf 编解码

Google的Protobuf在业界非常流行,很多商业项目选择Protobuf作为编解码框架。

  • 在谷歌内部长期使用,产品成熟度髙:

  • 跨语言、支持多种语言,包括C++、Java和Python:

  • 编码后的消息更小,更加冇利于存储和传输:

  • 编解码的性能非常高:

  • 支持不同协议版本的前向兼容:

  • 支持定义可选和必选字段。

  1. JBoss Marshalling 编解码

JBoss Marshalling是一个Java对象序列化包,对JDK默认的序列化框架进行了优化, 但又保持跟java.io.Serializablc接口的兼容,同时増加了 -些可调的参数和附加的特性, 这些参数和特性可通过工厂类进行配置。

2. Google Protobuf 编解码

Netty二、Netty编解码_第1张图片

客户端产生业务数据,经过 ProtobufEncoder 编码器编码,客户端接受后通过 ProtobufDecoder 解码。

2.1 编写.proto文件

//版本
syntax = "proto3";
// 生成的外部类型名称
option java_outer_classname = "StudentPOJO";
// protobuf 使用 message 管理数据
message Student{  // 会在 StudentPOJO 外部类生成内部类, 真正发送的 pojo 对象
    // 1 标识属性序号,不代表值
    int32 id = 1;
    string name = 2;
}
// https://developers.google.com/protocol-buffers/docs/proto  文档

google protobuf 会根据此文件生成对应的 java类,此内部类对相应 message 定义的内容。

StudentPOJO 需要通过 protoc.exe 生成,protoc.exe --java_out=. Student.proto

public final class StudentPOJO {
    private StudentPOJO() {
    }

    public static void registerAllExtensions(
            com.google.protobuf.ExtensionRegistryLite registry) {
    }

    public static void registerAllExtensions(
            com.google.protobuf.ExtensionRegistry registry) {
        registerAllExtensions(
                (com.google.protobuf.ExtensionRegistryLite) registry);
    }

    public interface StudentOrBuilder extends
            // @@protoc_insertion_point(interface_extends:Student)
            com.google.protobuf.MessageOrBuilder {

        /**
         * 
         * 1 标识属性序号,不代表值
         * 
* * int32 id = 1; */
int getId(); /** * string name = 2; */ java.lang.String getName(); /** * string name = 2; */ com.google.protobuf.ByteString getNameBytes(); } public static final class Student extends com.google.protobuf.GeneratedMessageV3 implements StudentOrBuilder { private static final long serialVersionUID = 0L; // Use Student.newBuilder() to construct. private Student(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { super(builder); } private Student() { id_ = 0; name_ = ""; } ... } }

2.2 Netty 客户端服务端

2.2.1 服务端及处理器

package cn.painnote.netty.codec.protocolBuf;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;

public class NettyServer {


    /**
     * Description netty 服务端
     *
     * @author Qian YuHua
     */
    public static void main(String[] args) throws Exception {

        // Boss group 处理链接请求 NioEventLoopGroup 默认构造函数线程数为 0,CPU * 2
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        // Worker group 处理数据客户端业务
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 服务器端启动对象
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 设置线程组
            bootstrap.group(bossGroup, workerGroup)
                    // 使用  NioServerSocketChannel 作为服务器通道实现类
                    .channel(NioServerSocketChannel.class)
                    /*
                     * ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数,
                     * 函数listen(int socketfd,int backlog)用来初始化服务端可连接队列,
                     * 服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,
                     * 多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,
                     * backlog参数指定了队列的大小
                     */
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childHandler(new ChannelInitializer<SocketChannel>() {//创建一个通道初始化对象(匿名对象)
                        //给pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // 制定对哪种对象进行解码
                            ch.pipeline().addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    }); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器

            System.out.println(".....server is ready...");

            //绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
            //启动服务器(并绑定端口)
            ChannelFuture channelFuture = bootstrap.bind(6666).sync();
            //对关闭通道进行监听
            channelFuture.channel().closeFuture().sync();

        } catch (Exception e) {
            System.out.println(e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}
package cn.painnote.netty.codec.protocolBuf;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.util.concurrent.TimeUnit;

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        StudentPOJO.Student student = (StudentPOJO.Student) msg;
        System.out.println("客户端发送消息是:" + " id " + student.getId() + " name " + student.getName());
    }
}

2.2.1 客户端及处理器

package cn.painnote.netty.codec.protocolBuf;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufEncoder;

public class NettyClient {


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

        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventExecutors)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 加入 protobufEncoder
                        ch.pipeline().addLast("encoder", new ProtobufEncoder());
                        ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器
                    }
                });

        System.out.println("client ok..");

        ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();

        //给关闭通道进行监听
        channelFuture.channel().closeFuture().sync();
    }
}
package cn.painnote.netty.codec.protocolBuf;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 *   
 *  

Title:

*

Description: Function Description

* @author qyh */
public class NettyClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("client " + ctx); // 发送 Student 到服务器 StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(111).setName("钱育华").build(); ctx.writeAndFlush(student); } }

3.xmind

Netty二、Netty编解码_第2张图片

你可能感兴趣的:(java,网络通信,netty)