netty using multi-protocol message protobuf

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/fu_huo_1993/article/details/88224992

    We use netty process, sometimes to efficient the transfer of data is often used protobuf transmission data, netty default for the codec protobuf we realized, but the case where the default only achieve a single object of the codec , but we use netty process, the object may have a variety of needs to be transmitted, then how to implement multi-protocol decode protobuf it ?

    There is a type of field is called in protobuf in  oneof , is oneof the statement is similar to field an optional field, at the same time there is only one field value, and they shared memory.

With the above basic knowledge, we have to implement a simple function.

demand:

       After connecting the client on the server side, every 1s is sent to the server object protobuf type ( such as log message, create a task message, delete the task telegram, etc. ), the server receives the object and print it out.

Write protobuf file:

       In protobuf file, we affirm a type of field enumeration used to identify the type of the current protobuf object of transmission, such as a login message, the message to create a task or something else, then  oneof field, and affirmed that all may need to pass message entity .

An introduction protobuf-java jar package

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

 

Second, the preparation of proto file


   note:

            1, is to define enumeration identifying the current transmission what type of message

            2, a plurality of messages needs to be sent into unified to be stated in oneof

            3, the time to give netty codec when he codec TaskProtocol objects

Third, the use protoc generates a corresponding command file java code according .proto

Create the four, netty server

/**
 * netty protobuf server
 *
 * @author huan.fu
 * @date 2019/2/15 - 11:54
 */
@Slf4j
public class NettyProtobufServer {

	public static void main(String[] args) throws InterruptedException {
		EventLoopGroup parentGroup = new NioEventLoopGroup(1);
		EventLoopGroup childGroup = new NioEventLoopGroup();
		ServerBootstrap bootstrap = new ServerBootstrap();
		bootstrap.group(parentGroup, childGroup)
				.channel(NioServerSocketChannel.class)
				.option(ChannelOption.SO_BACKLOG, 1024)
				// 连接超时
				.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
				.handler(new LoggingHandler(LogLevel.TRACE))
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch) {
						ch.pipeline()
								.addLast(new ProtobufVarint32FrameDecoder())
								.addLast(new ProtobufDecoder(TaskProtobufWrapper.TaskProtocol.getDefaultInstance()))
								.addLast(new ProtobufVarint32LengthFieldPrepender())
								.addLast(new ProtobufEncoder())
								.addLast(new ServerProtobufHandler());
					}
				});
		// 绑定端口,同步等待成功
		ChannelFuture future = bootstrap.bind(9090).sync();
		log.info("server start in port:[{}]", 9090);
		// 等待服务端链路关闭后,main线程退出
		future.channel().closeFuture().sync();
		// 关闭线程池资源
		parentGroup.shutdownGracefully();
		childGroup.shutdownGracefully();
	}
}

    note:

            1, pay attention to how netty is to use those codecs codec protobuf of.

Fifth, the server receives the client process sends a message over the

/**
 * 服务器端接收到客户端发送的请求,然后随机给客户端返回一个对象
 *
 * @author huan.fu
 * @date 2019/2/15 - 14:26
 */
@Slf4j
public class ServerProtobufHandler extends SimpleChannelInboundHandler<TaskProtobufWrapper.TaskProtocol> {

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, TaskProtobufWrapper.TaskProtocol taskProtocol) {
		switch (taskProtocol.getPackType()) {
			case LOGIN:
				log.info("接收到一个登录类型的pack:[{}]", taskProtocol.getLoginPack().getUsername() + " : " + taskProtocol.getLoginPack().getPassword());
				break;
			case CREATE_TASK:
				log.info("接收到一个创建任务类型的pack:[{}]", taskProtocol.getCreateTaskPack().getTaskId() + " : " + taskProtocol.getCreateTaskPack().getTaskName());
				break;
			case DELETE_TASK:
				log.info("接收到一个删除任务类型的pack:[{}]", Arrays.toString(taskProtocol.getDeleteTaskPack().getTaskIdList().toArray()));
				break;
			default:
				log.error("接收到一个未知类型的pack:[{}]", taskProtocol.getPackType());
				break;
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		ctx.close();
		log.error("发生异常", cause);
	}
}

    note:

            1, the server side determines according packType field sent by the client what type of message

Write six, netty client

/**
 * netty protobuf client
 *
 * @author huan.fu
 * @date 2019/2/15 - 11:54
 */
@Slf4j
public class NettyProtobufClient {

	public static void main(String[] args) throws InterruptedException {
		EventLoopGroup group = new NioEventLoopGroup();
		Bootstrap bootstrap = new Bootstrap();
		bootstrap.group(group)
				.channel(NioSocketChannel.class)
				.option(ChannelOption.TCP_NODELAY, true)
				.handler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch) {
						ch.pipeline()
								.addLast(new ProtobufVarint32FrameDecoder())
								.addLast(new ProtobufDecoder(TaskProtobufWrapper.TaskProtocol.getDefaultInstance()))
								.addLast(new ProtobufVarint32LengthFieldPrepender())
								.addLast(new ProtobufEncoder())
								.addLast(new ClientProtobufHandler());
					}
				});
		ChannelFuture future = bootstrap.connect("127.0.0.1", 9090).sync();
		log.info("client connect server.");
		future.channel().closeFuture().sync();
		group.shutdownGracefully();
	}
}

 

Seven, the client connects to the server-side processing

/**
 * 客户端连接到服务器端后,每隔1s发送一个报文到服务器端
 *
 * @author huan.fu
 * @date 2019/2/15 - 14:26
 */
@Slf4j
public class ClientProtobufHandler extends ChannelInboundHandlerAdapter {

	private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

	private AtomicInteger atomicInteger = new AtomicInteger(1);

	@Override
	public void channelActive(ChannelHandlerContext ctx) {
		executor.scheduleAtFixedRate(() -> {
			// 产生的pack类型
			int packType = new Random().nextInt(3);
			switch (TaskProtobufWrapper.PackType.forNumber(packType)) {
				case LOGIN:
					TaskProtobufWrapper.LoginPack loginPack = TaskProtobufWrapper.LoginPack.newBuilder().setUsername("张三[" + atomicInteger.getAndIncrement() + "]").setPassword("123456").build();
					ctx.writeAndFlush(TaskProtobufWrapper.TaskProtocol.newBuilder().setPackType(TaskProtobufWrapper.PackType.LOGIN).setLoginPack(loginPack).build());
					break;
				case CREATE_TASK:
					TaskProtobufWrapper.CreateTaskPack createTaskPack = TaskProtobufWrapper.CreateTaskPack.newBuilder().setCreateTime(System.currentTimeMillis()).setTaskId("100" + atomicInteger.get()).setTaskName("任务编号" + atomicInteger.get()).build();
					ctx.writeAndFlush(TaskProtobufWrapper.TaskProtocol.newBuilder().setPackType(TaskProtobufWrapper.PackType.CREATE_TASK).setCreateTaskPack(createTaskPack).build());
					break;
				case DELETE_TASK:
					TaskProtobufWrapper.DeleteTaskPack deleteTaskPack = TaskProtobufWrapper.DeleteTaskPack.newBuilder().addTaskId("1001").addTaskId("1002").build();
					ctx.writeAndFlush(TaskProtobufWrapper.TaskProtocol.newBuilder().setPackType(TaskProtobufWrapper.PackType.DELETE_TASK).setDeleteTaskPack(deleteTaskPack).build());
					break;
				default:
					log.error("产生一个未知的包类型:[{}]", packType);
					break;
			}
		}, 0, 1, TimeUnit.SECONDS);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		ctx.close();
		log.error("发生异常", cause);
	}
}

    note:

           1, every 1s client sending different messages to the server when the server is connected

Eight, operating results


 Nine, complete code

完成代码如下https://gitee.com/huan1993/netty-study/tree/master/src/main/java/com/huan/netty/protobuf

Guess you like

Origin blog.csdn.net/fu_huo_1993/article/details/88224992