場景
Netty的Socket程式設計詳解-搭建服務端與用戶端并進行資料傳輸:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108615023
ProtoBuf的介紹以及在Java中使用protobuf将對象進行序列化與反序列化:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108667427
在上面兩篇部落格基礎上,對于Socket搭建服務端和用戶端進行通信以及
在Java中Protobuf的使用已經有了初步的了解。
那麼怎樣使用Netty進行服務端和用戶端的通信時使用protobuf進行資料傳遞。
注:
部落格:
https://blog.csdn.net/badao_liumang_qizhi
關注公衆号
霸道的程式猿
擷取程式設計相關電子書、教程推送與免費下載下傳。
實作
在src下建立protobuf目錄,目錄下建立Person.prpto描述檔案
syntax = "proto3"; package com.badao.protobuf; option optimize_for =SPEED;option java_package = "com.badao.NettyProtobuf";option java_outer_classname = "BadaoDataInfo"; message Person { string name = 1; int32 age = 2; string address = 3;}
具體的講解可以見上面的部落格。
然後在src/main/java下建立包com.badao.NettyProtobuf
打開終端Ternimal,使用protoc進行編譯代碼
protoc --java_out=src/main/java src/protobuf/Person.proto
然後就會在上面NettyProtobuf包下生成BadaoDataInfo類
上面具體的流程與講解參照上面的部落格。
然後建立服務端類NettyProtobufServer
package com.badao.NettyProtobuf; import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler; public class NettyProtobufServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try{ ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new NettyProtobufInitializer()); //綁定端口 ChannelFuture channelFuture = serverBootstrap.bind(70).sync(); channelFuture.channel().closeFuture().sync(); }finally { //關閉事件組 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }}
其中用到了自定義的初始化器NettyProtoInitilizer
package com.badao.NettyProtobuf; 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; public class NettyProtobufInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new ProtobufVarint32FrameDecoder()); pipeline.addLast(new ProtobufDecoder(BadaoDataInfo.Person.getDefaultInstance())); pipeline.addLast(new ProtobufVarint32LengthFieldPrepender()); pipeline.addLast(new ProtobufEncoder()); pipeline.addLast(new NettyProtobufHandler()); }}
注意這裡不同的是添加的處理器是Netty自帶的關于Protobuf的解碼編碼的處理器。
其中Decode處理器的參數就是上面生成的代碼的預設的執行個體。
然後最後又添加了自定義的處理器NettyProtobufHandler
package com.badao.NettyProtobuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class NettyProtobufHandler extends SimpleChannelInboundHandler<BadaoDataInfo.Person> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, BadaoDataInfo.Person msg) throws Exception {
System.out.println(msg.getName());
System.out.println(msg.getAge());
System.out.println(msg.getAddress());
}
}
自定義處理器中在收到發送的消息時将消息進行輸出,這裡這裡在繼承SimpleChannelInboundHandler
的泛型就是上面生成的類。
然後建立用戶端類
package com.badao.NettyProtobuf; import io.netty.bootstrap.Bootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioSocketChannel; public class NettyProtobufClient { public static void main(String[] args) throws Exception { EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) .handler(new NettyProtoBufClientInitializer()); //綁定端口 ChannelFuture channelFuture = bootstrap.connect("localhost", 70); channelFuture.channel().closeFuture().sync(); } finally { //關閉事件組 eventLoopGroup.shutdownGracefully(); } }}
編寫其用到的初始化器
package com.badao.NettyProtobuf; 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; public class NettyProtoBufClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new ProtobufVarint32FrameDecoder()); pipeline.addLast(new ProtobufDecoder(BadaoDataInfo.Person.getDefaultInstance())); pipeline.addLast(new ProtobufVarint32LengthFieldPrepender()); pipeline.addLast(new ProtobufEncoder()); pipeline.addLast(new NettyProtobufClientHandler()); }}
和服務端的初始化器用到的處理器一緻,最後也要有自定義的處理器
package com.badao.NettyProtobuf; import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler; public class NettyProtobufClientHandler extends SimpleChannelInboundHandler<BadaoDataInfo.Person> { @Override protected void channelRead0(ChannelHandlerContext ctx, BadaoDataInfo.Person msg) throws Exception { } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { BadaoDataInfo.Person person = BadaoDataInfo.Person.newBuilder() .setName("公衆号:霸道的程式猿") .setAge(100) .setAddress("中國").build(); ctx.channel().writeAndFlush(person); }}
在與服務端建立連接配接即通道激活的回調方法中向服務端發送資料。
然後運作服務端的main方法再運作用戶端的main方法。