Google的protoBuf的简单实用demo
1.首先编写.proto文件,定义传输数据内容。然后在命令行实用protoc.exe程序将该文件编译成.java文件(可以使用idea的插件辅助生成,更方便)
syntax = "proto3"; option optimize_for = SPEED; // 加快解析 option java_package = "me.jar.netty.codec"; // 指定生成到哪个包下 option java_outer_classname = "MyDataInfo"; // 外部类名 // protoBuf可以使用message管理其他的message message MyMessage { // 定义一个枚举类型 enum DataType { StudentType = 0; // 在proto3 要求enum的编号从0开始 WorkerType = 1; } // 用data_type标识传的是哪一个枚举类型 DataType data_type = 1; // 表示每次枚举类型最多只能出现其中的一个,节省空间 oneof dataBody { Student student = 2; Worker worker = 3; } } message Student { int32 id = 1; string name = 2; } message Worker { string name = 1; int32 age = 2; }
2.服务端主程序
package me.jar.netty.codec; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; 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; /** * @Description * @Date 2021/3/7-21:06 */ public class Server { public static void main(String[] args) { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // 创建服务端的启动对象,配置参数 ServerBootstrap bootstrap = new ServerBootstrap(); // 对启动对象进行设置 bootstrap.group(bossGroup, workerGroup) // 设置两个线程组 .channel(NioServerSocketChannel.class) // 使用NioServerSocketChannel作为服务器的通道实现 .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列等待连接个数 .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态 .childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道初始化对象(匿名对象) // 给pipeline设置处理器 @Override protected void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); // 在pipeline中加入ProtoBufDecoder,指定对哪种对象进行解码 // pipeline.addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance())); pipeline.addLast("decoder", new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance())); pipeline.addLast(new SimpleServerHandler()); } }); // 给workerGroup的EventLoop对应的管道设置处理器 // 绑定一个端口并且同步,生成一个ChannelFuture对象。启动服务器,并绑定端口 ChannelFuture cf = bootstrap.bind(6666).sync(); // Future-Listener机制。添加listener可以在事件完成时判断事件是否成功、是否取消等操作 cf.addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { System.out.println("监听端口6666成功"); } else { System.out.println("监听端口6666失败,原因:" + future.cause().getMessage()); } }); // 对关闭通道进行监听 cf.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally{ bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
3.服务端自定义Handler
package me.jar.netty.codec; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * @Description * @Date 2021/3/7-22:20 */ //public class SimpleServerHandler extends SimpleChannelInboundHandler<StudentPOJO.Student> { public class SimpleServerHandler extends SimpleChannelInboundHandler<MyDataInfo.MyMessage> { @Override protected void channelRead0(ChannelHandlerContext ctx, MyDataInfo.MyMessage msg) { // 根据dataType来显示不同的消息 MyDataInfo.MyMessage.DataType dataType = msg.getDataType(); if (dataType == MyDataInfo.MyMessage.DataType.StudentType) { MyDataInfo.Student student = msg.getStudent(); System.out.println("学生id=" + student.getId() + ",姓名=" + student.getName()); } else if (dataType == MyDataInfo.MyMessage.DataType.WorkerType) { MyDataInfo.Worker worker = msg.getWorker(); System.out.println("工人名字=" + worker.getName() + ",年龄=" + worker.getAge()); } else { System.out.println("传输的类型不正确"); } } // 处理异常,一般是需要关闭通道 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
4.客户端主程序
package me.jar.netty.codec; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; 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; /** * @Description * @Date 2021/3/7-21:03 */ public class Client { public static void main(String[] args) { // 客户端需要一个事件循环组 EventLoopGroup eventExecutors = new NioEventLoopGroup(); try { // 创建客户端启动对象 Bootstrap bootstrap = new Bootstrap(); // 设置相关参数 bootstrap.group(eventExecutors) // 设置线程组 .channel(NioSocketChannel.class) // 设置客户端通道的实现类(反射) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); // 在pipeline中加入ProtoBufEncoder pipeline.addLast("encoder", new ProtobufEncoder()); pipeline.addLast(new ClientHandler()); // 加入自己的处理器 } }); System.out.println("客户端准备完毕..."); // 启动客户端去连接服务器端 ChannelFuture cf = bootstrap.connect("127.0.0.1", 6666).sync(); // 对关闭通道进行监听 cf.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { eventExecutors.shutdownGracefully(); } } }
5.客户端自定义Handler
package me.jar.netty.codec; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.CharsetUtil; import java.util.Random; /** * @Description * @Date 2021/3/7-21:02 */ public class ClientHandler extends ChannelInboundHandlerAdapter { // 当通道就绪就会触发该方法 @Override public void channelActive(ChannelHandlerContext ctx) { // 发送一个Student对象到服务器 // StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(5).setName("汤姆吊炸天").build(); // ctx.writeAndFlush(student); // 随机发送Student 或 Worker 对象 int random = new Random().nextInt(3); MyDataInfo.MyMessage myMessage; if (random == 0) { // 发送Student对象 myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType). setStudent(MyDataInfo.Student.newBuilder().setId(3).setName("我是学生").build()).build(); } else { // 发送一个Worker对象 myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType) .setWorker(MyDataInfo.Worker.newBuilder().setAge(24).setName("我是工人").build()).build(); } ctx.writeAndFlush(myMessage); } // 当通道有读取事件时,就会触发该方法 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { ByteBuf byteBuf = (ByteBuf) msg; System.out.println("服务器回复的消息->" + byteBuf.toString(CharsetUtil.UTF_8)); System.out.println("服务器的地址->" + ctx.channel().remoteAddress()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }