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

八、运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/214970.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jsp文件引用的css修改后刷新不生效问题

问题 在对 JavaWeb 项目修改的过程中&#xff0c;发现修改了 jsp 文件引入的 css 文件的代码后页面的样式没有更新的问题。 原因 导致这个问题的原因可能是因为浏览器缓存的问题。 解决方法 下面介绍两种解决方法&#xff0c;供大家参考&#xff1a; 1、给 link 标签的 c…

TrustZone之安全虚拟化

在Armv7-A首次引入虚拟化时,它仅在非安全状态中添加。在Armv8.3之前,Armv8也是如此,如下图所示: 如前所述在切换安全状态时,EL3用于托管固件和安全监视器。安全EL0/1托管受信任的执行环境(TEE),由受信任的服务和内核组成。 在安全状态下,没有对多个虚拟机的需…

Kafka生产问题总结及性能优化实践

1、消息丢失情况 消息发送端&#xff1a; &#xff08;1&#xff09;acks0&#xff1a; 表示producer不需要等待任何broker确认收到消息的回复&#xff0c;就可以继续发送下一条消息。性能最高&#xff0c;但是最容易丢消息。大数据统计报表场景&#xff0c;对性能要求很高&am…

JavaCV之rtmp推流(FLV和M3U8)

JavaCV与FFmpeg FFmpeg是一款开源的多媒体处理工具集&#xff0c;它包含了一系列用于处理音频、视频、字幕等多媒体数据的库和工具。 JavaCV集成了FFmpeg库&#xff0c;使得Java开发者可以使用FFmpeg的功能&#xff0c;比如视频解码、编码、格式转换等。 除了FFmpeg&#xff0…

LeetCode力扣每日一题(Java):35、搜索插入位置

一、题目 二、解题思路 1、我的思路&#xff08;又称&#xff1a;论API的重要性&#xff09; 读完题目之后&#xff0c;我心想这题目怎么看着这么眼熟&#xff1f;好像我之前学过的一个API呀&#xff01; 于是我回去翻了翻我之前写的博客&#xff1a;小白备战蓝桥杯&#xf…

通用的AGI 安全风险

传统安全风险 平台基础设施安全风险 模型与数据层安全风险 应用层安全风险 平台基础设施安全风险 &#xff08;1&#xff09;物理攻击&#xff1a;机房管控不到位 &#xff08;2&#xff09;网络攻击 &#xff08;3&#xff09;计算环境&#xff1a;自身安全漏洞&#xf…

编辑器Sublime text 常用快捷命令 列模式 替换空行

平替notepad 下载可取官网 www.sublimetext.com 据说可以无限试用&#xff0c;没有功能限制 1、快速删除空行 ctrl h选择正则表达式 .*Find输入&#xff1a; ^(\t)*$\nReplace输入&#xff1a;点击Replace All 2、快速选择指定字符 用鼠标选中alt f3修改 3、列编辑模式 ct…

宇视科技视频监控 main-cgi 文件信息泄露漏洞复现

0x01 产品简介 宇视(Uniview)高清网络摄像机是一种高性能的网络摄像机,它可以通过网络进行视频传输和监控。该摄像机采用先进的视频技术,具有高清晰度、低照度、宽动态等特点,能够提供高质量的视频图像。 0x02 漏洞概述 宇视(Uniview)高清网络摄像机存在信息泄露漏洞…

ppt编辑密码如何设置?

大家在PPT中设置了限制编辑&#xff0c;发现后面任然可以编辑文件。那么如何将PPT文件设置成禁止修改模式呢&#xff1f;今天分享几个方法给大家。 方法一 将PPT文件直接保存或者另存为一份文件&#xff0c;在保存时&#xff0c;将文件格式选择为PowerPoint图片演示文稿 方法…

.NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(测试篇)

WebAppDbTest 项目测试 测试工具 ltt介绍安装使用方式1、Drill2、Hammer3、Nailgun 测试主机规格配置CRUD 性能测试对比1、ltt 工具测试1.1、AddSingle 单条数据添加1.2、AddBulk 批量数据&#xff08;1000&#xff09;条添加1.3、GetSingle 单条数据查询1.4、GetAll 多条&…

多合一iPhone 解锁工具:iMyFone LockWiper iOS

多合一iPhone 解锁工具 无需密码解锁 iPhone/iPad/iPod touch 上所有类型的屏幕锁定 在几分钟内解锁 iPhone Apple ID、Touch ID 和 Face ID 立即绕过 MDM 并删除 iPhone/iPad/iPod touch 上的 MDM 配置文件 支持所有 iOS 版本和设备&#xff0c;包括最新的 iOS 17 和 iPhone 1…

JAVA实操经验

零&#xff1a; 按照需要&#xff0c;可以使用需要某个类下&#xff08;主要是java提供的&#xff09;的方法来实现某个功能。&#xff08;主要是用在不同类下的方法会进行重写功能不同&#xff09; 方法和构造方法不同&#xff1a;方法是方法&#xff0c;构造方法是构造器&a…

基于FPGA的视频接口之高速IO

简介 相对于其他视频接口来说,高速IO接口(以Xilinx公司为例,spartan 6系列的GTP、Artix7系列的GTP,KENTEX7系列的GTX和GTH等)具有简化设计、充分利用FPGA资源、降低设计成本等功能。 高速IO接口传输视频,一般会被拓展为万兆以太网、40G以太网、10G光纤、40G光纤、3G-SDI、…

c语言插入排序及希尔排序详解

目录 前言&#xff1a; 插入排序&#xff1a; 希尔排序&#xff1a; 前言&#xff1a; 排序在我们生活中无处不在&#xff0c;比如学生成就排名&#xff0c;商品价格排名等等&#xff0c;所以排序在数据结构的学习中尤为重要&#xff0c;今天就为大家介绍两个经典的排序算法&…

深入解析C++中的虚函数和虚继承:实现多态性与继承关系的高级特性

这里写目录标题 虚函数虚函数实现动态绑定虚继承抽象类 虚函数 虚函数是在C中用于实现多态性的一种特殊函数。它通过使用关键字"virtual"进行声明&#xff0c;在基类中定义&#xff0c;可在派生类中进行重写。虚函数允许在运行时根据对象的实际类型来调用相应的函数…

BigData之Google Hadoop中间件安装

前言 Hadoop / Zookeeper / Hbase 因资源有限 这三个都是安装在同一台Centos7.9的机器上 但通过配置 所以在逻辑上是distributed模式 1 Java安装 1.1 下载java11 tar/opt/java/jdk-11.0.5/ 1.2 环境配置修改 文件/etc/profile export JAVA_HOME/opt/java/jdk-11.0.5/ e…

HarmonyOS编译开源native库(OpenSSL实例)

前言 近期项目要开始做鸿蒙版本&#xff0c;有一部分依赖native的代码也需要迁移&#xff0c;某个native模块依赖openssl&#xff0c;需要在鸿蒙下重新编译openssl才行。一开始找了很多相关文档都没有得到方法&#xff0c;无奈只能自己凭经验慢慢试&#xff0c;最后还是成功了…

JS基础之执行上下文

JS基础之执行上下文 执行上下文顺序执行可执行代码执行上下文栈回顾上文 执行上下文 顺序执行 写个JavaScript的开发者都会有个直观的印象&#xff0c;那就是顺序执行&#xff1a; var foo function(){console.log(foo1) } foo(); //foo1 var foo function(){console.log(…

HTML面试题---专题一

文章目录 一、前言二、 HTML5 中 <header> 和 <footer> 标签的用途是什么&#xff1f;三、如何在 HTML 中嵌入 SVG&#xff08;可缩放矢量图形&#xff09;文件&#xff1f;四、解释 contenteditable 属性的用途五、如何创建随屏幕尺寸缩放的响应式图像&#xff1f…

线上扭蛋机小程序搭建,扭蛋与科技的完美结合

扭蛋机作为当下比较热门的一种盲盒玩法&#xff0c;在年轻人群体中非常受欢迎。随着经济的增长和人们生活水平的提高&#xff0c;人们对娱乐消费需求也在增加&#xff0c;扭蛋机的受众群体也在扩大。 目前线上扭蛋机小程序也获得了大众的青睐&#xff0c;扭蛋机小程序就是把线…