第三方库介绍——Protobuf库(更高效的协议)

news2024/10/7 2:21:33

文章目录

  • protobuf综述
  • 传输协议与指令
    • 创建协议
    • 编译协议
    • 介绍addressbook.pb.h文件
    • 序列化与反序列化的接口
  • 利用soctet实现客户端与服务端传输协议
    • Linux(Ubuntu)安装protoc步骤
    • 编写案例代码
      • Cartoon.proto
      • tcpsocket.h
      • MyTcpsocket.h
      • client.cpp
      • server.cpp
      • CMakeList.tx:
    • 编译案例

protobuf综述

  • Protocol Buffers,是Google公司开发的一种数据格式,类似于XML和json,是一种用于数据传输时将数据序列化和反序列化的一个跨平台(支持目前主流的各种语言)工具库,可用于数据存储、通信协议等方面。它不依赖于语言和平台并且可扩展性极强。

protobuf的优点:

  • 更小、更快、更简单。

  • 支持多种编程语言 。

  • 解析速度快。

  • 可扩展性强。

传输协议与指令

创建协议

protobuf的核心是一个.proto文件,我们自定义一个.proto来创建我们的协议数据,然后使用protocol buffer 编译器工具编译生成两个"文件名.pb.cc""文件名.pb.h"的文件

例如我们创建一个addressbook.proto文件:

  • 该文件定义为“地址簿”应用程序,可以在文件中读写联系人的详细信息。地址簿中的每个人都有姓名、ID、电子邮件地址和联系电话。
syntax = "proto2";
package tutorial;

message Person {
  required string name 		= 1;//对每个字段需要带上一个编号,编号是从1开始的:
  required int32 id 			= 2;  
  optional string email 		= 3;

  enum PhoneType {
    MOBILE 			= 0;//移动电话
    HOME 			= 1;//家庭电话
    WORK 			= 2;//工作电话
  }

  message PhoneNumber {
    required string number 	= 1;
    optional PhoneType type 	= 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;

  message AddressBook{
	repeated Person people = 1;
  }
}
  • syntax为语法版本

    • 有proto2、proto3两个版本,语法上是存在区别的,proto3并不是完全兼容proto2的。同C/C++语言类似,
  • FieldType fieldName = fieldNumber;

    • .proto规定了一些数据格式,如proto2的数据类型有:double 、 float、 int32 、 uint32 、 string、bool等。
    • fieldName是字段的名称,可以根据需求自定义。
    • fieldNumber是字段的唯一标识号,用于在消息的二进制编码中标识字段。标识号的作用是确保在消息的编解码过程中能够准确地识别每个字段。在同一个Message中,每个字段的标识号必须是唯一的。
    • 标识号的范围是1到2^29 - 1(Protobuf 3版本中是1到536,870,911)。
    • [1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。
  • message为关键字

    • 修饰的Person 会对应生成C++中的Person 的结构体。
    • 支持嵌套,实际上生成了多个类,有几个message就生成几个类
  • required为前缀修饰【字段只能出现1次】

    • 表明该字段是必填字段,被修饰的变量必须要赋值。还有其它两个修饰关键字:

      • optional:被修饰的变量可以不赋值,未赋值则采用默认值,例如bool类型为false,int32为0,string为“”,enum为0等【字段可以出现0次或1次】

      • repeated:作用是用来发送一个list的,当我们对某个变量发送的是一个list的时候,就需要给这个变量前面加上repeated【字段可以出现任意多次(包含0次)】

  • enum类型数据

    • enum编号是从0开始的,因为每个枚举定义必须包含一个映射到零的常量作为第一个元素
    • 每个枚举值应以分号结束,而不是逗号。
  • package为命名空间

    • 指定生成后.pd.h类的命名空间
    • 通过使用包,可以在一个大型的Protobuf项目中组织消息类型,避免不同消息类型之间的命名冲突。同时,包还可以在生成的代码中生成对应的命名空间,以便在编程语言中进行访问和引用。
  • import导入另外一个文件的pb

    • proto可以导入在不同的文件中的定义。通过在文件顶端加入一个import语句
    • 例如:import "myproject/other_protos.proto";
    • Import Message 的用处主要在于提供了方便的代码管理机制,类似 C 语言中的头文件。您可以将一些公用的 Message 定义在一个 package 中,然后在别的 .proto 文件中引入该 package,进而使用其中的消息定义。Google Protocol Buffer 可以很好地支持嵌套 Message 和引入 Message。

对于proto3版本还有多个关键字:

  • singular 不是关键字,代表这个变量是一种类型,就和上面的例子一样,就是 类型+变量名的格式,在传输时,可以不给这种类型赋值,若没赋值则采用默认值,例如bool类型为false,int32为0,string为“”等
  • repeated 作用是用来发送一个list的,当我们对某个变量发送的是一个list的时候,就需要给这个变量前面加上repeated
  • map:这是一个成对的键值对字段
  • optional:与 singular 相同,不过您可以检查该值是否明确设置

参考博文:C/C++编程:Protobuf 使用

编译协议

使用protocol buffer 编译器工具编译addressbook.proto文件的命令

protoc --cpp_out=. ./addressbook.proto

此时编译会生成addressbook.pb.cc、addressbook.pb.h两个文件。

介绍addressbook.pb.h文件

addressbook.pb.h里生成了一个协议数据结构体与操作该结构体的一些接口,包括组包与解包(序列化与反序列化)接口,对应的addressbook.pb.cc里就是这些接口对应的实现。

序列化:通俗讲就是将类的实例化对象转成二进制数据然后通过TCP和UDP等传输
反序列化:通俗讲就是将序列化后的二进制数据转换成类的实例化对象,也就是将数据取出来并还原成原先的类的实例化对象

生成的"addressbook.pb.h"中,有几个message就生成几个类

Person类中,编译器会为每个字段生成访问器,例如

在这里插入图片描述
每个message类还包含许多其他方法,可用于检查或操作整个类:
检查是否所有 required 字段已被初始化

bool IsInitialized() const;

返回 message 的人类可读的表达形式,对调试特别有用

string DebugString() const;

用给定的 message 的值覆盖 message 特别有用

void CopyFrom(const Person& from);

/将所有元素清除回 empty 状态

void Clear();

而用repeated修饰的对象,需要遍历,然后加入值时有个方法是add_对象名()

people.add_phones();

序列化与反序列化的接口

每个 protocol buffer 类都有提供通过使用 protocobuffer 的二进制格式来读写message 的方法:

序列化消息并将二进制字节存储在给定的字符串中

bool SerializeToString(string* output) const

将给定字符串解析为message

bool ParseFromString(const string& data);

将 message 写入给定的 C++ 的 ostream

bool SerializeToOstream(ostream* output) const;

将给定 C++ istream 解析为message

bool ParseFromIstream(istream* input);

参考博文:protobuf学习笔记

利用soctet实现客户端与服务端传输协议

运行环境:ubuntu18.04,C++11,cmake3.15.5,protobuf3.17.3

Linux(Ubuntu)安装protoc步骤

安装protobuf需要依赖一些工具,需要先安装依赖:

sudo apt-get install autoconf automake libtool curl make g++ unzip

进入自定义的目录下安装proto3,解压并编译安装

wget -O protobuf-3.17.3.tar.gz https://codeload.github.com/protocolbuffers/protobuf/tar.gz/refs/tags/v3.17.3
tar -zxf protobuf-3.17.3.tar.gz
cd protobuf-3.17.3/cmake/
mkdir build && cd build
cmake .. -Dprotobuf_BUILD_TESTS=OFF
make -j4 install

安装好后使用 protoc --version命令看看是否出现版本号,出现则代表安装成功

如果通过weget不能下载,则在windows端下载

https://github.com/protocolbuffers/ProtoBuf/releases/download/v3.17.3/protoc-3.17.3-linux-x86_64.zip

编写案例代码

创建目录结构如下:

在这里插入图片描述

Cartoon.proto

syntax = "proto3";

import "google/protobuf/timestamp.proto";

message Cartoon{
  int32 Id                        = 1;
  string name                     = 2;
  string company                  = 3;
  google.protobuf.Timestamp time  = 4;
}

message CartoonList{
  repeated Cartoon cartoonList    = 1;          //CartoonList
}


message CartoonRequest{
  int32 query                     = 1;          //命令码 1->add 2->selectById 3->selectAll
  CartoonList cartoon             = 2;          //add 时一个Cartoon list, 默认放在  CartoonInf  文件中
  int32 selectById                = 3;          //select 时 Cartoon的Id
  bool selectAll                  = 4;          //查询所有的 Cartoon
}

message CartoonResponse{
  string res                      = 1;
  CartoonList cartoon             = 2;          //返回的 select Cartoon的信息
}

tcpsocket.h

//
// Created by Aaj on 2021/8/9.
//

#ifndef MYPROTOCOLBUF_TCPSOCKET_H
#define MYPROTOCOLBUF_TCPSOCKET_H

#include <iostream>
#include <string>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<unistd.h>
using std::cout;
using std::endl;
using std::cin;
using std::string;
inline void CHECK(bool operation) {
    if (operation == false) {
        exit(-1);
    }
}
class TcpSocket {
public:
    TcpSocket() : _sock(-1) {}
    void setNewSockFd(int newsockfd) {
        _sock = newsockfd;
    }
    // 创建套接字
    bool Socket() {
        _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (_sock < 0) {
            perror("socket error");
            return false;
        }
        return true;
    }
    // 为套接字绑定地址信息
    bool Bind(string& ip, uint16_t& port) const {
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        size_t len = sizeof(struct sockaddr_in);
        int binding = bind(_sock, (struct sockaddr*)&addr, len);
        if (binding < 0) {
            perror("bind error");
            return false;
        }
        return true;
    }
    // 服务端监听
    bool Listen(int backlog = 5) const {
        int listening = listen(_sock, backlog);
        if (listening < 0) {
            perror("listen error");
            return false;
        }
        return true;
    }
    // 客户端请求连接
    bool Connect(string& ip, uint16_t& port) const {
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        size_t len = sizeof(struct sockaddr_in);
        int connecting = connect(_sock, (struct sockaddr*)&addr, len);
        if (connecting < 0) {
            perror("connect error");
            return false;
        }
        return true;
    }
    // 服务端接受客户端请求
    bool Accept(TcpSocket& cli_sock, struct sockaddr_in* cli_addr = NULL) {
        TcpSocket clisock;
        size_t len = sizeof(struct TcpSocket);
        int newsockfd = accept(_sock, (struct sockaddr*)&clisock, (socklen_t *)&len);
        if (newsockfd < 0) {
            perror("accept error");
            return false;
        }
        if (cli_addr != NULL) {
            memcpy(cli_addr, &clisock, len);
            cli_sock.setNewSockFd(newsockfd);
        }
        return true;
    }
    // 发送数据
    bool Send(string& buf) {
        size_t size = send(_sock, buf.c_str(), buf.size(), 0);
        if (size < 0) {
            perror("send error");
            return false;
        }
        return true;
    }
    // 接收数据
    bool Recv(string& buf) {
        char buf_tmp[4096] = {0};
        size_t size = recv(_sock, buf_tmp, sizeof(buf_tmp), 0);
        if (size < 0) {
            perror("recv error");
            return false;
        } else if (size == 0) {
            perror("peer shutdown");
            return false;
        }
        buf.assign(buf_tmp, size);
        return true;
    }
    void Close() {
        close(_sock);
        _sock = -1;
    }
private:
    int _sock;
};

#endif //MYPROTOCOLBUF_TCPSOCKET_H

MyTcpsocket.h

//
// Created by Aaj on 2021/8/6.
//

#ifndef MYPROTOCOLBUF_MYTCPSOCKET_H
#define MYPROTOCOLBUF_MYTCPSOCKET_H  1

#include<string.h>
#include<iostream>
#include<string>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<unistd.h>

using namespace std;

void CHECK(bool res){
    if(res == false){
        exit(-1);
    }
}

class MyTcpSocket{
private:
    int _sock;
public:
    MyTcpSocket() : _sock(-1){}
    void setNewSockFd(int newsockfd){
        _sock = newsockfd;
    }
    //1.创建套接字
    bool Socket(){
        //函数原型 int socket(int domain, int type, int protocol);
        //int domain(协议族) 参数为 AF_INET 表示 TCP/IP_IPv4
        //int type(套接字类型) 参数为SOCK_STREAM 表示 TPC流
        //int protocol 参数为0,因为 domain 和 type 已经确定
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if(_sock < 0){
            perror("socket error");
            return false;
        }
        return true;
    }

    //2.为套接字绑定地址信息
    bool Bind(string& ip,uint16_t& port) const{
        struct sockaddr_in addr;        //加上struct为c写法
        bzero(&addr, sizeof(addr));     //清零
        addr.sin_family = AF_INET;      //TCP
        addr.sin_port = htons(port);    //htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处。
        addr.sin_addr.s_addr = inet_addr(ip.c_str()); //将IP转为二进制形式
        size_t len = sizeof(struct sockaddr_in);
        int binding = bind(_sock,(struct sockaddr*)&addr, len);
        if(binding < 0){
            perror("bind error");
            return false;
        }
        return true;
    }

    //3.服务端监听
    bool Listen(int backlog = 5) const {
        int listening = listen(_sock,backlog);
        if(listening < 0){
            perror("listen error");
            return false;
        }
        return true;
    }

    //4.客户端请求连接
    bool Connect(string& ip,uint16_t& port) const {
        struct sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        size_t len = sizeof(struct sockaddr_in);
        int connecting = connect(_sock,(struct sockaddr*)& addr,len);
        if(connecting < 0){
            perror("connect error");
            return false;
        }
        return true;
    }

    //5.服务端接受客户端请求
    bool Accept(MyTcpSocket& cli_sock,struct sockaddr_in* cli_addr = NULL){
        MyTcpSocket clisock;
        size_t len = sizeof(struct MyTcpSocket);
        int newsockfd = accept(_sock, (struct sockaddr*)&clisock, (socklen_t *)&len);
        if(newsockfd < 0){
            perror("accept error");
            return false;
        }
        if (cli_addr != NULL) {
            memcpy(cli_addr, &clisock, len);
            cli_sock.setNewSockFd(newsockfd);
        }
        return true;
    }

    //6.发送数据
    bool Send(string& buf){
        size_t size = send(_sock, buf.c_str(),buf.size(), 0);
        if(size < 0){
            perror("send error");
            return false;
        }
        return true;
    }

    //7.接收数据
    bool Recv(string& buf){
        char buf_tmp[4096] = {0};
        size_t size = recv(_sock,buf_tmp, sizeof(buf_tmp),0);
        if(size < 0){
            perror("recv error");
            return false;
        }
        else if(size == 0){
            perror("peer shutdown");
            return false;
        }
        buf.assign(buf_tmp, size);
        return true;
    }

    void Close(){
        close(_sock);
        _sock = -1;
    }
};

#endif //MYPROTOCOLBUF_MYTCPSOCKET_H

client.cpp

//
// Created by Aaj on 2021/8/6.
//

#include "src/Cartoon.pb.h"
#include "src/MyTcpSocket.h"
#include <google/protobuf/util/time_util.h>

#include<cstdio>
#include<iostream>
#include<fstream>
#include<ctime>
#include<list>
#include <netdb.h>

using namespace std;
using google::protobuf::util::TimeUtil;

void addCartoon(CartoonRequest &cartoonRequest){
    while(true){
        Cartoon *cartoon = cartoonRequest.mutable_cartoon()->add_cartoonlist();
        cout<<"输入动画Id: ";
        int Id;
        cin>>Id;
        //cout<<Id<<endl;
        cin.ignore(256,'\n');//忽略掉一个回车
        cartoon->set_id(Id);

        cout<<"输入动画名称: ";
        string name;
        getline(cin,name);
        //cout<<name<<endl;
        cartoon->set_name(name);

        cout<<"输入动画出品公司: ";
        string company;
        getline(cin,company);
        //cout<<company<<endl;
        cartoon->set_company(company);

        *cartoon->mutable_time() = TimeUtil::SecondsToTimestamp(time(NULL));

        string res;
        while(true){
            cout<<"输入 0 结束,1 继续输入"<<endl;
            cin>>res;
            if(res == "0" || res == "1")
                break;
        }
        if(res == "0"){
//            /*这里暂时写成直接写入CartoonInf文件方式*/
//            fstream output("CartoonInf",ios::out | ios::binary | ios::app); //app代表追加方式写入
//            CartoonList cartoonList = cartoonRequest.cartoon();
//            if(!cartoonList.SerializePartialToOstream(&output)){
//                cerr<<"无法写入CartoonInf\n";
//                exit(-1);
//            }
            return ;
        }
    }
}
void selectById(CartoonRequest &cartoonRequest){
    cout<<"请输入Id: ";
    int Id;
    cin>>Id;
    cin.ignore(256,'\n');//忽略掉一个回车
    cartoonRequest.set_selectbyid(Id);
}

void selectAll(CartoonRequest &cartoonRequest){
    cartoonRequest.set_selectall(true);
}
int main(){
    //如果没有CartoonInf则先创建一个
    if(!fopen("CartoonInf","r")){
        fopen("CartoonInf","w");
    }

    string ip = "127.0.0.1";//本机
    uint16_t  port = 9999;
    MyTcpSocket client;
    CHECK(client.Socket());
    CHECK(client.Connect(ip,port));

    while(1){
        CartoonRequest cartoonRequest;
        cout<<"输入请求码 1->add 2->selectById 3->selectAll,0->退出:";
        int code;
        cin>>code;
        cin.ignore(256,'\n');//忽略掉一个回车
        //cout<<code<<endl;
        cartoonRequest.set_query(code);
        //add
        if(code == 1){
            addCartoon(cartoonRequest);
            //break;
        }
        //selectById
        else if(code == 2){
            selectById(cartoonRequest);
            //break;
        }
        //selectAll
        else if(code == 3){
            selectAll(cartoonRequest);
            //break;
        }
        else if(code == 0 ){
            cout<<"退出成功!"<<endl;
            break;
        }
        else{
            cout<<"请求码非法,重新输入!"<<endl;
            continue;
        }
        string data;
        cartoonRequest.SerializeToString(&data);
        CHECK(client.Send(data));
        data.clear();
        CHECK(client.Recv(data));
        CartoonResponse cartoonResponse;
        cartoonResponse.ParseFromString(data);
        cout<<cartoonResponse.res()<<endl;
        if(code == 2 || code == 3){
            for(int i=0;i<cartoonResponse.mutable_cartoon()->cartoonlist_size();++i){
                Cartoon cartoon = cartoonResponse.mutable_cartoon()->cartoonlist(i);
                cout<<"动画Id:"<<cartoon.id()<<endl;
                cout<<"动画名称:"<<cartoon.name()<<endl;
                cout<<"动画出品公司:"<<cartoon.company()<<endl;
                cout<<"***************************************"<<endl;
            }
        }
    }
    client.Close();
    return 0;
}

server.cpp

//
// Created by Aaj on 2021/8/6.
//

#include"src/Cartoon.pb.h"
#include "src/MyTcpSocket.h"

#include<iostream>
#include <fstream>
#include <string>

using namespace std;

void addCartoon(CartoonRequest &cartoonRequest){
    fstream output("CartoonInf",ios::out | ios::binary | ios::app); //app代表追加方式写入
    CartoonList cartoonList = cartoonRequest.cartoon();
    if(!cartoonList.SerializePartialToOstream(&output)){
        cerr<<"无法写入CartoonInf"<<endl;
        exit(-1);
    }
    return ;
}

void selectByCartoonId(int Id,CartoonResponse &cartoonResponse){
    CartoonList cartoonList;
    fstream input("CartoonInf",ios::in | ios::binary);
    if(input && !cartoonList.ParseFromIstream(&input)){
        cerr<<"无法读取CartoonInf"<<endl;
        exit(-1);
    }


//    /*这里暂时就直接显示出来*/
//    cout<<"*selectByCartoonId*"<<endl;
//    for(int i=0;i<cartoonList.cartoonlist_size();++i){
//        Cartoon cartoon = cartoonList.cartoonlist(i);
//        cout<<"动画Id:"<<cartoon.id()<<endl;
//        cout<<"动画名称:"<<cartoon.name()<<endl;
//        cout<<"动画出品公司:"<<cartoon.company()<<endl;
//        cout<<"***************************************"<<endl;
//    }

    for(int i=0;i<cartoonList.cartoonlist_size();++i){
        if(cartoonList.cartoonlist(i).id() == Id){
            Cartoon* cartoon = cartoonResponse.mutable_cartoon()->add_cartoonlist();
            cartoon->set_id(cartoonList.cartoonlist(i).id());
            cartoon->set_name(cartoonList.cartoonlist(i).name());
            cartoon->set_company(cartoonList.cartoonlist(i).company());
            break;
        }
    }
    return ;
}

void selectAllCartoon(CartoonResponse &cartoonResponse){
    CartoonList cartoonList;
    fstream input("CartoonInf",ios::in | ios::binary);
    if(input && !cartoonList.ParseFromIstream(&input)) {
        cerr << "无法读取CartoonInf"<<endl;
        exit(-1);
    }

//    /*这里暂时就直接显示出来*/
//    cout<<"*selectAllCartoon*"<<endl;
//    for(int i=0;i<cartoonList.cartoonlist_size();++i){
//        Cartoon cartoon = cartoonList.cartoonlist(i);
//        cout<<"动画Id:"<<cartoon.id()<<endl;
//        cout<<"动画名称:"<<cartoon.name()<<endl;
//        cout<<"动画出品公司:"<<cartoon.company()<<endl;
//        cout<<"***************************************"<<endl;
//    }

    for(int i=0;i<cartoonList.cartoonlist_size();++i){
        Cartoon* cartoon = cartoonResponse.mutable_cartoon()->add_cartoonlist();
        cartoon->set_id(cartoonList.cartoonlist(i).id());
        cartoon->set_name(cartoonList.cartoonlist(i).name());
        cartoon->set_company(cartoonList.cartoonlist(i).company());
    }
    return ;
}

int main(){
    //如果没有CartoonInf则先创建一个
    if(!fopen("CartoonInf","r")){
        fopen("CartoonInf","w");
    }
    string ip = to_string(INADDR_ANY);
    uint16_t port = 9999;
    MyTcpSocket service;
    CHECK(service.Socket());
    CHECK(service.Bind(ip,port));
    CHECK(service.Listen());


    MyTcpSocket clisock;
    struct sockaddr_in cliaddr;
    CHECK(service.Accept(clisock, &cliaddr));
    //selectAllCartoon(); //测试用

    while(1){
        string data;
        CartoonRequest cartoonRequest;
        CHECK(clisock.Recv(data));
        cartoonRequest.ParseFromString(data);
        CartoonResponse cartoonResponse;
        if(cartoonRequest.query() == 1){
            cout<<"addCartoon"<<endl;
            addCartoon(cartoonRequest);
            cartoonResponse.set_res("addCartoon success!");
        }
        else if(cartoonRequest.query() == 2){
            cout<<"selectByCartoonId"<<endl;
            selectByCartoonId(cartoonRequest.selectbyid(),cartoonResponse);
            if(cartoonResponse.cartoon().cartoonlist_size()==0){
                cartoonResponse.set_res("Cartoons don't exist");
            }
            else{
                cartoonResponse.set_res("selectByCartoonId success!");
            }
        }
        else if(cartoonRequest.query() == 3){
            cout<<"selectAllCartoon"<<endl;
            selectAllCartoon(cartoonResponse);
            if(cartoonResponse.cartoon().cartoonlist_size()==0){
                cartoonResponse.set_res("Cartoons don't exist");
            }
            else{
                cartoonResponse.set_res("selectAllCartoon success!");
            }
        }
        else{
            cartoonResponse.set_res("query error!");
        }
        cartoonResponse.SerializeToString(&data);
        CHECK(clisock.Send(data));
    }
    service.Close();
    return 0;
}

CMakeList.tx:

cmake_minimum_required(VERSION 3.0)

project(myProtocolBuf)
set(CMAKE_CXX_STANDARD 11)

find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
link_libraries(${Protobuf_LIBRARY})

FILE(GLOB SRC ./src/*.cc ./src/*.h)

add_executable(client client.cpp ${SRC})
add_executable(server server.cpp ${SRC})

编译案例

进入src目录,编译生成相应的”.pb.cc“和“.pb.h”文件

protoc --cpp_out=. ./Cartoon.proto

进入build目录,执行命令:

cmake ..
make

运行服务端与客户端并查看结果;
在这里插入图片描述

参考博文:使用socket和TCP网络协议实现的客户端和服务端示例,采用谷歌的protobuf数据传输协议(类似json),部署在Linux上


拓展博文:
嵌入式大杂烩 | Protobuf:一种更小、更快、更高效的协议

干货 | protobuf-c之嵌入式平台使用

干货 | 项目乏力?nanopb助你一臂之力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/679832.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

01.4进程原理和系统调用--->经典的CFS调度器

进程的一些正常状态 什么是进程 操作系统作为硬件的使用层&#xff0c;提供使用硬件资源的能力&#xff0c;进程作为操作系统使用层&#xff0c; 提供使用操作系统抽象出的资源层的能力。 进程&#xff1a;是指计算机中已运行的程序。进程本身不是基本的运行单位&#xff0c;…

【微服务】springboot 通用限流方案设计与实现

目录 一、背景 二、限流概述 2.1 dubbo 服务治理模式 2.1.1 dubbo框架级限流 2.1.2 线程池设置 2.1.3 集成第三方组件 2.2 springcloud 服务治理模式 2.2.1 hystrix 2.2.2 sentinel 2.3 网关层限流 三、常用限流策略 3.1 限流常用的算法 3.1.1 令牌桶算法 3.1.2 …

2023最新Java面试八股文汇总(五十万字总结版)

写在前面 今年的疫情&#xff0c;让招聘面试变得雪上加霜。已经有不少大厂&#xff0c;如腾讯、字节跳动的招聘名额明显减少&#xff0c;面试门槛却一再拔高&#xff0c;如果不用心准备&#xff0c;很可能就被面试官怼得哑口无言&#xff0c;甚至失去了难得的机会。 现如今&a…

<Linux开发>驱动开发 -之- Linux RTC 驱动

&#xff1c;Linux开发&#xff1e;驱动开发 -之- Linux RTC 驱动 交叉编译环境搭建&#xff1a; &#xff1c;Linux开发&#xff1e; linux开发工具-之-交叉编译环境搭建 uboot移植可参考以下&#xff1a; &#xff1c;Linux开发&#xff1e; -之-系统移植 uboot移植过程详细…

Dubbo【 Dubbo概念(什么是分布式系统、什么是RPC、核心组件、Zookeeper注册中心 )】(一)-全面详解(学习总结---从入门到深化)

目录 Dubbo概念_什么是分布式系统 什么是分布式 Dubbo概念_什么是RPC Dubbo概念_简介 Dubbo概念_核心组件 Dubbo配置开发环境_Zookeeper注册中心 Dubbo配置开发环境_管理控制台 Dubbo入门案例_需求介绍 Dubbo入门案例_配置开发环境 Dubbo入门案例_服务生产者配置…

VLC-QT源码编译(Windows10+VS2020+MSVC20019+QT5.15)

参考VLC-Qt的编译与使用 windows10VS2019qt5.15 下载源码 VLC-QT https://github.com/vlc-qt/vlc-qt可以通过git或者直接下载ZIP文件&#xff0c;但是里面的libvlc-header和packaging没有下载下来&#xff0c;需要再自行下载。 VLC https://download.videolan.org/vlc/las…

行为型模式--备忘录模式

目录 概述 结构 案例实现 “白箱”备忘录模式 总结&#xff1a; “黑箱”备忘录模式 优缺点 优点&#xff1a; 缺点&#xff1a; 使用场景 概述 又叫快照模式&#xff0c;在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象之外保存这…

zeromq的学习笔记

ctx_t 在创建ctx_t时&#xff0c;会设置以下参数 _io_thread_count io线程数默认是1 _max_sockets最大socket数是1023 _starting标识设置为true,此时socket还没有创建 _terminating设置为false,在调用zmq_ctx_term时该标识会设置为true _tag设置为ZMQ_CTX_TAG_VALUE_GOOD&…

mySql和VSC++

确认主机服务里的mysql服务已打开 使用组合键“winR”运行“services.msc”&#xff0c;进入本地服务窗口&#xff1b; 2.进入本地服务窗口后&#xff0c;在右侧服务列表中&#xff0c;查找到“ mysql ”服务选项&#xff1b; 3.查找到mysql服务选项后&#xff0c;双击打开mysq…

C++ 面向对象(3)——重载运算符和重载函数

C 允许在同一作用域中的某个函数和运算符指定多个定义&#xff0c;分别称为函数重载和运算符重载。 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明&#xff0c;但是它们的参数列表和定义&#xff08;实现&#xff09;不相同。 当您调用一个重…

FDM3D打印系列——3、常用打印材料介绍

大家好&#xff0c;我是阿赵。 FDM3D打印机一般都可以支持多种打印材料的&#xff0c;下面来介绍一下几种常用的打印材料 一、PLA 使用FDM打印&#xff0c;最常见的材料就是PLA了 PLA&#xff08;Polylactic acid&#xff09;&#xff0c;中文名为生物降解塑料聚乳酸&#…

网络安全面试题,渗透测试面试总结

1.什么是WebShell? WebShell就是以asp、php、jsp或者cgi等网页文件形式存在的─种命令执行环境&#xff0c;也可以将其称做为─种网页后门。黑客在入侵了─个网站后&#xff0c;通常会将这些asp或php后门文件与网站服务器WEB目录下正常的网页文件混在─起&#xff0c;然后就可…

【Vue3】生命周期(钩子)函数

在 Vue 3 中&#xff0c;生命周期函数已经被重新设计为钩子函数&#xff0c;并且与 Vue 2 中的生命周期函数有所不同&#xff0c;可以在 setup 函数中使用 onXXX 形式的钩子函数来执行对应的操作。以下是选项式 API 和组合式 API 中常用的几个钩子函数对比&#xff1a; beforeC…

【Java】PriorityQueue--优先级队列

目录 一、优先级队列 &#xff08;1&#xff09;概念 二、优先级队列的模拟实现 &#xff08;1&#xff09;堆的概念 &#xff08;2&#xff09;堆的存储方式 &#xff08;3&#xff09;堆的创建 堆向下调整 &#xff08;4&#xff09;堆的插入与删除 堆的插入 堆的…

Kubernetes(k8s)容器编排组件介绍

目录 1 整体架构1.1 Master 架构1.2 Node 架构 2 k8s部署组件介绍2.1 K8s 集群架构图2.2 k8s控制组件2.2.1 控制平面2.2.2 kube-apiserver2.2.3 kube-scheduler2.2.4 kube-controller-manager2.2.5 etcd 2.3 k8s运行组件2.3.1 k8s节点2.3.2 容器集2.3.3 容器运行时引擎2.3.4 ku…

机试复试准备中--梦校真题

一、矩阵转置二、统计单词写法一&#xff1a;读取一整行写法二&#xff1a;依次读入每一个单词 三、二叉排序树&#xff08;DFS&#xff09;四、IP地址五、特殊排序六、ab&#xff08;高精度加法&#xff09;七、奇偶校验八、最大的两个数九、二叉树遍历(DFS)十、成绩排序十一、…

【C++学习】C++入门 | 引用 | 引用的底层原理 | auto关键字 | 范围for(语法糖)

写在前面&#xff1a; 上一篇文章我介绍了缺省参数和函数重载&#xff0c; 探究了C为什么能够支持函数重载而C语言不能&#xff0c; 这里是传送门&#xff0c;有兴趣可以去看看&#xff1a;http://t.csdn.cn/29ycJ 这篇我们继续来学习C的基础知识。 目录 写在前面&#x…

正交编码与正交沃尔什函数详解

本专栏包含信息论与编码的核心知识&#xff0c;按知识点组织&#xff0c;可作为教学或学习的参考。markdown版本已归档至【Github仓库&#xff1a;https://github.com/timerring/information-theory 】或者公众号【AIShareLab】回复 信息论 获取。 文章目录 正交编码正交编码的…

Spring Boot 集成 WebSocket 实现服务端推送消息到客户端

假设有这样一个场景&#xff1a;服务端的资源经常在更新&#xff0c;客户端需要尽量及时地了解到这些更新发生后展示给用户&#xff0c;如果是 HTTP 1.1&#xff0c;通常会开启 ajax 请求询问服务端是否有更新&#xff0c;通过定时器反复轮询服务端响应的资源是否有更新。 在长…

css基础(二)

目录 1. CSS 的复合选择器 1.1 什么是复合选择器 1.2 后代选择器(重要&#xff09; 1.3 子选择器(重要&#xff09; 1.4 并集选择器(重要&#xff09; 1.5 伪类选择器 1.6 链接伪类选择器 1.7 :focus伪类选择器 1.8 复合选择器总结 二、 CSS 的元素显示模式 2.1什么是元素显示模…