protobuf在netty中的使用
Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。并且支跨平台、跨语言,即支持目前绝大多数语言,例如 C++、C#、Java、python 等。
Protobuf的使用:Protobuf 是将类的定义使用.proto 文件进行描述。在idea 中编写 .proto 文件时,会自动提示是否下载 .ptotot 编写插件. 可以让语法高亮。然后通过 protoc.exe 编译器根据.proto 自动生成.java 文件,proto文件的举例如下:
syntax="proto3";
option optimize_for = SPEED;//加速解析
option java_package="com.mc_74120.codec2";//指定生成到哪个包下
option java_outer_classname="MyDataInfo";
//使用一个message 管理其他messa
message MyMessage{
//定义一个枚举类型
enum DataType {
StudentType=0;//从0开始
WorkerType=1;
}
//用data_type标识传的是哪个枚举类型
DataType data_type=1;
//表示每次枚举类型最多只能出现其中一个,节省空间
oneof dataBody{
Student student=2;
Worker worker=3;
}
option optimize_for = SPEED;//加速解析
option java_package="com.mc_74120.codec2";//指定生成到哪个包下
option java_outer_classname="MyDataInfo";
//使用一个message 管理其他messa
message MyMessage{
//定义一个枚举类型
enum DataType {
StudentType=0;//从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;
message Student{
int32 id=1;
string name=2;
}
message Worker{
string name=1;
int32 age=2;
}
}
}
使用示意图·如下:
客户端对.proto生成的java文件进行编码,将编码成的二进制字节码进行传输,服务端对二进制字节码进行解码,对解码对象进行使用。
使用实例:
客户端可以发送一个Student PoJo 对象到服务器 (通过 Protobuf 编码)
服务端能接收Student PoJo 对象,并显示信息(通过 Protobuf 解码)
服务端能接收Student PoJo 对象,并显示信息(通过 Protobuf 解码)
proto文件内容如下:
syntax="proto3";//版本
option java_outer_classname="StudentPOJO";//生成的java外部类名也是文件名称
//protobuf 使用message管理数据
message Student{//会在StudentPOJO外部类中生成一个内部类Student 真正发送的POJO对象
int32 id=1; //Student类中有一个属性 id 类型为int 1表示属性的序号
string name=2;
}
通过该proto文件生成java文件
Server端如下:
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
//创建BossGroup和WorkerGroup
//bossGroup处理连接请求 workerGroup处理业务
//底层都是无限循环
EventLoopGroup boosGroup = new NioEventLoopGroup();
EventLoopGroup workerGruop = new NioEventLoopGroup();
try{
//服务器端启动对象 配置参数
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boosGroup,workerGruop)//设置两个线程组
.channel(NioServerSocketChannel.class)//设置通道类型
.option(ChannelOption.SO_BACKLOG,128)//设置线程队列的连接个数
.childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动状态
.childHandler(new ChannelInitializer<SocketChannel>() {
//创建一个通道测试对象(匿名对象)
//给pipeline设置处理器
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//在pipeline中加入ProtobufDecoder
//指定解码的对象
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("decoder",new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
pipeline.addLast(new NettyServerHandler());
}
});
//给workerGroup的EventLoop对应的管道设置处理器
System.out.println("服务器准备好了");
//绑定端口并启动服务器
ChannelFuture channelFuture = serverBootstrap.bind(8848).sync();
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if(channelFuture.isSuccess())
System.out.println("成功");
else
System.out.println("失败");
}
});
//对通道关闭进行监听
channelFuture.channel().closeFuture().sync();
}
finally {
boosGroup.shutdownGracefully();
workerGruop.shutdownGracefully();
}
}
}
serverHandler如下:
package com.mc_74120.codec;
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;
import java.util.concurrent.TimeUnit;
//自定义的handler需要实现netty规定的某个适配器 自定义的handler才能称为handler
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//读取客户端发送的消息
/*
ChannelHandlerContext上下文对象 含有通道 管道 地址等
Object msg 客户端发送的数据
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//读取从客户端发送的Student对象
StudentPOJO.Student student= (StudentPOJO.Student) msg; ;
System.out.println("id="+student.getId());
}
//数据读取完毕
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//数据写入缓冲并刷新
//数据需要编码并发送
ctx.writeAndFlush(Unpooled.copiedBuffer("hello", CharsetUtil.UTF_8));
}
@Override
//处理异常 一般需要关闭通道
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.channel().close();
}
}
Client如下:
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
//客户端需要一个事件循环组
NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
try{
//创建客户端启动对象
Bootstrap bootstrap = new Bootstrap();
//设置相关参数
bootstrap.group(nioEventLoopGroup)
.channel(NioSocketChannel.class).
handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
//在pipelin中加入ProtoBuf编码器
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("encode", new ProtobufEncoder());
pipeline.addLast(new NettyClientHandler());
}
});
System.out.println("客户端准备好了");
//启动客户端去连接服务器端
ChannelFuture cf = bootstrap.connect("127.0.0.1", 8848).sync();
cf.channel().closeFuture().sync();
}finally {
nioEventLoopGroup.shutdownGracefully();
}
}
}
clientHandler如下:
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
//通道就绪触发该方法
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//发送一个student对象到服务器
StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(4).setName("74120").build();
ctx.writeAndFlush(student);
}
//当通道有读取事件时
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
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) throws Exception {
ctx.channel().close();
}
}

浙公网安备 33010602011771号