maven 中心找到protobuf坐标 https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.19.2</version>
</dependency>
编写proto文件
message People{
enum DataType{
BoyType = 0;//proto3枚举enum编号从0开始
GirlType = 1;
}
//用data_type来识别哪个枚举类型(DataType是People的第一个属性,属性名是data_type)
DataType data_type = 1;
//每次枚举类型最多只能出现其中一个,节省空间
oneof dataBody{
Boy boy = 2;
Girl girl = 3;
}
}
//protobuf 使用message 管理数据
message Boy{//会在PeoplePOJO外部类生成一个内部类People,他是真正发送的POJO对象
int32 id = 1;//People类中有一个属性名为id类型为int32(protobuf类型) 1表示属性序号,不是值
string name = 2;
}
message Girl{//会在PeoplePOJO外部类生成一个内部类People,他是真正发送的POJO对象
int32 id = 1;//People类中有一个属性名为id类型为int32(protobuf类型) 1表示属性序号,不是值
string name = 2;
}
怎么使用proto
客户端
public class Client {
public static void main(String[] args) throws InterruptedException {
//客户端只需要一个事件循环组
EventLoopGroup group = new NioEventLoopGroup();
try {
//创建客户端启动对象,配置参数
Bootstrap bootstrap = new Bootstrap();
//用链式编程进行设置
bootstrap.group(group)//设置事件循环组
.channel(NioSocketChannel.class)//设置客户端通道实现
.handler(new ChannelInitializer<SocketChannel>() {//创建通道测试对象(匿名对象)
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline()
.addLast("encoder", new ProtobufEncoder())
.addLast(new ClientHandler());//向管道最后添加处理器
}
});//给workGroup的EventLoop对应的管道设置处理器
//System.out.println(LocalDateTime.now() + "客户端准备好了");
//启动客户端,绑定端口,连接服务端
ChannelFuture future = bootstrap.connect("127.0.0.1", 888).sync();
//对关闭通道进行监听(非阻塞监听,异步模型)
future.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
客户端handler类
public class ClientHandler extends ChannelInboundHandlerAdapter {
//当通道有读事件,会触发
//ChannelHandlerContext上下文,含有管道pipeline与通道channel
//Object msg 通道数据
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//将msg转成ByteBuf
ByteBuf buf = (ByteBuf) msg;
System.out.println(buf.toString(CharsetUtil.UTF_8));
}
//当通道就绪触发该方法
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
int random = new Random().nextInt(3);
PeoplePOJO.People people = null;
if (random == 0) {
people = PeoplePOJO.People.newBuilder().setDataType(PeoplePOJO.People.DataType.BoyType).setBoy(PeoplePOJO.Boy.newBuilder().setId(10000).setName("男").build()).build();
}else {
people = PeoplePOJO.People.newBuilder().setDataType(PeoplePOJO.People.DataType.GirlType).setGirl(PeoplePOJO.Girl.newBuilder().setId(10001).setName("女").build()).build();
}
//将数据写入缓冲区并刷新 write+flush
//对发送数据编码
ctx.writeAndFlush(people);
}
//处理异常,关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
服务端
public class Server {
public static void main(String[] args) throws InterruptedException {
//创建两个事件循环组,bossGroup只处理连接请求,workGroup处理客户端业务处理,交给bossGroup
//两个都是无线循环
//默认CPU核*2
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
//创建服务端启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap();
//用链式编程进行设置
bootstrap.group(bossGroup, workGroup)//设置两个线程组
.channel(NioServerSocketChannel.class)//使用NioSocketChannel作为服务器通道实现
.option(ChannelOption.SO_BACKLOG, 128)//设置线程队列个数
.childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持活动连接状态
.childHandler(new ChannelInitializer<SocketChannel>() {//创建通道测试对象(匿名对象)
//给pipeline设置处理器
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline()
.addLast("decoder", new ProtobufDecoder(PeoplePOJO.People.getDefaultInstance()))
.addLast(new ServerHandler());//向管道最后添加处理器
}
});//给workGroup的EventLoop对应的管道设置处理器
//启动服务器,绑定端口,生成ChannelFuture对象
ChannelFuture future = bootstrap.bind(888).sync();
//对关闭通道进行监听(非阻塞监听,异步模型)
future.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
服务端handler类
//public class ServerHandler extends ChannelInboundHandlerAdapter {
public class ServerHandler extends SimpleChannelInboundHandler<PeoplePOJO.People> {
//当通道有读事件,会触发
//ChannelHandlerContext上下文,含有管道pipeline与通道channel
//Object msg 通道数据
// @Override
// public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// //读取从客户端发送的Student.proto
// StudentPOJO.Student student = (StudentPOJO.Student) msg;
// System.out.println("客户端: " + student.getId() + student.getName());
// }
@Override
protected void channelRead0(ChannelHandlerContext ctx, PeoplePOJO.People msg) throws Exception {
PeoplePOJO.People.DataType dataType = msg.getDataType();
if (dataType == PeoplePOJO.People.DataType.BoyType) {
System.out.println("客户端: " + msg.getBoy().getName());
} else {
System.out.println("客户端: " + msg.getGirl().getName());
}
}
//数据读取完毕
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将数据写入缓冲区并刷新 write+flush
//对发送数据编码
ctx.writeAndFlush(Unpooled.copiedBuffer("服务端:您好", CharsetUtil.UTF_8));
}
//处理异常,关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}