当前位置: 首页 > article >正文

protobuf: 网络版通讯录

1.客户端

1.协议约定

2..proto文件编写

syntax = "proto3";
package Add_http_contacts;

message AddContactRequest
{
    string name = 1;
    int32 age = 2;
    //里面是电话的字段和类型
    message Phone
    {
        string num = 1;
        enum PhoneType 
        {
        MP = 0; // 移动电话
        TEL = 1; // 固定电话
        }
        PhoneType type = 2;
    }
    repeated Phone phone = 3;
}

message AddContactResponse
{
  bool success = 1;
  string error_desc = 2;
  string uuid = 3;
}

3.Makefile

server:*.cc
    g++ -o $@ $^ -std=c++11 -lpthread -lprotobuf    //-lpthread是因为引入了第三方 httplib库
.PHONY:clean
clean:
    rm -rf server

4.异常类

#include <iostream>
#include <string>

class ContactException
{
private:
    std::string _error;
public:
    ContactException(std::string err = "a problem")
    :_error(err)
    {}
    std::string What() const
    {
        return _error;
    }
};

5.client.cc

#include <iostream>
#include "httplib.h"
#include "ContactException.hpp"
#include "add_contact.pb.h"

using std::cerr;
using std::cin;
using std::cout;
using std::endl;

using namespace httplib;

#define CONTACT_HOST "82.157.3.118"
#define CONTACT_PORT 8888

void menu()
{
    std::cout << "-----------------------------------------------------" << std::endl
              << "--------------- 请选择对通讯录的操作 ----------------" << std::endl
              << "------------------ 1、新增联系⼈ --------------------" << std::endl
              << "------------------ 2、删除联系⼈ --------------------" << std::endl
              << "------------------ 3、查看联系⼈列表 ----------------" << std::endl
              << "------------------ 4、查看联系⼈详细信息 ------------" << std::endl
              << "------------------ 0、退出 --------------------------" << std::endl
              << "-----------------------------------------------------" << std::endl;
}

void BuildAddContactRequest(Add_http_contacts::AddContactRequest *req)
{
    std::cout << "--------------新增联系人--------------" << std::endl;
    std::cout << "请输入联系人的姓名: ";
    std::string name;
    getline(std::cin, name);
    req->set_name(name);

    std::cout << "请输入联系人的年龄: ";
    int age;
    std::cin >> age;
    req->set_age(age);
    std::cin.ignore(256, '\n'); // 清除缓冲区所有内容,直到'\n',把'\n'删除后结束,或者清除256个字节直接结束

    for (int i = 0;; i++)
    {
        std::cout << "请输入联系人的电话: " << i + 1 << "(只输入回车完成电话新增): ";
        std::string number;
        getline(std::cin, number);
        if (number.empty())
            break;
        // 接收add_phone返回的phone对象
        Add_http_contacts::AddContactRequest_Phone *phone = req->add_phone();
        phone->set_num(number);

        std::cout << "请输入联系人的电话类型:(0.移动电话, 1.固定电话): ";
        int type;
        std::cin >> type;
        std::cin.ignore(256, '\n');
        switch (type)
        {
        case 0:
            phone->set_type(Add_http_contacts::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP);
            break;
        case 1:
            phone->set_type(Add_http_contacts::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL);
            break;
        default:
            std::cout << "输入有误, 请重新根据提示输入" << std::endl;
            break;
        }
    }
}
void AddContact()
{
    Client client(CONTACT_HOST, CONTACT_PORT);
    // 构建req对象
    Add_http_contacts::AddContactRequest req;
    BuildAddContactRequest(&req);
    // 序列化
    std::string req_str;
    if (!req.SerializeToString(&req_str))
    {
        std::string errstr = "AddContact时,序列化失败";
        throw ContactException(errstr);
    }
    // 发起post请求
    auto post = client.Post("/contacts/add", req_str, "application/protobuf");
    if (!post) 
    {
        std::string err_str = "/contacts/add 链接失败,错误原因: ";
        err_str.append(httplib::to_string(post.error()));
        throw ContactException(err_str);
    }

    // 反序列化rsp
    Add_http_contacts::AddContactResponse resp;
    bool parse = resp.ParseFromString(post->body);
    if (post->status != 200 && !parse)
    {
        std::string err_str = "调用 /contact/add 失败 ";
        err_str.append(std::to_string(post->status))
            .append("(")
            .append(post->reason)
            .append(")");
        throw ContactException(err_str);
    }
    else if (post->status != 200)
    {
        std::string err_str = "调用 /contact/add 失败 ";
        err_str.append(std::to_string(post->status))
            .append("(")
            .append(post->reason)
            .append(")")
            .append("错误原因: ")
            .append(resp.error_desc());
        throw ContactException(err_str);
    }
    else if (!resp.success())
    {
        std::string err_str = "调用 /contact/add 结果异常 ";
        err_str.append("异常原因: ")
            .append(resp.error_desc());
        throw ContactException(err_str);
    }
    // 打印结果
    cout << "新增联系人成功" << "联系人id: " << resp.uuid() << endl;
}

int main()
{
    enum Option
    {
        Quit = 0,
        Add,
        Del,
        Findone,
        Findall
    };
    menu();
    cout << "---->请选择: ";
    int choose;
    cin >> choose;
    cin.ignore(256, '\n');
    try
    {
        switch (choose)
        {
        case Option::Quit:
            cout << "---->退出" << endl;
            return 1;
        case Option::Add:
            cout << "---->新增联系人" << endl;
            AddContact();
            break;
        case Option::Del:
            cout << "---->暂未实现" << endl;
            break;
        case Option::Findone:
            cout << "---->暂未实现" << endl;
            break;
        case Option::Findall:
            cout << "---->暂未实现" << endl;
            break;
        default:
            cout << "---->选择有误,请重新选择" << endl;
            break;
        };
    }
    catch (const ContactException &e)
    {
        cout << "--->出现错误, 错误原因是: " << e.What() << endl;
    }
}

2.服务端

1.UUIDUtil类

#pragma once
#include <iostream>
#include <sstream>
#include <string>
#include <random>
#include <iomanip>
#include <atomic>

namespace ns_uuid
{
    class UUIDUtil
    {
    public:
        static std::string uuid()
        {
            // 生成机器随机数对象
            std::random_device rd;
            // 用mt算法,以机器随机数对象为种子生成伪随机数对象
            std::mt19937 generator(rd());
            // 构造限定数据范围的伪随机数
            std::uniform_int_distribution<int> distribution(0, 255);
            // ⽣成8个随机数,按照特定格式组织成为16进制数字字符的字符串
            std::stringstream ss;
            for (int i = 0; i < 8; i++)
            {
                if(i == 4 || i == 6) ss << "-";
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
            }
            ss << "-";
            // 定义⼀个8字节序号,逐字节组织成为16进制数字字符的字符串
            static std::atomic<int> a(0);
            // size_t 64个bit位
            size_t cur = a.fetch_add(1);
            for (int i = 7; i >= 0; i--)
            {
                if(i == 5) ss << "-";
                ss << std::setw(2) << std::setfill('0') << std::hex << ((cur >> (i*8)) & 0xFF);
            }

            return ss.str();
        }
    };
}

2.server.cc

#include <iostream>
#include "add_contact.pb.h"
#include "ContactException.hpp"
#include <string>
#include "httplib.h"
#include "UUIDUtil.hpp"

using namespace httplib;
using namespace ns_uuid;
using std::cout;
using std::endl;



void PrintContacts(Add_http_contacts::AddContactRequest& request)
{
        std::cout << "联系人姓名:" << request.name() << std::endl;
        std::cout << "联系人年龄:" << request.age() << std::endl;
        for(int j = 0;j<request.phone_size();j++)
        {
            const Add_http_contacts::AddContactRequest_Phone phone = request.phone(j);
            std::cout << "联系人电话" << j+1  << ": "<< phone.num() << "   ";
            std::cout << "电话类型"  << ": ("<< AddContactRequest_Phone_PhoneType_Name(phone.type()) << ")" << std::endl; 
        }
}
int main()
{
    Server server;
    server.Post("/contacts/add", [](const Request &req, Response &resp)
        {
            // 反序列化request : req.body
            Add_http_contacts::AddContactRequest request;
            Add_http_contacts::AddContactResponse response;
            try
            {
                if (!request.ParseFromString(req.body))
                {
                    throw ContactException("request反序列化失败");
                }

                //新增联系人,持久化存储——————这里只是实现打印
                PrintContacts(request);

                // 序列化response
                response.set_success(true);
                
                response.set_uuid(UUIDUtil::uuid());
                std::string resp_str;
                if (!response.SerializeToString(&resp_str))
                {
                    throw ContactException("response序列化失败");
                }
                resp.status = 200;
                resp.body = resp_str;
                resp.set_header("Content-Type", "application/protobuf");
            }
            catch (const ContactException &e)
            {
                resp.status = 500;
                response.set_success(false);
                response.set_error_desc(e.What());
                std::string resp_str;
                if (response.SerializeToString(&resp_str))
                {
                    resp.body = resp_str;
                    resp.set_header("Content-Type", "application/protobuf");
                }
            }
        });

    server.listen("0.0.0.0", 8888);
    return 0;
}

3.总结

序列化协议

通⽤性

格式

可读性

序列化⼤

序列化性能

JSON

通⽤(json、

xml已成为多种

⾏业标准的编

写⼯具

⽂本格式

轻量(使

⽤键值对

⽅式,压

缩了⼀定

的数据空

XML

通用

⽂本格式

重量(数

据冗余,

因为需要

成对的闭

合标签

ProtoBuf

独⽴

(Protobuf只

是Google公司

内部的⼯具)

⼆进制格式

差(只能

反序列化

后得到真

正可读的

数据)

轻量(⽐

JSON更轻

量,传输

起来带宽

和速度会

有优化

⼩结:
  1. XML、JSON、ProtoBuf都具有数据结构化和数据序列化的能⼒。
  2. XML、JSON更注重数据结构化,关注可读性和语义表达能⼒。ProtoBuf更注重数据序列化,关注
效率、空间、速度,可读性差,语义表达能⼒不⾜,为保证极致的效率,会舍弃⼀部分元信息。
3. ProtoBuf的应⽤ 场景更为明确,XML、JSON的应⽤ 场景更为丰富。
protobuf 和 Json 的实验对比:
  • 编解码性能:ProtoBuf的编码解码性能,⽐JSON⾼出2-4倍。
  • 内存占⽤:ProtoBuf的内存278,⽽JSON到达567,ProtoBuf的内存占⽤只有JSON的1/2。
注:以上结论的数据只是根据该项实验得出。因为受不同的字段类型、字段个数等影响,测出的数据会有所差异。

http://www.kler.cn/a/510564.html

相关文章:

  • 【线性代数】行列式的概念
  • MySQL课堂练习(多表查询练习)
  • springboot医院信管系统
  • 【深度学习】Huber Loss详解
  • vscode离线安装插件--终极解决方案
  • python爬虫报错日记
  • Ubuntu 24.04 LTS 服务器折腾集
  • Qt常用控件之输入类控件
  • 论文阅读 AutoDev: Automated AI-Driven Development
  • AT8870单通道直流电机驱动芯片
  • 算法竞赛里面的STL——堆和priority_queue
  • ThreadLocal如何实现的
  • 【HarmonyOS之旅】基于ArkTS开发(二) -> UI开发之常见布局
  • 硬件学习笔记--33 GB/T 17215.211相关内容介绍
  • 双序列双指针
  • Python(十七)excel指定列自动翻译成英文
  • 多学科视角下探索开源Github、Git初步学习
  • 企业邮箱iRedMail搭建
  • 一岁征程:学习、挑战与成长
  • wireshark工具简介
  • 如何引导LabVIEW项目相关方合理参与项目?
  • 【2024年华为OD机试】(C卷,100分)- 求满足条件的最长子串的长度 (Java JS PythonC/C++)
  • Spring篇 解决因为Bean的循环依赖——理论篇
  • 基于springboot的教师人事档案管理系统
  • vue移动端统计分析echarts开发小结(多提示框渲染,下载适配,数据量大的时候不分页崩溃等)
  • 用LSTM模型预测股价的例子(1)