作者:rickiyang
出處:www.cnblogs.com/rickiyang/p/11074232.html
我們來使用Protobuf進行序列化,它和XML,json一樣都有自己的語法,xml的后綴是.xml,json檔案的后綴是.json,自然Protobuf檔案的后綴就是.proto(哈哈,當然不是全稱),
下面我們使用Protobuf來封裝一段訊息,通過一個案例簡單介紹一下它的使用,
首先我們用Protobuf的語法格式來寫一段需要序列化的物件,命名格式為:Msg.proto
option java_package = "cn.edu.hust.netty.demo10";
option java_outer_classname = "MessageProto";
message RequestMsg{
required bytes msgType = 1;
required string receiveOne = 2;
required string msg = 3;
}
message ResponseMsg{
required bytes msgType = 1;
required string receiveOne = 2;
required string msg = 3;
}
關于Message.proto中的語法格式,詳情大家google一下相關的說明,網上很多介紹,再次簡單就上面的語法說明一下:
- option java_package:表示生成的.java檔案的包名
- option java_outer_classname:生成的java檔案的檔案名
- message : 為他的基本型別,如同java中的class一樣
欄位修飾符:
- required:一個格式良好的訊息一定要含有1個這種欄位,表示該值是必須要設定的;
- optional:訊息格式中該欄位可以有0個或1個值(不超過1個),
- repeated:在一個格式良好的訊息中,這種欄位可以重復任意多次(包括0次),重復的值的順序會被保留,表示該值可以重復,相當于java中的List,
字符型別稍微有些不同:double,float,int32,int64,bool(boolean),string,bytes,稍微有些不同,String,boolean,int有差別,
另外我們看到上面3個欄位分別賦值了,這個值是什么意思呢?訊息定義中,每個欄位都有唯一的一個數字識別符號,這些識別符號是用來在訊息的二進制格式中識別各個欄位的,一旦開始使用就不能夠再改變,注:[1,15]之內的標識號在編碼的時候會占用一個位元組,[16,2047]之內的標識號則占用2個位元組,所以應該為那些頻繁出現的訊息元素保留 [1,15]之內的標識號,
關于Protobuf 的語法我們就簡單的介紹這么多,更多細節大家自己去查閱檔案吧,下面我們開始使用Protobuf 來進行序列化,
首先我們的在工程中引入protobuf的jar包,目前官方版本最高3.2,我們用3.0的吧:
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.0.2</version>
</dependency>
Protobuf的檔案已經定義好了,下就需要把它編譯成java代碼,這里我們的借助到google為我們提供的腳本工具protoc,鏈接在這里,點擊下載這里提供的是protoc-3.0.2,要注意protoc的版本需要和Protobuf的版本對應上,不然不同的版本之間會有一些差異決議可能會有問題,現在知道我們為啥非得選用protobuf3.0.2版本吧,因為我沒有找到別的版本的protoc,,,
下載好了我們解壓縮然后把剛才寫好的Msg.proto檔案復制進去,

接著我們進cmd輸入如下命令:

主要是第三句命令,如果你輸入沒有報錯的話你的proto檔案夾應該會生成一個子檔案夾:

進去該檔案夾你會看到已經生成了MessageProto.java檔案,恭喜你,這時候你已經完成了protobuf序列化檔案的生成,然后你把該檔案拷貝至工程目錄下,
接下來我們用生成的檔案去發訊息吧,還是老套路服務端和客戶端,
服務端:
public class ProtoBufServer {
private int port;
public ProtoBufServer(int port) {
this.port = port;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap server = new ServerBootstrap().group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInitializer());
try {
ChannelFuture future = server.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
ProtoBufServer server = new ProtoBufServer(7788);
server.start();
}
}
服務端Initializer:
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(MessageProto.RequestMsg.getDefaultInstance()));
pipeline.addLast(new ProtoBufServerHandler());
}
}
服務端handler:
public class ProtoBufServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
MessageProto.ResponseMsg.Builder builder = MessageProto.ResponseMsg.newBuilder();
builder.setMsgType(ByteString.copyFromUtf8("CBSP"));
builder.setReceiveOne("小紅");
builder.setMsg("你好,你有啥事");
ctx.writeAndFlush(builder.build());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
MessageProto.RequestMsg m = (MessageProto.RequestMsg)msg;
System.out.println("Client say: "+m.getReceiveOne()+","+m.getMsg());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
}
客戶端:
public class ProtoBufClient {
private int port;
private String address;
public ProtoBufClient(int port, String address) {
this.port = port;
this.address = address;
}
public void start(){
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInitializer());
try {
ChannelFuture future = bootstrap.connect(address,port).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
ProtoBufClient client = new ProtoBufClient(7788,"127.0.0.1");
client.start();
}
}
客戶端Initializer:
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new ProtoBufClientHandler());
}
}
客戶端handler:
public class ProtoBufClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
MessageProto.ResponseMsg m = (MessageProto.ResponseMsg)msg;
System.out.println("Server say: "+m.getReceiveOne()+","+m.getMsg());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
MessageProto.RequestMsg.Builder builder = MessageProto.RequestMsg.newBuilder();
builder.setMsgType(ByteString.copyFromUtf8("CBSP"));
builder.setReceiveOne("小明");
builder.setMsg("你好,我找你有事");
ctx.writeAndFlush(builder.build());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client is close");
}
}
啟動服務端和客戶端,輸出如下:

最簡單的protoBuf應用案例我們就寫完了,真實的使用場景大同小異,隨機應變即可,
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2021最新版)
2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!
3.阿里 Mock 工具正式開源,干掉市面上所有 Mock 工具!
4.Spring Cloud 2020.0.0 正式發布,全新顛覆性版本!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/288026.html
標籤:Java
