netty 客户端使用protobuf 与netty服务端 通信

参考文档:Language Guide (proto3)  |  Protocol Buffers  |  Google Developers

参考视频:尚硅谷Netty视频教程(B站超火,好评如潮)_哔哩哔哩_bilibili

protobuf 的优点:

1.传输数据的体积小

2.解析速度快

3.可以在多种语言中使用

4.可以使用protoc.exe 自动生成相应的类,用于构造对象,使用protobuf传输

idea 配置proto 插件

file→settings→Plugins,输入 protobuf ,点击安装,插件可以进行proto 再文件编辑的过程中代码关键字高亮提示

下载protoc.exe https://repo1.maven.org/maven2/com/google/protobuf/protoc/3.11.1/protoc-3.11.1-windows-x86_64.exe

 添加maven依赖

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.74.Final</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.6.1</version>
        </dependency>

1.创建netty服务器,需要再pipeline中添加 ProtobufDecoder ,用于解码protobuf 协议传送过来的对象

package netty.proto;

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 {

    public static void main(String[] args) throws InterruptedException {
        // 设置bossGroup 线程组
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        // 设置workerGroup 线程组
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();

        try {
            serverBootstrap.group(bossGroup,workerGroup);
            serverBootstrap.option(ChannelOption.SO_BACKLOG,128)// 设置等待的连接数
                    .childOption(ChannelOption.SO_KEEPALIVE,true)// 设置保持长连接
                    .channel(NioServerSocketChannel.class) // 设置服务器通道的类型实现
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new ProtobufDecoder(TestPOJO.data.getDefaultInstance()));// 构造器传入解析的对象的类型,这个类型是 Student 类或者 Teacher 类
                            ch.pipeline().addLast(new ServerChannelHandler());
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(8888).sync();//绑定端口,并且同步
            // 对关闭通道事件进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

}
package netty.proto;

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

/**
 * 服务器端处理业务逻辑
 */
public class ServerChannelHandler extends SimpleChannelInboundHandler<TestPOJO.data> {


    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // 接收完成后,将消息发给客户端
        //ctx.channel().writeAndFlush(Unpooled.wrappedBuffer("hello client ".getBytes()));

    }

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


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TestPOJO.data msg) throws Exception {
        // 接收客户端发送给过来的信息
        if(msg.getDt() == TestPOJO.data.dataType.Student){
            TestPOJO.Student s = msg.getS();
            System.out.println("接收到了 "+ s.getName() + " "+ s.getId());
        }
    }
}

1.1 编写proto 文件,新建一个 TestPOJO.proto,data 这个类可以代表 要么 Student类型,要么代表 Teacher 类型,可以根据 dataType 来进行区分,服务端再接收的时候,可以根据datatype 来区分。

syntax = "proto3";// 使用proto3协议
option java_package = "netty.proto"; // 生成Java类的路径
option java_outer_classname = "TestPOJO"; // java类的名称

message data{
    enum dataType { // 枚举类,可以定义 两个类型,教师,学生
        Student = 0; // 0 表示第一个枚举类型
        Teacher = 1;
    }
    dataType dt = 1; // 表示 message data 的第一个属性 是枚举类型的
    oneof data{ // oneof 关键字表示 学生/老师只能选择一个类型
        Student s = 2;
        Teacher t = 3;
    }

}

message Student{
    int32 id = 1;  // 学生类的第一个属性是 id
    string name = 2; // 学生类的第二个属性是 name
}

message Teacher{
    int32 id = 1; // 老师类的第一个属性是id
    string name = 2; // 老师类的第二个属性是 name
    int32 class = 3; // 老师类的第三个属性是 班级
}

 .proto 文件中 int32 和Java中int类型对应,string 和 Java中的String 对应,具体对应关系如下

.proto TypeNotesC++ TypeJava/Kotlin Type[1]Python Type[3]Go TypeRuby TypeC# TypePHP TypeDart Type
doubledoubledoublefloatfloat64Floatdoublefloatdouble
floatfloatfloatfloatfloat32Floatfloatfloatdouble
int32Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.int32intintint32Fixnum or Bignum (as required)intintegerint
int64Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.int64longint/long[4]int64Bignumlonginteger/string[6]Int64
uint32Uses variable-length encoding.uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
uint64Uses variable-length encoding.uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sint32Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.int32intintint32Fixnum or Bignum (as required)intintegerint
sint64Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.int64longint/long[4]int64Bignumlonginteger/string[6]Int64
fixed32Always four bytes. More efficient than uint32 if values are often greater than 228.uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
fixed64Always eight bytes. More efficient than uint64 if values are often greater than 256.uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sfixed32Always four bytes.int32intintint32Fixnum or Bignum (as required)intintegerint
sfixed64Always eight bytes.int64longint/long[4]int64Bignumlonginteger/string[6]Int64
boolboolbooleanboolboolTrueClass/FalseClassboolbooleanbool
stringA string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot be longer than 232.stringStringstr/unicode[5]stringString (UTF-8)stringstringString
bytesMay contain any arbitrary sequence of bytes no longer than 232.stringByteStringstr (Python 2)
bytes (Python 3)
[]byteString (ASCII-8BIT)ByteStringstringList

1.2 编译proto 文件生成对应的Java代码

进入protoc-3.11.1-windows-x86_64.exe 所在的目录,将TestPOJO.proto 文件拷贝到该目录下面,将它编译为Java代码

protoc-3.11.1-windows-x86_64.exe --java_out=. TestProto.proto

2.创建netty 客户端,添加ProtobufEncoder 编码器

package netty.proto;

import io.netty.bootstrap.Bootstrap;
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.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufEncoder;

public class NettyClient {

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

        NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.group(eventExecutors);
            bootstrap.channel(NioSocketChannel.class)
                    .option(ChannelOption.SO_KEEPALIVE,true)
                    .handler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new ProtobufEncoder());
                            ch.pipeline().addLast(new ClientChannelHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8888).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {

            eventExecutors.shutdownGracefully();

        }


    }
}
package netty.proto;

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;

public class ClientChannelHandler extends ChannelInboundHandlerAdapter {


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("当前时间是 "+System.currentTimeMillis());

        //ctx.channel().writeAndFlush(Unpooled.wrappedBuffer("hello ,server".getBytes()));
        // 发送一个学生类型的数据
        TestPOJO.data data = TestPOJO.data.newBuilder().setDtValue(0).setS(TestPOJO.Student.newBuilder().setId(1).setName("king").build()).build();
        ctx.channel().writeAndFlush(data);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf)msg;
        System.out.println("接收到服务器 的 消息 "+(((ByteBuf) msg).toString(CharsetUtil.UTF_8)));
        System.out.println("当前时间是 "+System.currentTimeMillis());


    }
}

 最后一步,进行测试,先启动服务器,再启动客户端,服务器收到客户端发送过来的消息即成功。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值