Protocol Buffers学习【Qt】

参考

Protobuf通信协议详解:代码演示、详细原理介绍等 - 知乎 (zhihu.com)

Protocol Buffers | Google Developers

前言

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,略吧。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以按照以下步骤使用Google Protocol Buffers (protobuf)序列化和发送`std::vector<cv::Vec6f>`对象,并使用QTsocket进行发送和接收: 1. 定义消息格式:首先,您需要在.proto文件中定义消息格式。创建一个名为`vector.proto`的文件,并添加以下内容: ``` syntax = "proto3"; import "opencv.proto"; message VectorMessage { repeated cv.Vec6f vector = 1; } ``` 2. 定义OpenCV类型:由于`cv::Vec6f`是OpenCV的类型,您需要在.proto文件中定义该类型。创建一个名为`opencv.proto`的文件,并添加以下内容: ``` syntax = "proto3"; package cv; message Vec6f { repeated float data = 1; } ``` 3. 生成C++类:使用protobuf编译器生成C++类。在命令行中运行以下命令: ``` protoc --cpp_out=. opencv.proto protoc --cpp_out=. vector.proto ``` 这将生成`opencv.pb.h`和`opencv.pb.cc`文件以及`vector.pb.h`和`vector.pb.cc`文件。 4. 序列化和发送:在发送方的代码中,您可以使用protobuf库将`std::vector<cv::Vec6f>`对象序列化为字节流,并使用QTsocket发送。 ```cpp #include <QTcpSocket> #include "vector.pb.h" void sendVector(const std::vector<cv::Vec6f>& vector) { // 创建一个VectorMessage对象 VectorMessage message; // 将cv::Vec6f向量复制到VectorMessage对象中 for (const cv::Vec6f& vec : vector) { cv::Vec6f* newVec = message.add_vector(); for (float value : vec) { newVec->add_data(value); } } // 序列化VectorMessage对象 std::string serializedData = message.SerializeAsString(); // 使用QTsocket发送序列化后的数据 QTcpSocket socket; socket.connectToHost("127.0.0.1", 1234); // 替换为实际的主机和端口 if (socket.waitForConnected()) { socket.write(serializedData.c_str(), serializedData.size()); socket.waitForBytesWritten(); socket.disconnectFromHost(); } } ``` 5. 接收和反序列化:在接收方的代码中,您可以使用protobuf库接收字节流并将其反序列化为`std::vector<cv::Vec6f>`对象。 ```cpp #include <QTcpServer> #include <QTcpSocket> #include "vector.pb.h" void receiveVector() { QTcpServer server; server.listen(QHostAddress::Any, 1234); // 替换为实际的端口 if (server.waitForNewConnection()) { QTcpSocket* socket = server.nextPendingConnection(); if (socket->state() == QAbstractSocket::ConnectedState) { QByteArray data = socket->readAll(); // 反序列化接收到的数据 VectorMessage message; message.ParseFromArray(data.constData(), data.size()); // 从VectorMessage对象中提取cv::Vec6f向量 std::vector<cv::Vec6f> vector; for (const cv::Vec6f& vec : message.vector()) { vector.push_back(vec); } // 处理接收到的向量 // ... socket->disconnectFromHost(); } socket->close(); socket->deleteLater(); } server.close(); } ``` 这是一个基本的示例,可以帮助您开始使用Google Protocol Buffers序列化和发送`std::vector<cv::Vec6f>`对象并使用QTsocket进行发送和接收。请根据您的实际需求进行适当的修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值