protobuf: 网络版通讯录

打印 上一主题 下一主题

主题 1018|帖子 1018|积分 3054

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1.客户端

1.协议约定


2..proto文件编写

  1. syntax = "proto3";
  2. package Add_http_contacts;
  3. message AddContactRequest
  4. {
  5.     string name = 1;
  6.     int32 age = 2;
  7.     //里面是电话的字段和类型
  8.     message Phone
  9.     {
  10.         string num = 1;
  11.         enum PhoneType
  12.         {
  13.         MP = 0; // 移动电话
  14.         TEL = 1; // 固定电话
  15.         }
  16.         PhoneType type = 2;
  17.     }
  18.     repeated Phone phone = 3;
  19. }
  20. message AddContactResponse
  21. {
  22.   bool success = 1;
  23.   string error_desc = 2;
  24.   string uuid = 3;
  25. }
复制代码
3.Makefile

  1. server:*.cc
  2.     g++ -o $@ $^ -std=c++11 -lpthread -lprotobuf    //-lpthread是因为引入了第三方 httplib库
  3. .PHONY:clean
  4. clean:
  5.     rm -rf server
复制代码
4.异常类

  1. #include <iostream>
  2. #include <string>
  3. class ContactException
  4. {
  5. private:
  6.     std::string _error;
  7. public:
  8.     ContactException(std::string err = "a problem")
  9.     :_error(err)
  10.     {}
  11.     std::string What() const
  12.     {
  13.         return _error;
  14.     }
  15. };
复制代码
5.client.cc

  1. #include <iostream>
  2. #include "httplib.h"
  3. #include "ContactException.hpp"
  4. #include "add_contact.pb.h"
  5. using std::cerr;
  6. using std::cin;
  7. using std::cout;
  8. using std::endl;
  9. using namespace httplib;
  10. #define CONTACT_HOST "82.157.3.118"
  11. #define CONTACT_PORT 8888
  12. void menu()
  13. {
  14.     std::cout << "-----------------------------------------------------" << std::endl
  15.               << "--------------- 请选择对通讯录的操作 ----------------" << std::endl
  16.               << "------------------ 1、新增联系⼈ --------------------" << std::endl
  17.               << "------------------ 2、删除联系⼈ --------------------" << std::endl
  18.               << "------------------ 3、查看联系⼈列表 ----------------" << std::endl
  19.               << "------------------ 4、查看联系⼈详细信息 ------------" << std::endl
  20.               << "------------------ 0、退出 --------------------------" << std::endl
  21.               << "-----------------------------------------------------" << std::endl;
  22. }
  23. void BuildAddContactRequest(Add_http_contacts::AddContactRequest *req)
  24. {
  25.     std::cout << "--------------新增联系人--------------" << std::endl;
  26.     std::cout << "请输入联系人的姓名: ";
  27.     std::string name;
  28.     getline(std::cin, name);
  29.     req->set_name(name);
  30.     std::cout << "请输入联系人的年龄: ";
  31.     int age;
  32.     std::cin >> age;
  33.     req->set_age(age);
  34.     std::cin.ignore(256, '\n'); // 清除缓冲区所有内容,直到'\n',把'\n'删除后结束,或者清除256个字节直接结束
  35.     for (int i = 0;; i++)
  36.     {
  37.         std::cout << "请输入联系人的电话: " << i + 1 << "(只输入回车完成电话新增): ";
  38.         std::string number;
  39.         getline(std::cin, number);
  40.         if (number.empty())
  41.             break;
  42.         // 接收add_phone返回的phone对象
  43.         Add_http_contacts::AddContactRequest_Phone *phone = req->add_phone();
  44.         phone->set_num(number);
  45.         std::cout << "请输入联系人的电话类型:(0.移动电话, 1.固定电话): ";
  46.         int type;
  47.         std::cin >> type;
  48.         std::cin.ignore(256, '\n');
  49.         switch (type)
  50.         {
  51.         case 0:
  52.             phone->set_type(Add_http_contacts::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP);
  53.             break;
  54.         case 1:
  55.             phone->set_type(Add_http_contacts::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL);
  56.             break;
  57.         default:
  58.             std::cout << "输入有误, 请重新根据提示输入" << std::endl;
  59.             break;
  60.         }
  61.     }
  62. }
  63. void AddContact()
  64. {
  65.     Client client(CONTACT_HOST, CONTACT_PORT);
  66.     // 构建req对象
  67.     Add_http_contacts::AddContactRequest req;
  68.     BuildAddContactRequest(&req);
  69.     // 序列化
  70.     std::string req_str;
  71.     if (!req.SerializeToString(&req_str))
  72.     {
  73.         std::string errstr = "AddContact时,序列化失败";
  74.         throw ContactException(errstr);
  75.     }
  76.     // 发起post请求
  77.     auto post = client.Post("/contacts/add", req_str, "application/protobuf");
  78.     if (!post)
  79.     {
  80.         std::string err_str = "/contacts/add 链接失败,错误原因: ";
  81.         err_str.append(httplib::to_string(post.error()));
  82.         throw ContactException(err_str);
  83.     }
  84.     // 反序列化rsp
  85.     Add_http_contacts::AddContactResponse resp;
  86.     bool parse = resp.ParseFromString(post->body);
  87.     if (post->status != 200 && !parse)
  88.     {
  89.         std::string err_str = "调用 /contact/add 失败 ";
  90.         err_str.append(std::to_string(post->status))
  91.             .append("(")
  92.             .append(post->reason)
  93.             .append(")");
  94.         throw ContactException(err_str);
  95.     }
  96.     else if (post->status != 200)
  97.     {
  98.         std::string err_str = "调用 /contact/add 失败 ";
  99.         err_str.append(std::to_string(post->status))
  100.             .append("(")
  101.             .append(post->reason)
  102.             .append(")")
  103.             .append("错误原因: ")
  104.             .append(resp.error_desc());
  105.         throw ContactException(err_str);
  106.     }
  107.     else if (!resp.success())
  108.     {
  109.         std::string err_str = "调用 /contact/add 结果异常 ";
  110.         err_str.append("异常原因: ")
  111.             .append(resp.error_desc());
  112.         throw ContactException(err_str);
  113.     }
  114.     // 打印结果
  115.     cout << "新增联系人成功" << "联系人id: " << resp.uuid() << endl;
  116. }
  117. int main()
  118. {
  119.     enum Option
  120.     {
  121.         Quit = 0,
  122.         Add,
  123.         Del,
  124.         Findone,
  125.         Findall
  126.     };
  127.     menu();
  128.     cout << "---->请选择: ";
  129.     int choose;
  130.     cin >> choose;
  131.     cin.ignore(256, '\n');
  132.     try
  133.     {
  134.         switch (choose)
  135.         {
  136.         case Option::Quit:
  137.             cout << "---->退出" << endl;
  138.             return 1;
  139.         case Option::Add:
  140.             cout << "---->新增联系人" << endl;
  141.             AddContact();
  142.             break;
  143.         case Option::Del:
  144.             cout << "---->暂未实现" << endl;
  145.             break;
  146.         case Option::Findone:
  147.             cout << "---->暂未实现" << endl;
  148.             break;
  149.         case Option::Findall:
  150.             cout << "---->暂未实现" << endl;
  151.             break;
  152.         default:
  153.             cout << "---->选择有误,请重新选择" << endl;
  154.             break;
  155.         };
  156.     }
  157.     catch (const ContactException &e)
  158.     {
  159.         cout << "--->出现错误, 错误原因是: " << e.What() << endl;
  160.     }
  161. }
复制代码
2.服务端

1.UUIDUtil类

  1. #pragma once
  2. #include <iostream>
  3. #include <sstream>
  4. #include <string>
  5. #include <random>
  6. #include <iomanip>
  7. #include <atomic>
  8. namespace ns_uuid
  9. {
  10.     class UUIDUtil
  11.     {
  12.     public:
  13.         static std::string uuid()
  14.         {
  15.             // 生成机器随机数对象
  16.             std::random_device rd;
  17.             // 用mt算法,以机器随机数对象为种子生成伪随机数对象
  18.             std::mt19937 generator(rd());
  19.             // 构造限定数据范围的伪随机数
  20.             std::uniform_int_distribution<int> distribution(0, 255);
  21.             // ⽣成8个随机数,按照特定格式组织成为16进制数字字符的字符串
  22.             std::stringstream ss;
  23.             for (int i = 0; i < 8; i++)
  24.             {
  25.                 if(i == 4 || i == 6) ss << "-";
  26.                 ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
  27.             }
  28.             ss << "-";
  29.             // 定义⼀个8字节序号,逐字节组织成为16进制数字字符的字符串
  30.             static std::atomic<int> a(0);
  31.             // size_t 64个bit位
  32.             size_t cur = a.fetch_add(1);
  33.             for (int i = 7; i >= 0; i--)
  34.             {
  35.                 if(i == 5) ss << "-";
  36.                 ss << std::setw(2) << std::setfill('0') << std::hex << ((cur >> (i*8)) & 0xFF);
  37.             }
  38.             return ss.str();
  39.         }
  40.     };
  41. }
复制代码
2.server.cc

  1. #include <iostream>
  2. #include "add_contact.pb.h"
  3. #include "ContactException.hpp"
  4. #include <string>
  5. #include "httplib.h"
  6. #include "UUIDUtil.hpp"
  7. using namespace httplib;
  8. using namespace ns_uuid;
  9. using std::cout;
  10. using std::endl;
  11. void PrintContacts(Add_http_contacts::AddContactRequest& request)
  12. {
  13.         std::cout << "联系人姓名:" << request.name() << std::endl;
  14.         std::cout << "联系人年龄:" << request.age() << std::endl;
  15.         for(int j = 0;j<request.phone_size();j++)
  16.         {
  17.             const Add_http_contacts::AddContactRequest_Phone phone = request.phone(j);
  18.             std::cout << "联系人电话" << j+1  << ": "<< phone.num() << "   ";
  19.             std::cout << "电话类型"  << ": ("<< AddContactRequest_Phone_PhoneType_Name(phone.type()) << ")" << std::endl;
  20.         }
  21. }
  22. int main()
  23. {
  24.     Server server;
  25.     server.Post("/contacts/add", [](const Request &req, Response &resp)
  26.         {
  27.             // 反序列化request : req.body
  28.             Add_http_contacts::AddContactRequest request;
  29.             Add_http_contacts::AddContactResponse response;
  30.             try
  31.             {
  32.                 if (!request.ParseFromString(req.body))
  33.                 {
  34.                     throw ContactException("request反序列化失败");
  35.                 }
  36.                 //新增联系人,持久化存储——————这里只是实现打印
  37.                 PrintContacts(request);
  38.                 // 序列化response
  39.                 response.set_success(true);
  40.                
  41.                 response.set_uuid(UUIDUtil::uuid());
  42.                 std::string resp_str;
  43.                 if (!response.SerializeToString(&resp_str))
  44.                 {
  45.                     throw ContactException("response序列化失败");
  46.                 }
  47.                 resp.status = 200;
  48.                 resp.body = resp_str;
  49.                 resp.set_header("Content-Type", "application/protobuf");
  50.             }
  51.             catch (const ContactException &e)
  52.             {
  53.                 resp.status = 500;
  54.                 response.set_success(false);
  55.                 response.set_error_desc(e.What());
  56.                 std::string resp_str;
  57.                 if (response.SerializeToString(&resp_str))
  58.                 {
  59.                     resp.body = resp_str;
  60.                     resp.set_header("Content-Type", "application/protobuf");
  61.                 }
  62.             }
  63.         });
  64.     server.listen("0.0.0.0", 8888);
  65.     return 0;
  66. }
复制代码
3.总结

序列化协议
通⽤性
格式
可读性
序列化⼤

序列化性能
JSON
通⽤(json、
xml已成为多种
⾏业标准的编
写⼯具
⽂本格式

轻量(使
⽤键值对
⽅式,压
缩了⼀定
的数据空


XML
通用
⽂本格式

重量(数
据冗余,
因为需要
成对的闭
合标签

ProtoBuf
独⽴
(Protobuf只
是Google公司
内部的⼯具)
⼆进制格式
差(只能
反序列化
后得到真
正可读的
数据)
轻量(⽐
JSON更轻
量,传输
起来带宽
和速度会
有优化

   ⼩结:   

  • XML、JSON、ProtoBuf都具有数据布局化和数据序列化的能⼒。
  • XML、JSON更注意数据布局化,关注可读性和语义表达能⼒。ProtoBuf更注意数据序列化,关注
    服从、空间、速度,可读性差,语义表达能⼒不⾜,为包管极致的服从,会舍弃⼀部门元信息。    3. ProtoBuf的应⽤  场景更为明确,XML、JSON的应⽤  场景更为丰富。     protobuf 和 Json 的实验对比:   

  • 编解码性能:ProtoBuf的编码解码性能,⽐JSON⾼出2-4倍。
  • 内存占⽤:ProtoBuf的内存278,⽽JSON到达567,ProtoBuf的内存占⽤只有JSON的1/2。
    注:以上结论的数据只是根据该项实验得出。因为受差别的字段类型、字段个数等影响,测出的数据会有所差别。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

玛卡巴卡的卡巴卡玛

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表