对protobuf不是很了解的可以看这篇文章:https://blog.csdn.net/weixin_44240370/article/details/101313220
使用protobuf进行序列化和反序列化操作,并且也知道了这个操作相对于JSON、XML数据格式的优势,但是不足的是它不适合给大量数据进行这样的操作,接下来就使用protobuf和Netty进行整合完成实际的protobuf应用实践。
首先也是需要先创建Client端和Server端,已经知道了Netty基本架构流程都是一个端,一个初始化的信息过滤器,一个自定义的控制器。暂且先不说Handler端的写法都是一样的,但是在解码和编码上确实是有一些问题这是需要我们注意的,但是服务器端和客户端的写法基本上是不变的。。
TestClientInitializer:
public class TestClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(DataInfo.MyMessage.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new TestClientHandler());
}
}
首先要清楚Initializer的消息定位,消息传过来之后首先要对这些信息进行一定的过滤,筛选的内容为消息的编码和解码,但是因为我们这里使用的是Protobuf的序列化和反序列化,因此就不能使用之前的StringDecoder和StringEncoder了,而是要用ProtobufDecoder和ProtobufEncoder,并且ProtobufDecoder里面要填写一个参数,内容就是具体解码的那个类,使用的也是Protobuf内置的方法getDefaultInstance()引用一下就可以了。
除了这两种方法,还要采用ProtobufVarint32LengthFieldPrepender()和ProtobufVarint32FrameDecoder()进行过滤。
ProtobufVarint32LengthFieldPrepender()的作用是对protobuf协议的的消息头上加上一个长度为32的整形字段,用于标志这个消息的长度。
ProtobufVarint32FrameDecoder()的作用是针对protobuf协议的ProtobufVarint32LengthFieldPrepender()所加的长度属性的解码器
客户端要给服务端去发送信息,那么过滤的机制必然是相同的
TestServerInitializer:
public class TestServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(DataInfo.MyMessage.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new TestServerHandler());
}
此外还要对Handler进行编写,我们需要知道的一件事情是:客户端要做的是发送消息,服务端接收消息并且打印出来
在客户端,我们要做的是发送消息,在服务端,我们要做的是接收消息打印出来,而客户端的信息产生我们藉由protobuf生成,然后放进channel去write出去,在服务端因为这些信息都被封装在msg里面了,因此直接打印就可以了。
TestClientHandler:
public class TestClientHandler extends SimpleChannelInboundHandler<MyDataInfo.Person> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, MyDataInfo.Person msg) throws Exception {
}
//从客户端发给服务器端,并且通过protobuf生成的序列化去发送过去
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
MyDataInfo.Person person = MyDataInfo.Person.newBuilder()
.setName("zxc")
.setAge(18)
.setAddress("anhui")
.build();
ctx.channel().writeAndFlush(person);
}
}
TestServerHandler:
public class TestServerHandler extends SimpleChannelInboundHandler<MyDataInfo.Person> {
//当客户端发送过来消息后,服务端的响应
@Override
protected void channelRead0(ChannelHandlerContext ctx, MyDataInfo.Person msg) throws Exception {
System.out.println(msg.getName());
System.out.println(msg.getAge());
System.out.println(msg.getAddress());
}
}