品牌推广与传播怎么写,怀化新站优化,做公司网站主要需要什么科目,android 做分享的网站环境搭建 Protobuf 还常⽤于通讯协议、服务端数据交换场景。 因为我们主要目的只是为了学习protobuf#xff0c;因此对于客户端#xff0c;原本应该具备#xff1a; 新增⼀个联系⼈ ◦ 删除⼀个联系⼈ ◦ 查询通讯录列表 ◦ 查询⼀个联系⼈的详细信息 这样四个功能。 …环境搭建 Protobuf 还常⽤于通讯协议、服务端数据交换场景。 因为我们主要目的只是为了学习protobuf因此对于客户端原本应该具备 新增⼀个联系⼈ ◦ 删除⼀个联系⼈ ◦ 查询通讯录列表 ◦ 查询⼀个联系⼈的详细信息 这样四个功能。
但是在这里我们只实现新增联系人的功能。
对于服务器端我们同样应该具备增删改查的功能这里我们也只实现增的功能。 这里我们需要用到一个Httplib库 cpp-httplib 是个开源的库是⼀个c封装的http库使⽤这个库可以在linux、 windows平台下完成http客⼾端、http服务端的搭建。使⽤起来⾮常⽅便只需要包含头⽂件 httplib.h 即可。编译程序时需要带上 -lpthread 选项。 双方通信的流程 约定双方交互req/rsp base_response.proto syntax proto3;
package base_response;message BaseResponse
{bool succes 1; // 返回结果string error_desc 2; // 状态码
}
add_contact_request.proto
syntax proto3;
package add_contact_request;message AddContactRequest
{string name 1; // 姓名int32 age 2; // 年龄message Phone{string number 1;enum PhoneType{MP 0; // 移动电话TEL 1; // 固定电话}PhoneType type 2; // 电话类型}repeated Phone phone_numbers 3; // 电话号码mapstring,string remark 4; // 备注
}add_contact_response.proto
syntax proto3;
package add_contact_response;
import base_response.proto; // 引入base_responsemessage AddContactResponse
{base_response.BaseResponse base_resp 1;string uid 2; // 联系人的uid
} 客户端部分
定义一个异常类用于抛异常 ContactException.h
#pragma once#include iostreamclass ContactException
{
private:std::string message;
public:ContactException(std::string str A Problem) : message(str) {}std::string What() const { return message; }
}; client.cc
#include iostream
#include ../add_contact_request.pb.h
#include ../add_contact_response.pb.h
#include ../../cpp-httplib/httplib.h
#include ContactException.husing namespace std;
using namespace httplib;
using namespace add_contact_request;
using namespace add_contact_response;const string IP 127.0.0.1;
const uint16_t PORT 8080;void addContact();void menu()
{cout ----------------网络通讯录------------------ endl;cout 选择: 1.添加联系人 2. 删除联系人 3.查找所有联系人 4. 查找一个联系人 0.退出 endl;
}void addContact()
{httplib::Client cli(IP, PORT);AddContactRequest info;cout ------------添加联系人------------- endl;cout 请输入联系人姓名: endl;string name;getline(cin, name);info.set_name(name);cout 请输入联系人年龄: endl;int32_t age;cin age;cin.ignore(256, \n); // 清空一下缓冲区// info-set_age(age);string numbers;for (int i 1;; i){cout 请输入联系人的第 i 个号码(直接回车终止输入) endl;getline(cin, numbers);if (numbers.empty())break;cout 请输入这个电话的类型(1/移动电话 2/固定电话): endl;int num 0;cin num;cin.ignore(256, \n); // 清空以下缓冲区add_contact_request::AddContactRequest_Phone *phone info.add_phone_numbers();phone-set_number(numbers); // info-add_phones()返回的是一块开辟好空间的地址// 为了代码的可扩展性这里使用switch而不是if语句switch (num){case 1:phone-set_type(add_contact_request::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP);break;case 2:phone-set_type(add_contact_request::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL);break;default:cout 选择有误,取默认类型 endl;break;}}for (int i 1;; i){cout 请输入备注 i 标题(直接回车退出输入) endl;string remark_key;getline(cin, remark_key);if (remark_key.empty())break;cout 请输入备注 i 内容 endl;string remark_val;getline(cin, remark_val);info.mutable_remark()-insert({remark_key, remark_val});}string str_req;if (!info.SerializePartialToString(str_req)){// 失败抛异常ContactException e(str_req序列化失败!);throw(e);}// 发起Post请求auto res cli.Post(/contacts/add, str_req, application/protobuf);if (!res){// 失败抛异常string str_error 发起post请求失败,错误类型: ;str_error httplib::to_string(res.error());ContactException e(str_error);throw(e);}// 反序列化responseAddContactResponse resp;bool parse resp.ParseFromString(res-body);if(res-status ! 200 !parse){string err_desc;err_desc post contact/add 失败: ;err_desc std::to_string(res-status);err_desc (;err_desc res-reason;err_desc );throw ContactException(err_desc);}else if(res-status ! 200){string err_desc;err_desc post contact/add 失败: ;err_desc std::to_string(res-status);err_desc (;err_desc res-reason;err_desc );err_desc resp.base_resp().error_desc();throw ContactException(err_desc);}else if(!parse){string err_desc;err_desc post contact/add 反序列化失败: ;err_desc resp.base_resp().error_desc();throw ContactException(err_desc);}cout 新增联系人成功,uid resp.uid() endl;
}int main()
{while (true){menu();int choose;cin choose;cin.ignore(256, \n);try{switch (choose){case 1:addContact();break;case 2:case 3:case 4:break;case 0:cout 程序退出 endl;exit(0);default:cout 选择有误 endl;exit(-1);break;}}catch (const ContactException e){cerr 捕获一个异常:操作通讯录时! endl;cerr 信息: e.What() endl;}}return 0;
}
makefile:
all:client
client:g client.cc ../add_contact_request.pb.cc ../add_contact_response.pb.cc ../base_response.pb.cc -o client -stdc11 -lpthread -lprotobuf.PHONY:clean
clean:rm -f client 服务器部分
这里为了简单偷个懒没有使用异常了程序出错就直接退出
server.cc
#include iostream
#include random
#include ../../cpp-httplib/httplib.h
#include ../add_contact_request.pb.h
#include ../add_contact_response.pb.husing namespace std;
using namespace httplib;
using namespace add_contact_request;
using namespace add_contact_response;static unsigned int random_char()
{// ⽤于随机数引擎获得随机种⼦std::random_device rd;// mt19937是c11新特性它是⼀种随机数算法⽤法与rand()函数类似但是mt19937// 具有速度快周期⻓的特点// 作⽤是⽣成伪随机数std::mt19937 gen(rd());// 随机⽣成⼀个整数i 范围[0, 255]std::uniform_int_distribution dis(0, 255);return dis(gen);
}
// ⽣成 UUID 通⽤唯⼀标识符
static std::string generate_hex(const unsigned int len)
{std::stringstream ss;// ⽣成 len 个16进制随机数将其拼接⽽成for (auto i 0; i len; i){const auto rc random_char();std::stringstream hexstream;hexstream std::hex rc;auto hex hexstream.str();ss (hex.length() 2 ? 0 hex : hex);}return ss.str();
}void PrintPeopelInfo(const AddContactRequest people_info)
{cout -----------新增联系人信息--------------- endl;cout 姓名: people_info.name() endl;// cout 年龄: people_info.age() endl;// 打印电话for (int j 0; j people_info.phone_numbers().size(); j){const add_contact_request::AddContactRequest_Phone phones people_info.phone_numbers(j);cout 第 j 1 个电话: phones.number();int num phones.type();switch (num){case add_contact_request::AddContactRequest_Phone::PhoneType::AddContactRequest_Phone_PhoneType_MP:cout (移动电话MP) endl;break;case add_contact_request::AddContactRequest_Phone::PhoneType::AddContactRequest_Phone_PhoneType_TEL:cout (固定电话TEL) endl;break;default:cerr 未知类型 endl;break;}}// 打印备注信息int count 1;for (auto pair : people_info.remark()){cout 备注信息 count : ;cout ( pair.first ) : pair.second endl;;}// 打印未知字段const google::protobuf::Reflection *reflection people_info.GetReflection();const google::protobuf::UnknownFieldSet unknown_field_set reflection-GetUnknownFields(people_info);for (int j 0; j unknown_field_set.field_count(); j){const google::protobuf::UnknownField unknown_field unknown_field_set.field(j);cout 未知字段 j 1 字段编号: unknown_field.number() 字段类型: unknown_field.type();switch (unknown_field.type()){case google::protobuf::UnknownField::Type::TYPE_VARINT:cout 值: unknown_field.varint() endl;break;case google::protobuf::UnknownField::Type::TYPE_LENGTH_DELIMITED:cout 值: unknown_field.length_delimited() endl;break;default:cout 未知 endl;break;}}cout 新增联系人成功 endl;
}int main()
{cout ----------服务器启动!---------- endl;httplib::Server svr;// 先设置方法再进行监听svr.Post(/contacts/add, [](const Request req, Response resp){AddContactRequest contact_req;string str_req;if (!contact_req.ParseFromString(req.body)){cerr /contacts/add Post,反序列化失败! endl;exit(-1);}PrintPeopelInfo(contact_req); // 打印新增联系人信息// 准备响应信息// 这里为了简单就硬编码设置了AddContactResponse contact_resp;contact_resp.set_uid(generate_hex(8));string str_resp;if(!contact_resp.SerializePartialToString(str_resp)){cerr 序列化响应信息失败程序退出! endl;exit(-1);}resp.status 200;resp.body str_resp;resp.set_header(Content-Type, application/protobuf);cout 响应构建成功,准备发送! endl;});svr.listen(0.0.0.0,8080);return 0;
}
在上述过程中设计了一个能随机生成uid的函数可以了解一下。
makefile
all:server
server:g server.cc ../add_contact_request.pb.cc ../add_contact_response.pb.cc ../base_response.pb.cc -o server -stdc11 -lpthread -lprotobuf.PHONY:clean
clean:rm -f server 演示效果
客户端的新增联系人功能 这里也简化了对服务器发送过来的响应的处理我们只打印了服务器给我们返回的联系人的uid
服务器端的新增联系人功能 服务器受到信息后就会对客户端那边设置的信息进行一个打印然后把生成的uid发送给客户端 总结ProtoBuf的性能与使用场景 1. XML、JSON、ProtoBuf 都具有数据结构化和数据序列化的能⼒。 2. XML、JSON 更注重数据结构化关注可读性和语义表达能⼒。ProtoBuf 更注重数据序列化关注 效率、空间、速度可读性差语义表达能⼒不⾜为保证极致的效率会舍弃⼀部分元信息。 3. ProtoBuf 的应⽤场景更为明确 适合⾼性能对响应 速度有要求的数据传 输场景。 XML、JSON 的应⽤场景更为丰富。