目录
[2. centos下编写的注意事项](#2. centos下编写的注意事项)
[5. 客户端代码实现](#5. 客户端代码实现)
前言
Protobuf还常用于通讯协议、服务端数据交换场景。那么在这个示例中,我们将实现一个网络版本的通讯录,模拟实现客户端与服务端的交互,通过Protobuf来实现各端之间的协议序列化。
需求如下:
●客户端可以选择对通讯录进行以下操作:
●新增一个联系人
●删除一个联系人
●查询通讯录列表
●查询一个联系人的详细信息
●服务端提供增删查能力,并需要持久化通讯录。
●客户端、服务端间的交互数据使用Protobuf来完成。
1.环境搭建
Httplib库: cpp-httplib 是个开源的库,是一个c++封装的http库,使用这个库可以在linux、windows平台下完成http客户端、http服务端的搭建。使用起来非常方便,只需要包含头文件httplib.h即可。编译程序时,需要带上-lpthread选项。
源码库地址: https://github.com/yhirose/cpp-httplib
2. centos下编写的注意事项
如果使用centOS环境,yum源带的g++最新版本是4.8.5,发布于2015年,年代久远。编译该项目会出现异常。将gcc/g++升级为更高版本可解决问题。
升级参考:https://juejin.cn/post/6844903873111392263
安装gcc 8版本
yum install -y devtoolset-8-gcc devtoolset-8-gcc-c++
启⽤版本
source /opt/rh/devtoolset-8/enable
查看版本已经变成gcc 8.3.1
gcc -v
3.约定双端交互接口
新增一个联系人:
[请求]
Post /contacts/add AddContactRequest
Content-Type: application/protobuf
[响应]
AddContactResponse
Content-Type: application/protobuf
删除一个联系人:
[请求]
Post /contacts/del DelContactRequest
Content-Type: application/protobuf
[响应]
DelContactResponse
Content-Type: application/protobuf
查询通讯录列表:
[请求]
GET /contacts/find-all
[响应]
FindAllContactsResponse
Content-Type: application/protobuf
查询一个联系人的详细信息:
[请求]
Post /contacts/find-one FindOneContactRequest
Content-Type: application/protobuf
[响应]
FindOneContactResponse
Content-Type: application/protobuf
4.约定双端交互req/resp
base_response.proto
cpp
syntax = "proto3";
package base_response;
message BaseResponse {
bool success = 1; // 返回结果
string error_desc = 2; // 错误描述
}
add_contact_request.proto
cpp
syntax = "proto3";
package add_contact_req;
// 新增联系⼈ req
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 = 3; // 电话
map<string, string> remark = 4; // 备注
}
add_contact_response.proto
bash
syntax = "proto3";
package add_contact_resp;
import "base_response.proto"; // 引⼊base_response
message AddContactResponse {
base_response.BaseResponse base_resp = 1;
string uid = 2;
}
del_contact_request.proto
cpp
syntax = "proto3";
package del_contact_req;
// 删除⼀个联系⼈ req
message DelContactRequest {
string uid = 1; // 联系⼈ID
}
del_contact_response.proto
bash
syntax = "proto3";
package del_contact_resp;
import "base_response.proto"; // 引⼊base_response
// 删除⼀个联系⼈ resp
message DelContactResponse {
base_response.BaseResponse base_resp = 1;
string uid = 2;
}
find_one_contact_request.proto
cpp
syntax = "proto3";
package find_one_contact_req;
// 查询⼀个联系⼈ req
message FindOneContactRequest {
string uid = 1; // 联系⼈ID
}
find_one_contact_response.proto
cpp
syntax = "proto3";
package find_one_contact_resp;
import "base_response.proto"; // 引⼊base_response
// 查询⼀个联系⼈ resp
message FindOneContactResponse {
base_response.BaseResponse base_resp = 1;
string uid = 2; // 联系⼈ID
string name = 3; // 姓名
int32 age = 4; // 年龄
message Phone {
string number = 1; // 电话号码
enum PhoneType {
MP = 0; // 移动电话
TEL = 1; // 固定电话
}
PhoneType type = 2; // 类型
}
repeated Phone phone = 5; // 电话
map<string, string> remark = 6; // 备注
}
find_all_contacts_response.proto
cpp
syntax = "proto3";
package find_all_contacts_resp;
import "base_response.proto"; // 引⼊base_response
// 联系⼈摘要信息
message PeopleInfo {
string uid = 1; // 联系⼈ID
string name = 2; // 姓名
}
// 查询所有联系⼈ resp
message FindAllContactsResponse {
base_response.BaseResponse base_resp = 1;
repeated PeopleInfo contacts = 2;
}
5. 客户端代码实现
cpp
#include <iostream>
#include "ContactsServer.h"
#include "ContactException.h"
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;
}
int main() {
enum OPERATE {ADD=1, DEL, FIND_ALL, FIND_ONE};
ContactsServer contactsServer;
while (true) {
menu();
std::cout << "---> 请选择:";
int choose;
std::cin >> choose;
std::cin.ignore(256, '\n');
try {
switch (choose) {
case OPERATE::ADD:
contactsServer.addContact();
break;
case OPERATE::DEL:
contactsServer.delContact();
break;
case OPERATE::FIND_ALL:
contactsServer.findContacts();
break;
case OPERATE::FIND_ONE:
contactsServer.findContact();
break;
case 0:
std::cout << "---> 程序已退出" << std::endl;
return 0;
default:
std::cout << "---> 无此选项,请重新选择!" << std::endl;
break;
}
} catch (const ContactException& e) {
std::cerr << "---> 操作通讯录时发现异常!!!" << std::endl
<< "---> 异常信息:" << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "---> 操作通讯录时发现异常!!!" << std::endl
<< "---> 异常信息:" << e.what() << std::endl;
}
}
}
ContactException.h:定义异常类
cpp
// 自定义异常类
class ContactException
{
private:
std::string message;
public:
ContactException(std::string str = "A problem") : message{str} {}
std::string what() const { return message; }
};
ContactsServer.h:客户端通讯录服务端定义
cpp
#include <iostream>
#include "./request/add_contact_request.pb.h"
#include "./response/add_contact_response.pb.h"
#include "./request/find_one_contact_request.pb.h"
#include "./response/find_one_contact_response.pb.h"
#include "./response/find_all_contacts_response.pb.h"
#include "./request/del_contact_request.pb.h"
#include "./response/del_contact_response.pb.h"
class ContactsServer
{
public:
void addContact();
void delContact();
void findContacts();
void findContact();
private:
void buildAddContactRequest(add_contact_req::AddContactRequest* req);
void printFindOneContactResponse(find_one_contact_resp::FindOneContactResponse& resp);
void printFindAllContactsResponse(find_all_contacts_resp::FindAllContactsResponse& resp);
};
ContactsServer.cc:客户端通讯录服务实现
cpp
#include "ContactsServer.h"
#include "ContactException.h"
#include "httplib.h"
#define CONTACTS_IP "139.159.150.152"
#define CONTACTS_PORT 8123
void ContactsServer::addContact() {
httplib::Client cli(CONTACTS_IP, CONTACTS_PORT);
// 构建 request 请求
add_contact_req::AddContactRequest req;
buildAddContactRequest(&req);
// 序列化 request
std::string req_str;
if (!req.SerializeToString(&req_str)) {
throw ContactException("AddContactRequest序列化失败!");
}
// 发起 post 请求
auto res = cli.Post("/contacts/add", req_str, "application/protobuf");
if (!res) {
std::string err_desc;
err_desc.append("/contacts/add 链接错误!错误信息:")
.append(httplib::to_string(res.error()));
throw ContactException(err_desc);
}
// 反序列化 response
add_contact_resp::AddContactResponse resp;
bool parse = resp.ParseFromString(res->body);
// 处理异常
if (res->status != 200 && !parse) {
std::string err_desc;
err_desc.append("post '/contacts/add/' 失败:")
.append(std::to_string(res->status))
.append("(").append(res->reason)
.append(")");
throw ContactException(err_desc);
}
else if (res->status != 200) {
// 处理服务异常
std::string err_desc;
err_desc.append("post '/contacts/add/' 失败 ")
.append(std::to_string(res->status))
.append("(").append(res->reason)
.append(") 错误原因:")
.append(resp.base_resp().error_desc());
throw ContactException(err_desc);
}
else if (!resp.base_resp().success()) {
// 处理结果异常
std::string err_desc;
err_desc.append("post '/contacts/add/' 结果异常:")
.append("异常原因:")
.append(resp.base_resp().error_desc());
throw ContactException(err_desc);
}
// 正常返回,打印结果
std::cout << "---> 新增联系人成功,联系人ID:" << resp.uid() << std::endl;
}
void ContactsServer::delContact() {
httplib::Client cli(CONTACTS_IP, CONTACTS_PORT);
// 构建 request 请求
del_contact_req::DelContactRequest req;
std::cout << "请输入要删除的联系人id: ";
std::string uid;
getline(std::cin, uid);
req.set_uid(uid);
// 序列化 request
std::string req_str;
if (!req.SerializeToString(&req_str)) {
throw ContactException("DelContactRequest序列化失败!");
}
// 发起 post 请求
auto res = cli.Post("/contacts/del", req_str, "application/protobuf");
if (!res) {
std::string err_desc;
err_desc.append("/contacts/del 链接错误!错误信息:")
.append(httplib::to_string(res.error()));
throw ContactException(err_desc);
}
// 反序列化 response
del_contact_resp::DelContactResponse resp;
bool parse = resp.ParseFromString(res->body);
// 处理异常
if (res->status != 200 && !parse) {
std::string err_desc;
err_desc.append("post '/contacts/del' 失败:")
.append(std::to_string(res->status))
.append("(").append(res->reason)
.append(")");
throw ContactException(err_desc);
}
else if (res->status != 200) {
std::string err_desc;
err_desc.append("post '/contacts/del' 失败 ")
.append(std::to_string(res->status))
.append("(").append(res->reason)
.append(") 错误原因:")
.append(resp.base_resp().error_desc());
throw ContactException(err_desc);
}
else if (!resp.base_resp().success()) {
// 结果异常
std::string err_desc;
err_desc.append("post '/contacts/del' 结果异常:")
.append("异常原因:")
.append(resp.base_resp().error_desc());
throw ContactException(err_desc);
}
// 正常返回,打印结果
std::cout << "---> 成功删除联系人,被删除的联系人ID为:" << resp.uid() << std::endl;
}
void ContactsServer::findContacts() {
httplib::Client cli(CONTACTS_IP, CONTACTS_PORT);
// 发起 get 请求
auto res = cli.Get("/contacts/find-all");
if (!res) {
std::string err_desc;
err_desc.append("/contacts/find-all 链接错误!错误信息:")
.append(httplib::to_string(res.error()));
throw ContactException(err_desc);
}
// 反序列化 response
find_all_contacts_resp::FindAllContactsResponse resp;
bool parse = resp.ParseFromString(res->body);
// 处理异常
if (res->status != 200 && !parse) {
std::string err_desc;
err_desc.append("get '/contacts/find-all' 失败:")
.append(std::to_string(res->status))
.append("(").append(res->reason)
.append(")");
throw ContactException(err_desc);
}
else if (res->status != 200) {
// 服务端异常
std::string err_desc;
err_desc.append("post '/contacts/find-all' 失败 ")
.append(std::to_string(res->status))
.append("(").append(res->reason)
.append(") 错误原因:")
.append(resp.base_resp().error_desc());
throw ContactException(err_desc);
}
else if (!resp.base_resp().success()) {
// 结果异常
std::string err_desc;
err_desc.append("post '/contacts/find-all' 结果异常:")
.append("异常原因:")
.append(resp.base_resp().error_desc());
throw ContactException(err_desc);
}
// 正常返回,打印结果
printFindAllContactsResponse(resp);
}
void ContactsServer::findContact() {
httplib::Client cli(CONTACTS_IP, CONTACTS_PORT);
// 构建 request 请求
find_one_contact_req::FindOneContactRequest req;
std::cout << "请输入要查询的联系人id: ";
std::string uid;
getline(std::cin, uid);
req.set_uid(uid);
// 序列化 request
std::string req_str;
if (!req.SerializeToString(&req_str)) {
throw ContactException("FindOneContactRequest序列化失败!");
}
// 发起 post 请求
auto res = cli.Post("/contacts/find-one", req_str, "application/protobuf");
if (!res) {
std::string err_desc;
err_desc.append("/contacts/find-one 链接错误!错误信息:")
.append(httplib::to_string(res.error()));
throw ContactException(err_desc);
}
// 反序列化 response
find_one_contact_resp::FindOneContactResponse resp;
bool parse = resp.ParseFromString(res->body);
// 处理异常
if (res->status != 200 && !parse) {
std::string err_desc;
err_desc.append("post '/contacts/find-one' 失败:")
.append(std::to_string(res->status))
.append("(").append(res->reason)
.append(")");
throw ContactException(err_desc);
}
else if (res->status != 200) {
std::string err_desc;
err_desc.append("post '/contacts/find-one' 失败 ")
.append(std::to_string(res->status))
.append("(").append(res->reason)
.append(") 错误原因:")
.append(resp.base_resp().error_desc());
throw ContactException(err_desc);
}
else if (!resp.base_resp().success()) {
// 结果异常
std::string err_desc;
err_desc.append("post '/contacts/find-one' 结果异常:")
.append("异常原因:")
.append(resp.base_resp().error_desc());
throw ContactException(err_desc);
}
// 正常返回,打印结果
std::cout << "---> 查询到联系人ID为:" << resp.uid() << " 的信息:" << std::endl;
printFindOneContactResponse(resp);
}
void ContactsServer::printFindAllContactsResponse(find_all_contacts_resp::FindAllContactsResponse& resp) {
if (0 == resp.contacts_size()) {
std::cout << "还未添加任何联系人" << std::endl;
return;
}
for (auto contact : resp.contacts()) {
std::cout << "联系人姓名: " << contact.name() << " 联系人ID:" << contact.uid() << std::endl;
}
}
void ContactsServer::buildAddContactRequest(add_contact_req::AddContactRequest* req) {
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');
for(int i = 1; ; i++) {
std::cout << "请输入联系人电话" << i << "(只输入回车完成电话新增): ";
std::string number;
getline(std::cin, number);
if (number.empty()) {
break;
}
add_contact_req::AddContactRequest_Phone* phone = req->add_phone();
phone->set_number(number);
std::cout << "选择此电话类型 (1、移动电话 2、固定电话) : " ;
int type;
std::cin >> type;
std::cin.ignore(256, '\n');
switch (type) {
case 1:
phone->set_type(add_contact_req::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP);
break;
case 2:
phone->set_type(add_contact_req::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL);
break;
default:
std::cout << "----非法选择,使用默认值!" << std::endl;
break;
}
}
for(int i = 1; ; i++) {
std::cout << "请输入备注" << i << "标题 (只输入回车完成备注新增): ";
std::string remark_key;
getline(std::cin, remark_key);
if (remark_key.empty()) {
break;
}
std::cout << "请输入备注" << i << "内容: ";
std::string remark_value;
getline(std::cin, remark_value);
req->mutable_remark()->insert({remark_key, remark_value});
}
}
void ContactsServer::printFindOneContactResponse(find_one_contact_resp::FindOneContactResponse& resp) {
std::cout << "姓名:" << resp.name() << std::endl;
std::cout << "年龄:" << resp.age() << std::endl;
for (auto& phone : resp.phone()) {
int j = 1;
std::cout << "电话" << j++ << ": " << phone.number();
std::cout << " (" << phone.PhoneType_Name(phone.type()) << ")" << std::endl;
}
if (resp.remark_size()) {
std::cout << "备注信息: " << std::endl;
}
for (auto it = resp.remark().cbegin(); it != resp.remark().cend(); ++it) {
std::cout << " " << it->first << ": " << it->second << std::endl;
}
}
6.服务端代码实现
服务端存储通讯录结构定义: contacts. proto
cpp
syntax = "proto3";
package contacts;
// 联系⼈
message PeopleInfo {
string uid = 1; // 联系⼈ID
string name = 2; // 姓名
int32 age = 3; // 年龄
message Phone {
string number = 1; // 电话号码
enum PhoneType {
MP = 0; // 移动电话
TEL = 1; // 固定电话
}
PhoneType type = 2; // 类型
}
repeated Phone phone = 4; // 电话
map<string, string> remark = 5; // 备注
}
// 通讯录
message Contacts {
map<string, PeopleInfo> contacts = 1;
}
cpp
#include <iostream>
#include "httplib.h"
#include "../server/ContactsServer.h"
#include "../common/ContactException.h"
#include "request/add_contact_request.pb.h"
#include "response/add_contact_response.pb.h"
#include "request/find_one_contact_request.pb.h"
#include "response/find_one_contact_response.pb.h"
#include "response/find_all_contacts_response.pb.h"
#include "request/del_contact_request.pb.h"
#include "response/del_contact_response.pb.h"
using std::cout;
using std::endl;
using std::cerr;
using namespace httplib;
int main() {
cout << "---> 服务启动..." << endl;
Server srv; // 创建服务端对象
ContactsServer contactsServer;
srv.Post("/contacts/add", [contactsServer](const Request& req, Response& res) {
add_contact_req::AddContactRequest request;
add_contact_resp::AddContactResponse response;
try {
// 反序列化 request
if (!request.ParseFromString(req.body)) {
throw ContactException("Parse AddContactRequest error!");
}
// 新增联系人
contactsServer.add(request, &response);
// 序列化 resp
std::string response_str;
if (!response.SerializeToString(&response_str)) {
throw ContactException("Serialize AddContactResponse error");
}
res.body = response_str;
res.set_header("Content-Type", "application/protobuf");
res.status = 200;
} catch (ContactException &e) {
cerr << "---> /contacts/add 发现异常!!!" << endl
<< "---> 异常信息:" << e.what() << endl;
res.status = 500;
base_response::BaseResponse* baseResponse = response.mutable_base_resp();
baseResponse->set_success(false);
baseResponse->set_error_desc(e.what());
std::string response_str;
if (response.SerializeToString(&response_str)) {
res.body = response_str;
res.set_header("Content-Type", "application/protobuf");
}
}
});
srv.Post("/contacts/del", [contactsServer](const Request& req, Response& res) {
del_contact_req::DelContactRequest request;
del_contact_resp::DelContactResponse response;
try {
// 反序列化 request
if (!request.ParseFromString(req.body)) {
throw ContactException("Parse DelContactRequest error!");
}
// 删除联系人
contactsServer.del(request, &response);
// 序列化 response
std::string response_str;
if (!response.SerializeToString(&response_str)) {
throw ContactException("Serialize DelContactResponse error");
}
res.body = response_str;
res.set_header("Content-Type", "application/protobuf");
res.status = 200;
} catch (ContactException &e) {
cerr << "---> /contacts/del 发现异常!!!" << endl
<< "---> 异常信息:" << e.what() << endl;
res.status = 500;
base_response::BaseResponse* baseResponse = response.mutable_base_resp();
baseResponse->set_success(false);
baseResponse->set_error_desc(e.what());
std::string response_str;
if (response.SerializeToString(&response_str)) {
res.body = response_str;
res.set_header("Content-Type", "application/protobuf");
}
}
});
srv.Post("/contacts/find-one", [contactsServer](const Request& req, Response& res) {
find_one_contact_req::FindOneContactRequest request;
find_one_contact_resp::FindOneContactResponse response;
try {
// 反序列化 request
if (!request.ParseFromString(req.body)) {
throw ContactException("Parse FindOneContactRequest error!");
}
// 查询联系人详细信息
contactsServer.findOne(request, &response);
// 序列化 response
std::string response_str;
if (!response.SerializeToString(&response_str)) {
throw ContactException("Serialize FindOneContactResponse error");
}
res.body = response_str;
res.set_header("Content-Type", "application/protobuf");
res.status = 200;
} catch (ContactException &e) {
cerr << "---> /contacts/find-one 发现异常!!!" << endl
<< "---> 异常信息:" << e.what() << endl;
res.status = 500;
base_response::BaseResponse* baseResponse = response.mutable_base_resp();
baseResponse->set_success(false);
baseResponse->set_error_desc(e.what());
std::string response_str;
if (response.SerializeToString(&response_str)) {
res.body = response_str;
res.set_header("Content-Type", "application/protobuf");
}
}
});
srv.Get("/contacts/find-all", [contactsServer](const Request& req, Response& res) {
find_all_contacts_resp::FindAllContactsResponse response;
try {
// 查询所有联系人
contactsServer.findAll(&response);
// 序列化 response
std::string response_str;
if (!response.SerializeToString(&response_str)) {
throw ContactException("Serialize FindAllContactsResponse error");
}
res.body = response_str;
res.set_header("Content-Type", "application/protobuf");
res.status = 200;
} catch (ContactException &e) {
cerr << "---> /contacts/find-all 发现异常!!!" << endl
<< "---> 异常信息:" << e.what() << endl;
res.status = 500;
base_response::BaseResponse* baseResponse = response.mutable_base_resp();
baseResponse->set_success(false);
baseResponse->set_error_desc(e.what());
std::string response_str;
if (response.SerializeToString(&response_str)) {
res.body = response_str;
res.set_header("Content-Type", "application/protobuf");
}
}
});
srv.listen("0.0.0.0", 8123);
}
ContactException.h:定义异常类
cpp
// 自定义异常类
class ContactException
{
private:
std::string message;
public:
ContactException(std::string str = "A problem") : message{str} {}
std::string what() const { return message; }
};
ContactsServer.h:通讯录服务定义
cpp
#pragma once
#include <iostream>
#include "httplib.h"
#include "../controller/request/add_contact_request.pb.h"
#include "../controller/response/add_contact_response.pb.h"
#include "../controller/request/find_one_contact_request.pb.h"
#include "../controller/response/find_one_contact_response.pb.h"
#include "../controller/response/find_all_contacts_response.pb.h"
#include "../controller/request/del_contact_request.pb.h"
#include "../controller/response/del_contact_response.pb.h"
#include "../dao/contacts.pb.h"
#include "../dao/ContactsMapper.h"
using namespace httplib;
class ContactsServer {
public:
ContactsMapper contactsMapper;
public:
void add(add_contact_req::AddContactRequest& request,
add_contact_resp::AddContactResponse* response) const;
void del(del_contact_req::DelContactRequest& request,
del_contact_resp::DelContactResponse* response) const;
void findOne(find_one_contact_req::FindOneContactRequest request,
find_one_contact_resp::FindOneContactResponse* response) const;
void findAll(find_all_contacts_resp::FindAllContactsResponse* rsp) const;
private:
void printAddContactRequest(add_contact_req::AddContactRequest& request) const;
void buildPeopleInfo(contacts::PeopleInfo* people, add_contact_req::AddContactRequest& request) const;
void buildFindOneContactResponse(const contacts::PeopleInfo& people,
find_one_contact_resp::FindOneContactResponse* response) const;
void buildFindAllContactsResponse(contacts::Contacts& contacts,
find_all_contacts_resp::FindAllContactsResponse* rsp) const;
};
ContactsServer.cc:通讯录服务实现
cpp
#include "ContactsServer.h"
#include "../common/ContactException.h"
#include "../common/Utils.h"
using std::cout;
using std::endl;
void ContactsServer::add(add_contact_req::AddContactRequest& request,
add_contact_resp::AddContactResponse* response) const {
// 打印日志
printAddContactRequest(request);
// 先读取已存在的 contacts
contacts::Contacts contacts;
contactsMapper.selectContacts(&contacts);
// 转换为存入文件的消息对象
google::protobuf::Map<std::string, contacts::PeopleInfo>* map_contacts = contacts.mutable_contacts();
contacts::PeopleInfo people;
buildPeopleInfo(&people, request);
map_contacts->insert({people.uid(), people});
// 向磁盘文件写入新的 contacts
contactsMapper.insertContacts(contacts);
response->set_uid(people.uid());
response->mutable_base_resp()->set_success(true);
// 打印日志
cout << "---> (ContactsServer::add) Success to write contacts." << endl;
}
void ContactsServer::del(del_contact_req::DelContactRequest& request,
del_contact_resp::DelContactResponse* response) const {
// 打印日志
cout << "---> (ContactsServer::del) DelContactRequest: uid: " << request.uid() << endl;
// 先读取已存在的 contacts
contacts::Contacts contacts;
contactsMapper.selectContacts(&contacts);
// 不含uid直接返回
if (contacts.contacts().find(request.uid()) == contacts.contacts().end()) {
cout << "---> (ContactsServer::del) not find uid: " << request.uid() << endl;
response->set_uid(request.uid());
response->mutable_base_resp()->set_success(false);
response->mutable_base_resp()->set_error_desc("not find uid");
return;
}
// 删除用户
contacts.mutable_contacts()->erase(request.uid());
// 向磁盘文件写入新的 contacts
contactsMapper.insertContacts(contacts);
// 构造resp
response->set_uid(request.uid());
response->mutable_base_resp()->set_success(true);
// 打印日志
cout << "---> (ContactsServer::del) Success to del contact, uid: " << request.uid() << endl;
}
void ContactsServer::findOne(find_one_contact_req::FindOneContactRequest request,
find_one_contact_resp::FindOneContactResponse* response) const {
// 打印日志
cout << "---> (ContactsServer::findOne) FindOneContactRequest: uid: " << request.uid() << endl;
// 获取通讯录
contacts::Contacts contacts;
contactsMapper.selectContacts(&contacts);
// 转换resp消息对象
const google::protobuf::Map<std::string, contacts::PeopleInfo>& map_contacts = contacts.contacts();
auto it = map_contacts.find(request.uid());
// 查找的联系人不存在
if (it == map_contacts.end()) {
cout << "---> (ContactsServer::findOne) not find uid: " << request.uid() << endl;
response->mutable_base_resp()->set_success(false);
response->mutable_base_resp()->set_error_desc("uid not exist");
return;
}
// 构建resp
buildFindOneContactResponse(it->second, response);
// 打印日志
cout << "---> (ContactsServer::findOne) find uid: " << request.uid() << endl;
}
void ContactsServer::findAll(find_all_contacts_resp::FindAllContactsResponse* rsp) const {
// 打印日志
cout << "---> (ContactsServer::findAll) " << endl;
// 获取通讯录
contacts::Contacts contacts;
contactsMapper.selectContacts(&contacts);
// 转换resp消息对象
buildFindAllContactsResponse(contacts, rsp);
}
void ContactsServer::buildFindAllContactsResponse(contacts::Contacts& contacts,
find_all_contacts_resp::FindAllContactsResponse* rsp) const {
if (nullptr == rsp) {
return;
}
rsp->mutable_base_resp()->set_success(true);
for (auto it = contacts.contacts().cbegin(); it != contacts.contacts().cend(); ++it) {
find_all_contacts_resp::PeopleInfo* people = rsp->add_contacts();
people->set_uid(it->first);
people->set_name(it->second.name());
}
}
void ContactsServer::buildFindOneContactResponse(const contacts::PeopleInfo& people,
find_one_contact_resp::FindOneContactResponse* response) const {
if (nullptr == response) {
return;
}
response->mutable_base_resp()->set_success(true);
response->set_uid(people.uid());
response->set_name(people.name());
response->set_age(people.age());
for (auto& phone : people.phone()) {
find_one_contact_resp::FindOneContactResponse_Phone* resp_phone = response->add_phone();
resp_phone->set_number(phone.number());
switch (phone.type()) {
case contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP:
resp_phone->set_type(find_one_contact_resp::FindOneContactResponse_Phone_PhoneType::FindOneContactResponse_Phone_PhoneType_MP);
break;
case contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL:
resp_phone->set_type(find_one_contact_resp::FindOneContactResponse_Phone_PhoneType::FindOneContactResponse_Phone_PhoneType_TEL);
break;
default:
break;
}
}
Utils::map_copy(response->mutable_remark(), people.remark());
}
void ContactsServer::printAddContactRequest(add_contact_req::AddContactRequest& request) const {
cout << "---> (ContactsServer::add) AddContactRequest:" << endl;
cout << "姓名:" << request.name() << endl;
cout << "年龄:" << request.age() << endl;
for (auto& phone : request.phone()) {
int j = 1;
cout << "电话" << j++ << ": " << phone.number();
cout << " (" << phone.PhoneType_Name(phone.type()) << ")" << endl;
}
if (request.remark_size()) {
cout << "备注信息: " << endl;
}
for (auto it = request.remark().cbegin(); it != request.remark().cend(); ++it) {
cout << " " << it->first << ": " << it->second << endl;
}
}
void ContactsServer::buildPeopleInfo(contacts::PeopleInfo* people, add_contact_req::AddContactRequest& request) const {
std::string uid = Utils::generate_hex(10);
people->set_uid(uid);
people->set_name(request.name());
people->set_age(request.age());
for (auto& phone : request.phone()) {
contacts::PeopleInfo_Phone* peo_phone = people->add_phone();
peo_phone->set_number(phone.number());
switch (phone.type()) {
case add_contact_req::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP:
peo_phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP);
break;
case add_contact_req::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL:
peo_phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);
break;
default:
break;
}
}
Utils::map_copy(people->mutable_remark(), request.remark());
}
Utils.h:定义工具类
cpp
#include <sstream>
#include <random>
#include <google/protobuf/map.h>
class Utils
{
public:
static unsigned int random_char() {
// 用于随机数引擎获得随机种子
std::random_device rd;
// mt19937是c++11新特性,它是一种随机数算法,用法与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();
}
static void map_copy(google::protobuf::Map<std::string, std::string>* target,
const google::protobuf::Map<std::string, std::string>& source) {
if (nullptr == target) {
std::cout << "map_copy warning, target is nullptr!" << std::endl;
return;
}
for (auto it = source.cbegin(); it != source.cend(); ++it) {
target->insert({it->first, it->second});
}
}
};
ContactsMapper.h:持久化存储通讯录方法定义
cpp
#include "contacts.pb.h"
class ContactsMapper {
public:
void selectContacts(contacts::Contacts* contacts) const;
void insertContacts(contacts::Contacts& contacts) const;
};
ContactsMapper.cc:持久化存储通讯录方法实现
注:本应该存入数据库中,在这里为了简化流程,将通讯录存入本地文件
cpp
#include "ContactsMapper.h"
#include "../common/ContactException.h"
#include <fstream>
#define TEXT_NAME "contacts.bin"
using std::ios;
using std::cout;
using std::endl;
// 本应该存入数据库中,在这里为了简化流程,将通讯录存入本地文件
void ContactsMapper::selectContacts(contacts::Contacts* contacts) const{
std::fstream input(TEXT_NAME, ios::in | ios::binary);
if (!input) {
cout << "---> (ContactsMapper::selectContacts) " << TEXT_NAME << ": File not found. Creating a new file." << endl;
}
else if (!contacts->ParseFromIstream(&input)) {
input.close();
throw ContactException("(ContactsMapper::selectContacts) Failed to parse contacts.");
}
input.close();
}
void ContactsMapper::insertContacts(contacts::Contacts& contacts) const {
std::fstream output(TEXT_NAME, ios::out | ios::trunc | ios::binary);
if (!contacts.SerializeToOstream(&output)) {
output.close();
throw ContactException("(ContactsMapper::insertContacts) Failed to write contacts.");
}
output.close();
}