当前位置: 首页 > news >正文

网络版本的通讯录青春版(protobuf)

环境搭建

Protobuf 还常⽤于通讯协议、服务端数据交换场景。
因为我们主要目的只是为了学习protobuf,因此对于客户端,原本应该具备:
新增⼀个联系⼈
删除⼀个联系⼈
查询通讯录列表
查询⼀个联系⼈的详细信息

这样四个功能。

但是在这里我们只实现新增联系人的功能。

对于服务器端,我们同样应该具备增删改查的功能,这里我们也只实现增的功能。

这里我们需要用到一个Httplib库:

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

双方通信的流程 

约定双方交互req/rsp

base_response.proto
syntax = "proto3";
package base_response;message BaseResponse
{bool succes = 1;    // 返回结果string error_desc = 2; // 状态码
}

add_contact_request.proto

syntax = "proto3";
package add_contact_request;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_numbers = 3; // 电话号码map<string,string> remark = 4; // 备注
}

add_contact_response.proto

syntax = "proto3";
package add_contact_response;
import "base_response.proto"; // 引入base_responsemessage AddContactResponse 
{base_response.BaseResponse base_resp = 1;string uid = 2; // 联系人的uid
}

客户端部分:
 

定义一个异常类,用于抛异常:
ContactException.h

#pragma once#include <iostream>class ContactException
{
private:std::string message;
public:ContactException(std::string str = "A Problem") : message(str) {}std::string What() const { return message; }
};

 client.cc

#include <iostream>
#include "../add_contact_request.pb.h"
#include "../add_contact_response.pb.h"
#include "../../cpp-httplib/httplib.h"
#include "ContactException.h"using namespace std;
using namespace httplib;
using namespace add_contact_request;
using namespace add_contact_response;const string IP = "127.0.0.1";
const uint16_t PORT = 8080;void addContact();void menu()
{cout << "----------------网络通讯录------------------" << endl;cout << "选择: 1.添加联系人  2. 删除联系人  3.查找所有联系人  4. 查找一个联系人  0.退出" << endl;
}void addContact()
{httplib::Client cli(IP, PORT);AddContactRequest info;cout << "------------添加联系人-------------" << endl;cout << "请输入联系人姓名: " << endl;string name;getline(cin, name);info.set_name(name);cout << "请输入联系人年龄: " << endl;int32_t age;cin >> age;cin.ignore(256, '\n'); // 清空一下缓冲区// info->set_age(age);string numbers;for (int i = 1;; ++i){cout << "请输入联系人的第" << i << "个号码(直接回车终止输入)" << endl;getline(cin, numbers);if (numbers.empty())break;cout << "请输入这个电话的类型(1/移动电话 2/固定电话): " << endl;int num = 0;cin >> num;cin.ignore(256, '\n'); // 清空以下缓冲区add_contact_request::AddContactRequest_Phone *phone = info.add_phone_numbers();phone->set_number(numbers); // info->add_phones()返回的是一块开辟好空间的地址// 为了代码的可扩展性,这里使用switch而不是if语句switch (num){case 1:phone->set_type(add_contact_request::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP);break;case 2:phone->set_type(add_contact_request::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL);break;default:cout << "选择有误,取默认类型" << endl;break;}}for (int i = 1;; ++i){cout << "请输入备注" << i << "标题(直接回车退出输入)" << endl;string remark_key;getline(cin, remark_key);if (remark_key.empty())break;cout << "请输入备注" << i << "内容" << endl;string remark_val;getline(cin, remark_val);info.mutable_remark()->insert({remark_key, remark_val});}string str_req;if (!info.SerializePartialToString(&str_req)){// 失败抛异常ContactException e("str_req序列化失败!");throw(e);}// 发起Post请求auto res = cli.Post("/contacts/add", str_req, "application/protobuf");if (!res){// 失败抛异常string str_error = "发起post请求失败,错误类型: ";str_error += httplib::to_string(res.error());ContactException e(str_error);throw(e);}// 反序列化responseAddContactResponse resp;bool parse = resp.ParseFromString(res->body);if(res->status != 200 && !parse){string err_desc;err_desc += "post contact/add 失败: ";err_desc += std::to_string(res->status);err_desc += "(";err_desc += res->reason;err_desc += ")";throw ContactException(err_desc);}else if(res->status != 200){string err_desc;err_desc += "post contact/add 失败: ";err_desc += std::to_string(res->status);err_desc += "(";err_desc += res->reason;err_desc += ")";err_desc += resp.base_resp().error_desc();throw ContactException(err_desc);}else if(!parse){string err_desc;err_desc += "post contact/add 反序列化失败: ";err_desc += resp.base_resp().error_desc();throw ContactException(err_desc);}cout << "新增联系人成功,uid = " << resp.uid() << endl;
}int main()
{while (true){menu();int choose;cin >> choose;cin.ignore(256, '\n');try{switch (choose){case 1:addContact();break;case 2:case 3:case 4:break;case 0:cout << "程序退出" << endl;exit(0);default:cout << "选择有误" << endl;exit(-1);break;}}catch (const ContactException &e){cerr << "捕获一个异常:操作通讯录时!" << endl;cerr << "信息: " << e.What() << endl;}}return 0;
}

makefile:
 

all:client
client:g++ client.cc ../add_contact_request.pb.cc ../add_contact_response.pb.cc ../base_response.pb.cc -o client -std=c++11 -lpthread -lprotobuf.PHONY:clean
clean:rm -f client

 

服务器部分:
 

这里为了简单,偷个懒,没有使用异常了,程序出错就直接退出

server.cc

#include <iostream>
#include <random>
#include "../../cpp-httplib/httplib.h"
#include "../add_contact_request.pb.h"
#include "../add_contact_response.pb.h"using namespace std;
using namespace httplib;
using namespace add_contact_request;
using namespace add_contact_response;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();
}void PrintPeopelInfo(const AddContactRequest &people_info)
{cout << "-----------新增联系人信息---------------" << endl;cout << "姓名: " << people_info.name() << endl;// cout << "年龄: " << people_info.age() << endl;// 打印电话for (int j = 0; j < people_info.phone_numbers().size(); ++j){const add_contact_request::AddContactRequest_Phone &phones = people_info.phone_numbers(j);cout << "第" << j + 1 << "个电话: " << phones.number();int num = phones.type();switch (num){case add_contact_request::AddContactRequest_Phone::PhoneType::AddContactRequest_Phone_PhoneType_MP:cout << "(移动电话MP)" << endl;break;case add_contact_request::AddContactRequest_Phone::PhoneType::AddContactRequest_Phone_PhoneType_TEL:cout << "(固定电话TEL)" << endl;break;default:cerr << "未知类型" << endl;break;}}// 打印备注信息int count = 1;for (auto &pair : people_info.remark()){cout << "备注信息" << count++ << ": ";cout << "(" << pair.first << ") : " << pair.second << endl;;}// 打印未知字段const google::protobuf::Reflection *reflection = people_info.GetReflection();const google::protobuf::UnknownFieldSet &unknown_field_set = reflection->GetUnknownFields(people_info);for (int j = 0; j < unknown_field_set.field_count(); ++j){const google::protobuf::UnknownField &unknown_field = unknown_field_set.field(j);cout << "未知字段" << j + 1<< " 字段编号: " << unknown_field.number()<< " 字段类型: " << unknown_field.type();switch (unknown_field.type()){case google::protobuf::UnknownField::Type::TYPE_VARINT:cout << " 值: " << unknown_field.varint() << endl;break;case google::protobuf::UnknownField::Type::TYPE_LENGTH_DELIMITED:cout << " 值: " << unknown_field.length_delimited() << endl;break;default:cout << "未知" << endl;break;}}cout << "新增联系人成功" << endl;
}int main()
{cout << "----------服务器启动!----------" << endl;httplib::Server svr;// 先设置方法,再进行监听svr.Post("/contacts/add", [](const Request &req, Response &resp){AddContactRequest contact_req;string str_req;if (!contact_req.ParseFromString(req.body)){cerr << "/contacts/add Post,反序列化失败!" << endl;exit(-1);}PrintPeopelInfo(contact_req); // 打印新增联系人信息// 准备响应信息// 这里为了简单就硬编码设置了AddContactResponse contact_resp;contact_resp.set_uid(generate_hex(8));string str_resp;if(!contact_resp.SerializePartialToString(&str_resp)){cerr << "序列化响应信息失败,程序退出!" << endl;exit(-1);}resp.status = 200;resp.body = str_resp;resp.set_header("Content-Type", "application/protobuf");cout << "响应构建成功,准备发送!" << endl;});svr.listen("0.0.0.0",8080);return 0;
}

在上述过程中,设计了一个能随机生成uid的函数,可以了解一下。

makefile:

all:server
server:g++ server.cc ../add_contact_request.pb.cc ../add_contact_response.pb.cc ../base_response.pb.cc -o server -std=c++11 -lpthread -lprotobuf.PHONY:clean
clean:rm -f server

 演示效果:

客户端的新增联系人功能:

这里也简化了对服务器发送过来的响应的处理,我们只打印了服务器给我们返回的联系人的uid

服务器端的新增联系人功能:

 

服务器受到信息后,就会对客户端那边设置的信息进行一个打印,然后把生成的uid发送给客户端

总结:ProtoBuf的性能与使用场景

1. XML、JSON、ProtoBuf 都具有数据结构化和数据序列化的能⼒。
2. XML、JSON 更注重数据结构化,关注可读性和语义表达能⼒。ProtoBuf 更注重数据序列化,关注 效率、空间、速度,可读性差,语义表达能⼒不⾜,为保证极致的效率,会舍弃⼀部分元信息。
3. ProtoBuf 的应⽤场景更为明确, 适合⾼性能,对响应 速度有要求的数据传 输场景。

XML、JSON 的应⽤场景更为丰富。

 

 

 

相关文章:

网络版本的通讯录青春版(protobuf)

环境搭建 Protobuf 还常⽤于通讯协议、服务端数据交换场景。 因为我们主要目的只是为了学习protobuf&#xff0c;因此对于客户端&#xff0c;原本应该具备&#xff1a; 新增⼀个联系⼈ ◦ 删除⼀个联系⼈ ◦ 查询通讯录列表 ◦ 查询⼀个联系⼈的详细信息 这样四个功能。 …...

开源模型应用落地-安全合规篇-用户输入价值观判断(三)

一、前言 在深度合规功能中,对用户输入内容的价值观判断具有重要意义。这一功能不仅仅是对信息合法性和合规性的简单审核,更是对信息背后隐含的伦理道德和社会责任的深刻洞察。通过对价值观的判断,系统能够识别可能引发不当影响或冲突的内容,从而为用户提供更安全、更和谐的…...

神经网络入门实战:(十四)pytorch 官网内置的 CIFAR10 数据集,及其网络模型

(一) pytorch 官网内置的网络模型 图像处理&#xff1a; Models and pre-trained weights — Torchvision 0.20 documentation (二) CIFAR10数据集的分类网络模型&#xff08;仅前向传播&#xff09;&#xff1a; 下方的网络模型图片有误&#xff0c;已做修改&#xff0c;具…...

【Rust在WASM中实现pdf文件的生成】

Rust在WASM中实现pdf文件的生成 前言概念和依赖问题描述分步实现pdf转Blob生成URL两种方式利用localstorage传递参数处理图片Vec<u8>到pdf格式的Vec<u8>使用rust创建iframe显示pdf的Blob最后 前言 实现了一个通用的前端jpg转pdf的wasm,因为动态响应框架无法直接打…...

在MySQL中执行sum case when报错:SUM does not exist

1. 报错 在pgsql中能正常运行的一段SQL在MySQL中运行的时候报错了&#xff1a; SELECT DATE( hr.handle_time ) AS statsDate,SUM ( CASE WHEN hma.app_type IN ( 2, 5 ) THEN ch_money ELSE 0 END ) AS aliPayAmt,SUM ( CASE WHEN hma.app_type IN ( 1, 4 ) THEN ch_money EL…...

【openssl】相关指令

熟悉下相关概念 x509&#xff1a;证书标准pem和der&#xff1a;两种&#xff08;包括公私钥、证书签名请求、证书等内容的&#xff09;的格式&#xff0c;前者是文本形式&#xff0c;linux常用&#xff0c;后者是二进制形式&#xff0c;windows常用&#xff0c;仅仅是格式&…...

实例分割详解

实例分割详解 引言 实例分割是计算机视觉领域的一项复杂任务&#xff0c;它要求模型能够识别图像中不同类别的对象&#xff0c;并对每个单独的对象进行像素级别的分类。与语义分割不同的是&#xff0c;实例分割不仅要区分不同的类别&#xff0c;还要识别同一类别中的不同个体…...

D87【python 接口自动化学习】- pytest基础用法

day87 pytest运行参数 -m -k 学习日期&#xff1a;20241203 学习目标&#xff1a;pytest基础用法 -- pytest运行参数-m -k 学习笔记&#xff1a; 常用运行参数 pytest运行参数-m -k pytest -m 执行特定的测试用例&#xff0c;markers最好使用英文 [pytest] testpaths./te…...

浅谈MySQL路由

华子目录 mysql-router介绍下载mysql-router安装mysql-router实验 mysql-router介绍 mysql-router是一个对应用程序透明的InnoDB Cluster连接路由服务&#xff0c;提供负载均衡、应用连接故障转移和客户端路由利用路由器的连接路由特性&#xff0c;用户可以编写应用程序来连接到…...

matlab中disp,fprintf,sprintf,display,dlmwrite输出函数之间的区别

下面是他们之间的区别&#xff1a; disp函数与fprintf函数的区别 输出格式的灵活性 disp函数&#xff1a;输出格式相对固定。它会自动将变量以一种比较直接的方式显示出来。对于数组&#xff0c;会按照行列形式展示&#xff1b;对于字符串&#xff0c;直接原样输出并换行。例如…...

30.100ASK_T113-PRO 用QT编写视频播放器(一)

1.再buildroot中添加视频解码库 X264, 执行 make menuconfig Target packages -->Libraries --> Multimedia --> X264 CLI 还需要添加 FFmpeg 2. 保存,重新编译 make all 3.将镜像下载开发板...

Linux-GPIO应用编程

本章介绍应用层如何控制 GPIO&#xff0c;譬如控制 GPIO 输出高电平、或输出低电平。 只要是用到GPIO的外设&#xff0c;都有可能用得到这些操作方法。 照理说&#xff0c;GPIO的操作应该是由驱动层去做的&#xff0c;使用寄存器操作或者GPIO子系统之类的框架。 但是&#xff0…...

opencvocr识别手机摄像头拍摄的指定区域文字,文字符合规则就语音报警

安装python&#xff0c;pycharm&#xff0c;自行安装。 Python下安装OpenCv 2.1 打开cmd,先安装opencv-python pip install opencv-python --user -i https://pypi.tuna.tsinghua.edu.cn/simple2.2 再安装opencv-contrib-python pip install opencv-contrib-python --user …...

微服务即时通讯系统(5)用户管理子服务,网关子服务

用户管理子服务&#xff08;user文件&#xff09; 用户管理子服务也是这个项目中的一个业务最多的子服务&#xff0c;接口多&#xff0c;但是主要涉及的数据表只有user表&#xff0c;Redis的键值对和ES的一个搜索引擎&#xff0c;主要功能是对用户的个人信息进行修改管理&#…...

postgreSQL安装后启动有The application server could not be contacted问题

不得不说pgsql是真的麻烦,找问题找了几个小时才解决.直接步入主题吧 首先问题如下 安装后,双击启动就出现上述问题 首先删除路径为 c:\Users\your_name\AppData\Roaming\pgAdmin 之内的所有文件和文件夹, 如果找不到AppData,就把这个点开 接着找到你安装pgsql的路径,我的是D…...

架构05-架构安全性

零、文章目录 架构05-架构安全性 1、软件架构安全的重要性 **系统安全&#xff1a;**不仅包括防御黑客攻击&#xff0c;还包括安全备份与恢复、安全审计、防治病毒等。**关注重点&#xff1a;**认证、授权、凭证、保密、传输安全、验证。 2、认证&#xff08;Authenticatio…...

虚幻引擎---材质篇

一、基础知识 虚幻引擎中的材质&#xff08;Materials&#xff09; 定义了场景中对象的表面属性&#xff0c;包括颜色、金属度、粗糙度、透明度等等&#xff1b;可以在材质编辑器中可视化地创建和编辑材质&#xff1b;虚幻引擎的渲染管线的着色器是用高级着色语言&#xff08;…...

NPM镜像详解

NPM镜像详解 什么是NPM镜像 NPM镜像&#xff08;NPM Mirror&#xff09;是一个完整的NPM包的副本服务器。由于npm的官方registry服务器部署在国外&#xff0c;国内访问可能会比较慢&#xff0c;因此使用镜像可以加快包的下载速度。 常用的NPM镜像源 npm官方镜像 https://reg…...

从智能合约到去中心化AI:Web3的技术蓝图

Web3正在成为互联网发展的重要方向&#xff0c;其核心理念是去中心化、用户主权和自治。随着区块链技术、智能合约以及人工智能&#xff08;AI&#xff09;等技术的发展&#xff0c;Web3不仅重新定义了数据存储和交易方式&#xff0c;还为更智能化、去中心化的数字生态系统铺平…...

STM32进阶 定时器3 通用定时器 案例1:LED呼吸灯——PWM脉冲

功能 它有基本定时器所有功能&#xff0c;还增加以下功能 TIM2、TIM3、TIM4、TIM5 多种时钟源&#xff1a; 外部时钟源模式1&#xff1a; 每个定时器有四个输入通道 只有通道1和通道2的信号可以作为时钟信号源 通道1 和通道2 的信号经过输入滤液和边缘检测器 外部时钟源…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

【堆垛策略】设计方法

堆垛策略的设计是积木堆叠系统的核心&#xff0c;直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法&#xff0c;涵盖基础规则、优化算法和容错机制&#xff1a; 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则&#xff1a; 大尺寸/重量积木在下&#xf…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...