【简易版】Linux下Protobuf 实现网络版通讯录--C++

2023-12-12 00:36

本文主要是介绍【简易版】Linux下Protobuf 实现网络版通讯录--C++,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、介绍

该项目的主要目的是用于熟悉protobuf的使用,体验数据在网络中序列化反序列化的形式,并非一个完整的项目。

该通讯录只实现了增加联系人的功能。服务器端接收到请求后会将联系人的信息打印。

二、环境搭建

使用Httplib库,可以快速完成客户端与服务器的搭建。

Httplib库:cpp-httplib是个开源的库,是⼀个c++封装的http库,使⽤这个库可以在linux、windows平台下完成http客户端、http服务端的搭建。使⽤起来⾮常⽅便,只需要包含头⽂件httplib.h即可。编译程序时,需要带上-lpthread选项。
源码库地址:https://github.com/yhirose/cpp-httplib

点击如图httplib.h文件,然后再点击下载该文件。
 

下载好后,在linux下使用rz指令将该头文件上传到linux的项目路径下。

注意:

如果使⽤centOS环境,yum源带的g++最新版本是4.8.5,发布于2015年,年代久远。编译该项⽬会出现异常。将gcc/g++升级为更⾼版本可解决问题。
可以通过指令gcc -v查看gcc的版本,如果以8.开头的版本,则需要更新一下。

更新方法可以参考这篇文章:CentOS 7上升级/安装gcc - 掘金

syntax = "proto3"; 
package add_contact;// 新增联系人 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; // 电话
}                                  message AddContactResponse{bool success = 1;  // 服务调用是否成功string error_desc = 2;  //错误原因    string uid = 3;                   
} 

五、自定义异常类

#include <iostream>
#include <string>// ⾃定义异常类
class ContactException
{
private:std::string message;public:ContactException(std::string str = "A problem") : message{str} {}std::string what() const { return message; }
};

六、客户端

客户端使用前面下载的httplib.h中的Client类,来实现客户端的构造:

Client cli(CONTACTS_HOST, CONTACTS_PORT);

参数包含CONTACTS_HOST服务器的ip地址,CONTACTS_PORT服务器的端口号,来与服务器建立链接。

void addContact()
{Client cli(CONTACTS_HOST, CONTACTS_PORT); // 构造reqadd_contact::AddContactRequest req;buidAddContactRequest(&req);// 序列化string req_str;if(!req.SerializeToString(&req_str)){throw ContactException("AddContactRequest is fail");}// 发起post调用auto res = cli.Post("/contacts/add", req_str, "application/protobuf");if(!res)  // 失败{string err_desc;err_desc.append("/contacts/add 链接失败! 错误信息: ").append(httplib::to_string(res.error()));throw ContactException(err_desc);}//反序列化add_contact::AddContactResponse resp;bool parse = resp.ParseFromString(res->body);if(res->status != 200 && !parse){string err_desc;err_desc.append("/contact/add 调用失败").append(std::to_string(res->status)).append("(").append(res->reason).append(")");throw ContactException(err_desc);}else if(res->status != 200){string err_desc;err_desc.append("/contact/add 调用失败").append(std::to_string(res->status)).append("(").append(res->reason).append(") 错误原因").append(resp.error_desc());throw ContactException(err_desc);}else if(!resp.success()){string err_desc;err_desc.append("/contact/add 结果异常").append("异常原因:").append(resp.error_desc());throw ContactException(err_desc);}// 结果打印cout<< "新增联系人成功,联系人ID:"<<resp.uid()<<endl;
}

完整代码

#include <iostream>
#include "httplib.h"
#include "ContactsException.h"
#include "add_contact.pb.h"using namespace httplib;
using namespace std;#define CONTACTS_HOST "120.55.73.49"
#define CONTACTS_PORT 8080void addContact();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 OPTION{QUIT = 0,ADD,DEL,FIND_ONE,FIND_ALL};while(true){menu();cout<< "请选择:"<<endl;int choose;cin >> choose;cin.ignore(256, '\n');try{switch(choose){case OPTION::QUIT:cout<<"---程序退出---"<<endl;return 0;case OPTION::ADD:addContact();break;case OPTION::DEL:break;case OPTION::FIND_ONE:break;case OPTION::FIND_ALL:break;default:cout<<"选择错误,请重新选择:"<<endl;break;}}catch(const ContactException& e){cout<< "---操作通讯录时发生异常"<<endl<< "---异常信息:"<< e.what() <<endl;}}return 0;
}void buidAddContactRequest(add_contact::AddContactRequest* req)
{cout<< "--------新增联系人--------"<<endl;cout<< "请输入姓名: ";string name;getline(cin, name);req->set_name(name);cout<< "请输入年龄: ";int age;cin >> age;req->set_age(age);cin.ignore(256, '\n'); // 清除输入缓冲区里面内容// 输入电话号码for(int i=0; ; i++){cout<< "请输入联系人电话"<< i+1 <<"(只输入回车表示结束)"<<endl;string number;getline(cin, number);if(number.empty()){break;}add_contact::AddContactRequest_Phone* phone = req->add_phone();phone->set_number(number);cout<< "请输入电话类型(1.移动电话 2.座机): ";int type;cin>>type;cin.ignore(256, '\n');switch(type){case 1:phone->set_type(add_contact::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP);break;case 2:phone->set_type(add_contact::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL);break;default:cout<<"输入有误"<<endl;break;}}
}void addContact()
{Client cli(CONTACTS_HOST, CONTACTS_PORT); // 构造reqadd_contact::AddContactRequest req;buidAddContactRequest(&req);// 序列化string req_str;if(!req.SerializeToString(&req_str)){throw ContactException("AddContactRequest is fail");}// 发起post调用auto res = cli.Post("/contacts/add", req_str, "application/protobuf");if(!res){string err_desc;err_desc.append("/contacts/add 链接失败! 错误信息: ").append(httplib::to_string(res.error()));throw ContactException(err_desc);}//反序列化add_contact::AddContactResponse resp;bool parse = resp.ParseFromString(res->body);if(res->status != 200 && !parse){string err_desc;err_desc.append("/contact/add 调用失败").append(std::to_string(res->status)).append("(").append(res->reason).append(")");throw ContactException(err_desc);}else if(res->status != 200){string err_desc;err_desc.append("/contact/add 调用失败").append(std::to_string(res->status)).append("(").append(res->reason).append(") 错误原因").append(resp.error_desc());throw ContactException(err_desc);}else if(!resp.success()){string err_desc;err_desc.append("/contact/add 结果异常").append("异常原因:").append(resp.error_desc());throw ContactException(err_desc);}// 结果打印cout<< "新增联系人成功,联系人ID:"<<resp.uid()<<endl;
}

七、服务器

void printContact(add_contact::AddContactRequest &req)打印req所包含的联系人信息

static unsigned int random_char();生成一个0~255范围内的随机数

static std::string generate_hex(const unsigned int len);⽣成 UUID (通⽤唯⼀标识符)

std::hex:将流中的整数以十六进制形式输出。

Server server:使用httplib.h中的Server类,构造服务器。

server.Post():参数中使用了lambda表达式,接收客户端的请求和响应。

#include <iostream>
#include "httplib.h"
#include "add_contact.pb.h"using namespace httplib;
using namespace std;// ⾃定义异常类
class ContactException
{
private:std::string message;public:ContactException(std::string str = "A problem") : message{str} {}std::string what() const { return message; }
};void printContact(add_contact::AddContactRequest &req)
{cout << "姓名:" << req.name() << endl;cout << "年龄:" << req.age() << endl;for (int j = 0; j < req.phone_size(); j++){const add_contact::AddContactRequest_Phone &phone = req.phone(j);cout << "联系电话:" << phone.number()<< " (" << phone.PhoneType_Name(phone.type()) << ")" << endl;// phone.type() 获取的是int值,phone.PhoneType_Name(phone.type())是根据int值转为对应的常量名(string)。}
}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;  // 生成的随机数转为16进制流auto hex = hexstream.str();  // 转为字符串形式ss << (hex.length() < 2 ? '0' + hex : hex);}return ss.str();
}int main()
{cout << "--------服务启动---------" << endl;Server server;server.Post("/contacts/add", [](const Request &req, Response &res){cout << "接收到post请求!" << endl;// 反序列化add_contact::AddContactRequest request;add_contact::AddContactResponse response;try{if (!request.ParseFromString(req.body)){throw ContactException("AddContactRequest 反序列化失败");}// 新增联系人持久化逻辑--》打印新增联系人信息printContact(request);// 构造返回结果 response.bodyresponse.set_success(true);response.set_uid(generate_hex(10));// res.body (序列化response)string response_str;if (!response.SerializeToString(&response_str)){throw ContactException("ADDcontactRequest序列化失败");}res.status = 200;res.body = response_str;res.set_header("Contact-Type", "application/protobuf");}catch (const ContactException &e){res.status = 500;response.set_success(false);response.set_error_desc(e.what());string response_str;if (response.SerializeToString(&response_str)){res.body = response_str;res.set_header("Content-type", "application/protobuf");}cout << "/contact/add 发生异常, 信息:" << e.what() << endl;} });// 绑定端口,且对外开放server.listen("0.0.0.0", 8080);return 0;
}

八、运行结果

这篇关于【简易版】Linux下Protobuf 实现网络版通讯录--C++的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/482633

相关文章

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

Redis分片集群的实现

《Redis分片集群的实现》Redis分片集群是一种将Redis数据库分散到多个节点上的方式,以提供更高的性能和可伸缩性,本文主要介绍了Redis分片集群的实现,具有一定的参考价值,感兴趣的可以了解一... 目录1. Redis Cluster的核心概念哈希槽(Hash Slots)主从复制与故障转移2.

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

使用Python实现一键隐藏屏幕并锁定输入

《使用Python实现一键隐藏屏幕并锁定输入》本文主要介绍了使用Python编写一个一键隐藏屏幕并锁定输入的黑科技程序,能够在指定热键触发后立即遮挡屏幕,并禁止一切键盘鼠标输入,这样就再也不用担心自己... 目录1. 概述2. 功能亮点3.代码实现4.使用方法5. 展示效果6. 代码优化与拓展7. 总结1.

Mybatis 传参与排序模糊查询功能实现

《Mybatis传参与排序模糊查询功能实现》:本文主要介绍Mybatis传参与排序模糊查询功能实现,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、#{ }和${ }传参的区别二、排序三、like查询四、数据库连接池五、mysql 开发企业规范一、#{ }和${ }传参的

Docker镜像修改hosts及dockerfile修改hosts文件的实现方式

《Docker镜像修改hosts及dockerfile修改hosts文件的实现方式》:本文主要介绍Docker镜像修改hosts及dockerfile修改hosts文件的实现方式,具有很好的参考价... 目录docker镜像修改hosts及dockerfile修改hosts文件准备 dockerfile 文

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat

基于SpringBoot+Mybatis实现Mysql分表

《基于SpringBoot+Mybatis实现Mysql分表》这篇文章主要为大家详细介绍了基于SpringBoot+Mybatis实现Mysql分表的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录基本思路定义注解创建ThreadLocal创建拦截器业务处理基本思路1.根据创建时间字段按年进