马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
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更轻
量,传输
起来带宽
和速度会
有优化
| 高
| ⼩结:
- 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企服之家,中国第一个企服评测及商务社交产业平台。 |