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

相关文章

linux本机进程间通信之UDS详解

《linux本机进程间通信之UDS详解》文章介绍了Unix域套接字(UDS)的使用方法,这是一种在同一台主机上不同进程间通信的方式,UDS支持三种套接字类型:SOCK_STREAM、SOCK_DGRA... 目录基础概念本机进程间通信socket实现AF_INET数据收发示意图AF_Unix数据收发流程图A

Python如何实现读取csv文件时忽略文件的编码格式

《Python如何实现读取csv文件时忽略文件的编码格式》我们再日常读取csv文件的时候经常会发现csv文件的格式有多种,所以这篇文章为大家介绍了Python如何实现读取csv文件时忽略文件的编码格式... 目录1、背景介绍2、库的安装3、核心代码4、完整代码1、背景介绍我们再日常读取csv文件的时候经常

Golang中map缩容的实现

《Golang中map缩容的实现》本文主要介绍了Go语言中map的扩缩容机制,包括grow和hashGrow方法的处理,具有一定的参考价值,感兴趣的可以了解一下... 目录基本分析带来的隐患为什么不支持缩容基本分析在 Go 底层源码 src/runtime/map.go 中,扩缩容的处理方法是 grow

Go 1.23中Timer无buffer的实现方式详解

《Go1.23中Timer无buffer的实现方式详解》在Go1.23中,Timer的实现通常是通过time包提供的time.Timer类型来实现的,本文主要介绍了Go1.23中Timer无buff... 目录Timer 的基本实现无缓冲区的实现自定义无缓冲 Timer 实现更复杂的 Timer 实现总结在

基于Python实现多语言朗读与单词选择测验

《基于Python实现多语言朗读与单词选择测验》在数字化教育日益普及的今天,开发一款能够支持多语言朗读和单词选择测验的程序,对于语言学习者来说无疑是一个巨大的福音,下面我们就来用Python实现一个这... 目录一、项目概述二、环境准备三、实现朗读功能四、实现单词选择测验五、创建图形用户界面六、运行程序七、

linux环境openssl、openssh升级流程

《linux环境openssl、openssh升级流程》该文章详细介绍了在Ubuntu22.04系统上升级OpenSSL和OpenSSH的方法,首先,升级OpenSSL的步骤包括下载最新版本、安装编译... 目录一.升级openssl1.官网下载最新版openssl2.安装编译环境3.下载后解压安装4.备份

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计

C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)

《C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)》本文主要介绍了C#集成DeepSeek模型实现AI私有化的方法,包括搭建基础环境,如安装Ollama和下载DeepS... 目录前言搭建基础环境1、安装 Ollama2、下载 DeepSeek R1 模型客户端 ChatBo

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、