Protobuf的C++使用笔记

简介

在网络编程中, 数据高效传输离不开数据的序列化. 直接编码不易于扩展, 而xml的方式太过于笨重, json的方式速度有点慢, protobuf是一个合理的方式, 把数据序列成二进制, 然后反序列化成原来的数据.

编译出protobuf文件

假设有一个msg.proto文件, 那么需要执行:

protoc msg.proto --cpp_out=./

当前目录会多出2个文件, 分别是msg.pb.hmsg.pb.cc, 这是编码解码的文件.

消息格式

使用protobuf3的语法格式, 结合数据嵌套, 最后要发送的是 FinalMsg数据.

syntax = "proto3";

package Data;

message Vector {
  int32 vx = 1;
  int32 vy = 2;
  int32 vz = 3;
}

message Point {
  float px = 1;
  float py = 2;
  float pz = 3;
}

enum MsgType {
  Vec = 0;
  Pnt = 1;
}

message FinalMsg {
  string username = 1;
  string password = 2;
  MsgType msgtype = 3;
  Vector vector = 4;
  Point point = 5;
}

客户端

g++ 1.cpp -o 1 data.pb.cc -lprotobuf -pthread -lboost_system

代码:

#include <iostream>
#include <boost/asio.hpp>
#include <string>
#include "data.pb.h"

std::string raw_ip_addr = "127.0.0.1";
unsigned short port_num = 6769;

int main() {
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::endpoint 
        ep(boost::asio::ip::address::from_string(raw_ip_addr), port_num);
    boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
    sock.connect(ep);

    Data::FinalMsg finalMsg;
    finalMsg.set_username(std::string("Tiger"));
    finalMsg.set_password(std::string("123456"));
    finalMsg.set_msgtype(Data::MsgType::Vec);
    
    auto vec = finalMsg.mutable_vector();
    vec->set_vx(1);
    vec->set_vy(2);
    vec->set_vz(3);

    auto pnt = finalMsg.mutable_point();
    pnt->set_px(1.1);
    pnt->set_py(2.5);
    pnt->set_pz(3.2);

    std::string msg;
    finalMsg.SerializeToString(&msg);

    if (boost::asio::write(sock, boost::asio::buffer(msg)) < 0) {
        std::cerr << "boost::asio::write() error\n";
        return -1;
    }
    std::cout << "send successfully\n";

    return 0;
}

服务器

g++ 2.cpp -o 2 data.pb.cc -lprotobuf -pthread -lboost_system
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include "data.pb.h"

size_t SIZE = 1024;
unsigned short port_num = 6769;

int main() {
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::endpoint 
        ep(boost::asio::ip::address_v4::any(), port_num);
    boost::asio::ip::tcp::acceptor acceptor(ioc, ep.protocol());
    acceptor.bind(ep);
    acceptor.listen(5);  // 最多排列5个

    boost::asio::ip::tcp::socket sock(ioc);
    acceptor.accept(sock);

    Data::FinalMsg finalMsg;
    std::string msg;
    boost::system::error_code error;
    try {
        char cmsg[SIZE];
        SIZE = boost::asio::read(sock, boost::asio::buffer(cmsg, SIZE), error);
        msg = std::move(std::string(cmsg, SIZE));
        if (error != boost::asio::error::eof) {
            throw boost::system::system_error(error);
        }
        finalMsg.ParseFromString(msg);  // 解析数据
    } catch (std::exception e) {
        std::cout << "recv error: " << e.what() << std::endl;
        sock.close();
        return -1;
    }
    sock.close();

    std::cout << "username: " << finalMsg.username() << std::endl;
    std::cout << "password:" << finalMsg.password() << std::endl;
    std::cout << "enum type: " << finalMsg.msgtype() << std::endl;
    
    auto vec = finalMsg.vector();
    auto pnt = finalMsg.point();

    std::cout << "vector, " << "x = " << vec.vx() << ", y = " << vec.vy() << ", z = " << vec.vz() << std::endl;
    std::cout << "point, " << "x = " << pnt.px() << ", y = " << pnt.py() << ", z = " << pnt.pz() << std::endl;

    return 0;
}

输出结果:

username: Tiger
password:123456
enum type: 0
vector, x = 1, y = 2, z = 3
point, x = 1.1, y = 2.5, z = 3.2

参考文档

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值