文章目录
参考
前言
Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
看到有人说起protobuf这个数据交换的格式,和平台无关,和语言无关,性能还比较好,就想学习一下。想到就做,边做边记录。
开发环境
- win10
- Qt 5.15.2
- msvc2019 x64
# 一、下载
1. 打开网页
https://developers.google.cn/protocol-buffers/
2. 点击download
3. 下载
二、 编译
1. 解压
目录结构如下
protobuf-3.21.9
├─benchmarks
├─build_defs
├─cmake
├─conformance
├─csharp
├─editors
├─examples
├─m4
├─objectivec
├─php
├─ruby
├─src
├─third_party
└─util
2. 用QC打开项目
3. 编译并等待
编译结果如下:
..\protobuf-3.21.9\build-cmake-Desktop_Qt_5_15_2_MSVC2019_64bit-Release
│ .ninja_deps
│ .ninja_log
│ build.ninja
│ CMakeCache.txt
│ cmake_install.cmake
│ CTestTestfile.cmake
│ extract_includes.bat // <------------ 注意这个文件
│ gmock.lib
│ gmock_main.lib
│ libprotobuf-lite.lib
│ libprotobuf.lib
│ libprotoc.lib
│ lite-arena-test.exe
│ lite-arena-test.exe.manifest
│ lite-test.exe
│ lite-test.exe.manifest
│ protobuf-lite-test-common.lib
│ protobuf-lite.pc
│ protobuf-test-common.lib
│ protobuf.pc
│ protoc.exe
│ protoc.exe.manifest
│ qtcsettings.cmake
│ tests.exe
│ tests.exe.manifest
│ test_plugin.exe
│ test_plugin.exe.manifest
│ version.rc
│
├─.cmake
├─.qtc_clangd
├─cmake
├─CMakeFiles
└─Testing
4. 打开 extract_includes.bat
内容如下所示:
# 新建一批目录
mkdir include
mkdir include\google
mkdir include\google\protobuf
mkdir include\google\protobuf\compiler
......
# 复制头文件到对应目录最终
copy "E:\3rd\protobuf-3.21.9\..\src\google\protobuf\any.h" include\google\protobuf\any.h
copy "E:\3rd\protobuf-3.21.9\..\src\google\protobuf\any.pb.h" include\google\protobuf\any.pb.h
copy "E:\3rd\protobuf-3.21.9\..\src\google\protobuf\api.pb.h" include\google\protobuf\api.pb.h
.....
copy的路径对于我来说,有点问题,所以做相应修改,去掉中间的 \..
目录:
copy "E:\3rd\protobuf-3.21.9\src\google\protobuf\api.pb.h" include\google\protobuf\api.pb.h
执行批处理文件后,最终结果会把相应的头文件复制到include目录下
5. 新建lib文件夹
再将编译好的lib文件,复制到这个目录,可以再细分为 debug 和 release 目录(此处略),最后将 include 和 该lib文件加复制到另外一个目录:lib_protobuf
..\lib_protobuf
│ protoc.exe // 可以考虑将这个文件复制过来
├─include
│ └─google
│ └─protobuf
│ │ any.h
│ │ .........
│ ├─compiler
│ │ ├─cpp
│ │ ├─csharp
│ │ ├─java
│ │ ├─objectivec
│ │ ├─php
│ │ ├─python
│ │ └─ruby
│ ├─io
│ ├─stubs
│ └─util
└─lib
gmock.lib
gmock_main.lib
libprotobuf-lite.lib
libprotobuf.lib
libprotoc.lib
protobuf-lite-test-common.lib
protobuf-test-common.lib
三、使用
protobuf的使用和qSoap的使用有点相似。
protobuf协议利用生成工具 protoc.exe 从 自定义的 .proto 的文件 生成相应语言(cpp,c#, java)的接口文件,然后加入工程进行编译即可。
命令格式:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto
1. 新建 lm.helloworld.proto
syntax = "proto3";
package lm;
message helloworld
{
int32 id = 1;
string greeting = 2;
}
利用 cmd 执行 protoc.exe --cpp_out=. lm.helloworld.proto
命令即可:
- lm.helloworld.pb.h ---- 生成类的头文件
- lm.helloworld.pb.cc ---- 生成类的实现文件
2. Qt 新建 HelloProtobuf 子目录项目
再建两个子项目分别为:Reader 和 Writer,再复制 lib_protobuf 的文件夹过来
整体目录如下:
3. lib_protobuf 目录
-
将 自定义的proto文件和生成的类文件拷贝到lib_protobuf 目录下
-
新建 protobuf.pri 文件
win32: LIBS += -L$$PWD/lib/ -llibprotobuf INCLUDEPATH += $$PWD/include \ $$PWD DEPENDPATH += $$PWD/include # 下面这一段 可以不用 #PRE_TARGETDEPS += $$PWD/lib/libprotobuf.lib #PRE_TARGETDEPS += $$PWD/gmock.lib #PRE_TARGETDEPS += $$PWD/libprotoc.lib #PRE_TARGETDEPS += $$PWD/gmock_main.lib #PRE_TARGETDEPS += $$PWD/libprotobuf.lib #PRE_TARGETDEPS += $$PWD/libprotobuf-lite.lib #PRE_TARGETDEPS += $$PWD/protobuf-test-common.lib #PRE_TARGETDEPS += $$PWD/protobuf-lite-test-common.lib HEADERS += \ $$PWD/ProtobufTool.h \ // 后加的文件 $$PWD/lm.helloworld.pb.h // protobuf 生成的头文件 SOURCES += \ $$PWD/lm.helloworld.pb.cc // protobuf 生成的实现文件 DISTFILES += \ $$PWD/lm.helloworld.proto \ // .proto 相当于原型文件, 加到项目中看得比较清楚 $$PWD/protoc.bat // 就是一句:protoc.exe --cpp_out=. lm.helloworld.proto,方便操作
-
ProtobufTool.h
唯一的功能是序列化成 QByteArray
#ifndef PROTOBUFTOOL_H #define PROTOBUFTOOL_H //#include <google/protobuf/io/coded_stream.h> //#include <google/protobuf/arena.h> //#include <google/protobuf/arenastring.h> //#include <google/protobuf/generated_message_util.h> //#include <google/protobuf/metadata_lite.h> //#include <google/protobuf/generated_message_reflection.h> //#include <google/protobuf/repeated_field.h> // IWYU pragma: export //#include <google/protobuf/extension_set.h> // IWYU pragma: export //#include <google/protobuf/unknown_field_set.h> #include <google/protobuf/message.h> #include <google/protobuf/port_def.inc> #include <QByteArray> #include <QString> class ProtobufTool { public: static QByteArray SerializeToByteArray(PROTOBUF_NAMESPACE_ID::Message &msg){ auto stdstring = msg.SerializeAsString(); return QByteArray::fromStdString (stdstring); } }; #endif // PROTOBUFTOOL_H
4. Writer 工程
-
Writer.pro 文件
CONFIG -= app_bundle CONFIG += c++17 SOURCES += main.cpp include ($$PWD/../lib_protobuf/protobuf.pri) msvc : QMAKE_CXXFLAGS += /utf-8
-
main.cpp
#include "ProtobufTool.h" #include "lm.helloworld.pb.h" #include <QDebug> #include <QFile> #include <QFileInfo> #define qout if( 1 ) qDebug() << __FILE__ << __LINE__ << ": " int main() { lm::helloworld msg1; msg1.set_id(1); msg1.set_greeting ("中国人民共和国!"); auto ba = ProtobufTool::SerializeToByteArray(msg1); qout << ba; QFile file("../log"); QFileInfo info(file); qout << info.absoluteFilePath (); if(file.open (QFile::WriteOnly)){ file.write (ba); file.close (); } return 0; }
5. Reader 工程
-
Reader.pro 文件 两个工程文件是一样的
CONFIG -= app_bundle CONFIG += c++17 SOURCES += main.cpp include ($$PWD/../lib_protobuf/protobuf.pri) msvc : QMAKE_CXXFLAGS += /utf-8
-
main.cpp
#include "lm.helloworld.pb.h" #include <QFile> #include <QDebug> #define qout if( 1 ) qDebug() << __FILE__ << __LINE__ << ": " int main(void ) { lm::helloworld msg; QByteArray ba; QFile file("../log"); if(file.open (QFile::ReadOnly)) { ba = file.readAll (); file.close (); } msg.ParseFromArray (ba.constData (),ba.size ()); qout << msg.id (); qout << QString::fromStdString (msg.greeting()); return 0; }
-
运行结果:
..\..\HelloProtobuf\Reader\main.cpp 19 : 1 ..\..\HelloProtobuf\Reader\main.cpp 20 : "中国人民共和国!"
四、总结
Protobuf 生成的对象流是二进制的,体积小巧,传输效率高。
开发效率没有json方便,如果不需要编译的程序,使用起来会更方便。
原来还想学习下protobuf的原型文件的语法来着,感觉在开发中不会用到protobuf,略吧。