【简易版】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

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Nginx部署HTTP/3的实现步骤

《Nginx部署HTTP/3的实现步骤》本文介绍了在Nginx中部署HTTP/3的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前提条件第一步:安装必要的依赖库第二步:获取并构建 BoringSSL第三步:获取 Nginx

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima