netty 4.1.45 protobuf codec to achieve

Note: For more related articles visit netty bloggers Column: netty column

PB protocol is simple to use

PB agreement on the use of the venue to introduce: Protocol Buffer 3 (Protobuf3) (the Java version) Getting started . Which describes how to use PB protocol.

netty protocol used to encode and decode PB

Request-response protocol defined

  • Request SubscribeReq.proto
syntax="proto3";
option java_package = "com.example.pojo";
option java_outer_classname = "SubscribeReqProto";
 
message SubscribeReq {
	int32 subReqId = 1;
	string userName = 2;
	string productName = 3;
	repeated string address = 4;
}
  • In response SubscribeResp.proto
syntax="proto2";
option java_package="com.example.pojo";
option java_outer_classname="SubscribeRespProto";
 
message SubscribeResp{
	required int32 subReqId = 1;
	required int32 respCode = 2;
	required string desc = 3;		
}

Use protoc --java_out {Java文件地址} {proto文件地址}compiled into Java files. Copy the Java file to the project directory. File code lot, the reader is to compile your own view.
Here Insert Picture Description

Server-side code written

Or transformation in the code base before.

The most important is to add PB agreement codecs, netty has been for us a good package.
Here Insert Picture Description

  • Semi packet processing ProtobufVarint32FrameDecoder
  • ProtobufDecoder only responsible for decoding, packet processing is not responsible for unpacking sticky, so before that you want to add ProtobufVarint32FrameDecoder
  • ProtobufEncoder: PB protocol encoder

It is then used SubscribeReqProto.SubscribeReqfor transmission of the message. Write code is quite simple, complete code is as follows:

package com.example;

import com.example.pojo.SubscribeReqProto;
import com.example.pojo.SubscribeRespProto;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * 自定义消息解码器
 */
public class MyNettyServerWithPB {
    public static void main(String[] args) {
        //配置服务端NIO线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workGroup)//配置主从线程组
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)//配置一些TCP的参数
                    .childHandler(new MyChildHandlerWithPB());//添加自定义的channel
            //绑定8080端口
            ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
            //服务端监听端口关闭
            ChannelFuture future = channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //netty优雅停机
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

class MyChildHandlerWithPB extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) {
        socketChannel.pipeline()
                .addLast(new ProtobufVarint32FrameDecoder())//用于半包处理
                //ProtobufDecoder解码器,参数是SubscribeReq,也就是需要接收到的消息解码为SubscribeReq类型的对象
                .addLast(new ProtobufDecoder(SubscribeReqProto.SubscribeReq.getDefaultInstance()))
                .addLast(new ProtobufVarint32LengthFieldPrepender())
                .addLast(new ProtobufEncoder())
                .addLast(new TimeWithPBServerHandler());
    }
}


/**
 * TimeServerHandler这个才是服务端真正处理请求的服务方法
 */
class TimeWithPBServerHandler extends ChannelInboundHandlerAdapter {

    private int count = 0;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        SubscribeReqProto.SubscribeReq req = (SubscribeReqProto.SubscribeReq) msg;

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String currentTimeStr = "TOM".equalsIgnoreCase(req.getUserName()) ?
                format.format(new Date()) + "" : "BAD ORDER";

        //受到一次请求,count加1
        System.out.println("第 " + count++ + " 次收到客户端请求:" + req.toString() + "  返回响应:" + currentTimeStr);

        SubscribeRespProto.SubscribeResp.Builder resp = SubscribeRespProto.SubscribeResp.newBuilder();
        resp.setRespCode(200)
                .setSubReqId(req.getSubReqId())
                .setDesc(currentTimeStr);

        ctx.write(resp);//将消息发送到发送缓冲区
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();//将消息从发送缓冲区中写入socketchannel中
    }

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

The client transformation

Similarly the client also want to configure the codec
Here Insert Picture Description

The complete code is as follows:

package com.example;

import com.example.pojo.SubscribeReqProto;
import com.example.pojo.SubscribeRespProto;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
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.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * 自定义消息解码器
 */
public class MyNettyClientWithPB {
    public static void main(String[] args) {
        //客户端NIO线程组
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ch.pipeline()
                                    .addLast(new ProtobufVarint32FrameDecoder())
                                    .addLast(new ProtobufDecoder(SubscribeRespProto.SubscribeResp.getDefaultInstance()))
                                    .addLast(new ProtobufVarint32LengthFieldPrepender())
                                    .addLast(new ProtobufEncoder())
                                    .addLast(new TimeWithPBClientHandler());

                        }
                    });

            //异步链接服务器
            ChannelFuture future = bootstrap.connect("127.0.0.1", 8080).sync();
            //等等客户端链接关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //优雅停机
            group.shutdownGracefully();
        }
    }
}


//客户端业务逻辑处理类
class TimeWithPBClientHandler extends ChannelInboundHandlerAdapter {
    private int count = 0;

    /**
     * 客户端与服务器TCP链路链接成功后调用该方法
     *
     * @param ctx
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        for (int i = 0; i < 100; i++) {
            List<String> list = Arrays.asList(new String[]{"杭州","北京"});
            SubscribeReqProto.SubscribeReq.Builder req = SubscribeReqProto.SubscribeReq.newBuilder();
            req.setSubReqId(i)
                    .setUserName("TOM")
                    .setProductName(UUID.randomUUID().toString())
                    .addAllAddress(list);

            ctx.writeAndFlush(req);//写入缓冲并且发送到socketchannel
        }
    }

    /**
     * 读取到服务端相应后执行该方法
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        SubscribeRespProto.SubscribeResp resp = (SubscribeRespProto.SubscribeResp) msg;
        System.out.println("第 " + count++ + " 次受到服务端返回:" + resp.getDesc());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        System.out.println("Unexpected exception from downstream : " + cause.getMessage());
        ctx.close();
    }
}

operation result

In turn starts the server and client, the results are as follows:
Here Insert Picture Description

Note: For more related articles visit netty bloggers Column: netty column

Published 233 original articles · won praise 211 · views 900 000 +

Guess you like

Origin blog.csdn.net/fgyibupi/article/details/104231927