netty已经实现了对Google protobuf序列化协议的编解码:
gitee:https://gitee.com/hsjjsh123/itcast_netty/tree/master/code/my_netty_study/Netty-protobuf
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline ph = ch.pipeline();
//传输的协议 Protobuf
//解码
ph.addLast(new ProtobufVarint32FrameDecoder());
ph.addLast(new ProtobufDecoder(MutipleMessageInfo.Person.getDefaultInstance()));
//编码
ph.addLast(new ProtobufVarint32LengthFieldPrepender());
ph.addLast(new ProtobufEncoder());
//业务逻辑实现类handler
}
那么,我们怎么实现多个protobuf的message实例呢?
protobuf有一个oneof,类似c里的联合体,节省内存。
proto文件定义:
syntax = "proto3";
//包路径,自定义,比package优先级高
//如果java_package存在则默认使用java_package的配置,如果使用java_package就必须要指定package
//以免在生成java以外的语言时,不存在java_package参数,造成命名秘密空间问题
option java_package = "com.pancm.protobuf";
//生成的外部类的名称,自定义 如果不定义则默认使用文件名的驼峰命名法转换的名称作为外部类名称
option java_outer_classname = "MutipleMessageInfo";
message Person{
enum DataType{
StudentType = 0;
TeacherType = 1;
LeaderType = 2;
}
DataType data_type = 1;
oneof DataList{
Student student = 2;
Teacher teacher = 3;
Leader leader = 4;
}
}
message Student{
int32 id = 1;
int32 stu_id = 2;
string stu_name = 3;
}
message Teacher{
int32 id = 1;
int32 t_id = 2;
string t_name = 3;
}
message Leader{
int32 id = 1;
int32 l_id = 2;
string l_name = 3;
}
proto转java类:
protoc.exe --java_out=./exe_out MutipleMessage.proto
使用:
常量:
package com.pancm.protocol;
/**
* @author HeShengjin [email protected]
* @Description 用户的常量
* @createTime 2021年07月29日 10:20:00
*/
public interface Constant {
int FROM_STUDENT_ID = 123456789;
String FROM_STUDENT_NAME = "学生";
int TO_TEACHER_ID = 987654321;
String TO_TEACHER_NAME = "老师";
}
简单的消息id:
package com.pancm.protocol;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class SequenceIdGenerator {
private static final AtomicInteger id = new AtomicInteger(0);
public static int nextId() {
return id.incrementAndGet();
}
}
客户端handler:ProtobufOneOfClientHandler.java,连接上立刻发送消息学生类型message给服务端:
package com.pancm.handler;/**
* @author HeShengjin [email protected]
* @Description TODO
* @createTime 2021年07月29日 17:39:00
*/
import com.pancm.protobuf.MutipleMessageInfo;
import com.pancm.protocol.Constant;
import com.pancm.protocol.SequenceIdGenerator;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
/**
* @author hsj
* @description:多个protobuf的类handler
* @date 2021/7/29 17:39
*/
@ChannelHandler.Sharable
@Slf4j
public class ProtobufOneOfClientHandler extends SimpleChannelInboundHandler<MutipleMessageInfo.Person> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, MutipleMessageInfo.Person msg) throws Exception {
MutipleMessageInfo.Person.DataType dataType = msg.getDataType();
switch (dataType) {
case StudentType:
MutipleMessageInfo.Student sutdent = msg.getStudent();
log.info("Student类型:{}",sutdent.getAllFields());
break;
case TeacherType:
MutipleMessageInfo.Teacher teacher = msg.getTeacher();
log.info("Teacher类型:{}",teacher.getAllFields());
break;
case LeaderType:
MutipleMessageInfo.Leader leader = msg.getLeader();
log.info("Leader类型:{}",leader.getAllFields());
break;
default:
log.error("未找到protobuf类型");
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
MutipleMessageInfo.Person person = getPerson4Student();
//连接上发送消息学生
ctx.channel().writeAndFlush(person);
}
private MutipleMessageInfo.Person getPerson4Student() {
MutipleMessageInfo.Person person = MutipleMessageInfo.Person.newBuilder()
.setDataType(MutipleMessageInfo.Person.DataType.StudentType)
.setStudent(
MutipleMessageInfo.Student.newBuilder()
.setId(SequenceIdGenerator.nextId())
.setStuId(Constant.FROM_STUDENT_ID)
.setStuName(Constant.FROM_STUDENT_NAME)
.build()
)
.build();
return person;
}
}
package com.pancm.client;
import com.pancm.handler.ProtobufOneOfClientHandler;
import com.pancm.protobuf.MutipleMessageInfo;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
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;
/**
* ◎编码过程: 通过ProtobufVarint32LengthFieldPrepender 将整个消息体的长度作为
* 消息头附加在消息体,然后使用ProtobufEncoder进行编码,编码完成后将编码
* 结果字节数组通过Netty进行传输。
*
* ◎解码过程: 接受消息的时候,ProtobufVarint32FrameDecoder 先接受消息头,获取
* 消息体的字节数组长度,直到获取到等于消息字节数组长度的字节数,才使用
* ProtobufDecoder进行解码操作。
*
* @Title: NettyClientFilter
* @Description: Netty客户端 过滤器
* @Version:1.0.0
* @author pancm
* @author hsj
* @date 2017年10月8日
*/
public class NettyClientFilter extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline ph = ch.pipeline();
//传输的协议 Protobuf
//解码
ph.addLast(new ProtobufVarint32FrameDecoder());
ph.addLast(new ProtobufDecoder(MutipleMessageInfo.Person.getDefaultInstance()));
//编码
ph.addLast(new ProtobufVarint32LengthFieldPrepender());
ph.addLast(new ProtobufEncoder());
//业务逻辑实现类
ph.addLast(new ProtobufOneOfClientHandler());
}
}
服务端handler:ProtobufOneOfServerHandler.java,接受到消息学生,立刻回复老师:
package com.pancm.handler;
import com.pancm.protobuf.MutipleMessageInfo;
import com.pancm.protocol.Constant;
import com.pancm.protocol.SequenceIdGenerator;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
/**
* @author hsj
* @description:多个protobuf的类handler
* @date 2021/7/29 17:27
*/
@ChannelHandler.Sharable
@Slf4j
public class ProtobufOneOfServerHandler extends SimpleChannelInboundHandler<MutipleMessageInfo.Person> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, MutipleMessageInfo.Person msg) throws Exception {
MutipleMessageInfo.Person.DataType dataType = msg.getDataType();
switch (dataType) {
case StudentType:
MutipleMessageInfo.Student sutdent = msg.getStudent();
log.info("Student类型:{}",sutdent.getAllFields());
MutipleMessageInfo.Person person = getPerson4Teacher();
//发送消息老师
ctx.channel().writeAndFlush(person);
break;
case TeacherType:
MutipleMessageInfo.Teacher teacher = msg.getTeacher();
log.info("Teacher类型:{}",teacher.getAllFields());
break;
case LeaderType:
MutipleMessageInfo.Leader leader = msg.getLeader();
log.info("Leader类型:{}",leader.getAllFields());
break;
default:
log.error("未找到protobuf类型");
}
}
private MutipleMessageInfo.Person getPerson4Teacher() {
MutipleMessageInfo.Person person = MutipleMessageInfo.Person.newBuilder()
.setDataType(MutipleMessageInfo.Person.DataType.TeacherType)
.setTeacher(
MutipleMessageInfo.Teacher.newBuilder()
.setId(SequenceIdGenerator.nextId())
.setTId(Constant.TO_TEACHER_ID)
.setTName(Constant.TO_TEACHER_NAME)
.build()
)
.build();
return person;
}
}
package com.pancm.server;
import com.pancm.handler.ProtobufOneOfServerHandler;
import com.pancm.protobuf.MutipleMessageInfo;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
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;
/**
* ◎编码过程: 通过ProtobufVarint32LengthFieldPrepender 将整个消息体的长度作为
* 消息头附加在消息体,然后使用ProtobufEncoder进行编码,编码完成后将编码
* 结果字节数组通过Netty进行传输。
*
* ◎解码过程: 接受消息的时候,ProtobufVarint32FrameDecoder 先接受消息头,获取
* 消息体的字节数组长度,直到获取到等于消息字节数组长度的字节数,才使用
* ProtobufDecoder进行解码操作。
*
* @Title: HelloServerInitializer
* @Description: Netty 服务端过滤器
* @Version:1.0.0
* @author pancm
* @date 2017年10月8日
*/
public class NettyServerFilter extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline ph = ch.pipeline();
// 解码和编码,应和客户端一致
// 传输的协议 Protobuf
// 解码
ph.addLast(new ProtobufVarint32FrameDecoder());
ph.addLast(new ProtobufDecoder(MutipleMessageInfo.Person.getDefaultInstance()));
// 编码
ph.addLast(new ProtobufVarint32LengthFieldPrepender());
ph.addLast(new ProtobufEncoder());
//业务逻辑实现类
ph.addLast(new ProtobufOneOfServerHandler());
}
}