网络版本的通讯录青春版(protobuf)
环境搭建
这样四个功能。
但是在这里我们只实现新增联系人的功能。
对于服务器端,我们同样应该具备增删改查的功能,这里我们也只实现增的功能。
这里我们需要用到一个Httplib库:
双方通信的流程
约定双方交互req/rsp
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的性能与使用场景
XML、JSON 的应⽤场景更为丰富。
相关文章:

网络版本的通讯录青春版(protobuf)
环境搭建 Protobuf 还常⽤于通讯协议、服务端数据交换场景。 因为我们主要目的只是为了学习protobuf,因此对于客户端,原本应该具备: 新增⼀个联系⼈ ◦ 删除⼀个联系⼈ ◦ 查询通讯录列表 ◦ 查询⼀个联系⼈的详细信息 这样四个功能。 …...
开源模型应用落地-安全合规篇-用户输入价值观判断(三)
一、前言 在深度合规功能中,对用户输入内容的价值观判断具有重要意义。这一功能不仅仅是对信息合法性和合规性的简单审核,更是对信息背后隐含的伦理道德和社会责任的深刻洞察。通过对价值观的判断,系统能够识别可能引发不当影响或冲突的内容,从而为用户提供更安全、更和谐的…...

神经网络入门实战:(十四)pytorch 官网内置的 CIFAR10 数据集,及其网络模型
(一) pytorch 官网内置的网络模型 图像处理: Models and pre-trained weights — Torchvision 0.20 documentation (二) CIFAR10数据集的分类网络模型(仅前向传播): 下方的网络模型图片有误,已做修改,具…...

【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中运行的时候报错了: 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:证书标准pem和der:两种(包括公私钥、证书签名请求、证书等内容的)的格式,前者是文本形式,linux常用,后者是二进制形式,windows常用,仅仅是格式&…...
实例分割详解
实例分割详解 引言 实例分割是计算机视觉领域的一项复杂任务,它要求模型能够识别图像中不同类别的对象,并对每个单独的对象进行像素级别的分类。与语义分割不同的是,实例分割不仅要区分不同的类别,还要识别同一类别中的不同个体…...

D87【python 接口自动化学习】- pytest基础用法
day87 pytest运行参数 -m -k 学习日期:20241203 学习目标:pytest基础用法 -- pytest运行参数-m -k 学习笔记: 常用运行参数 pytest运行参数-m -k pytest -m 执行特定的测试用例,markers最好使用英文 [pytest] testpaths./te…...

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

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

30.100ASK_T113-PRO 用QT编写视频播放器(一)
1.再buildroot中添加视频解码库 X264, 执行 make menuconfig Target packages -->Libraries --> Multimedia --> X264 CLI 还需要添加 FFmpeg 2. 保存,重新编译 make all 3.将镜像下载开发板...

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

opencvocr识别手机摄像头拍摄的指定区域文字,文字符合规则就语音报警
安装python,pycharm,自行安装。 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)用户管理子服务,网关子服务
用户管理子服务(user文件) 用户管理子服务也是这个项目中的一个业务最多的子服务,接口多,但是主要涉及的数据表只有user表,Redis的键值对和ES的一个搜索引擎,主要功能是对用户的个人信息进行修改管理&#…...

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

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

虚幻引擎---材质篇
一、基础知识 虚幻引擎中的材质(Materials) 定义了场景中对象的表面属性,包括颜色、金属度、粗糙度、透明度等等;可以在材质编辑器中可视化地创建和编辑材质;虚幻引擎的渲染管线的着色器是用高级着色语言(…...
NPM镜像详解
NPM镜像详解 什么是NPM镜像 NPM镜像(NPM Mirror)是一个完整的NPM包的副本服务器。由于npm的官方registry服务器部署在国外,国内访问可能会比较慢,因此使用镜像可以加快包的下载速度。 常用的NPM镜像源 npm官方镜像 https://reg…...

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

STM32进阶 定时器3 通用定时器 案例1:LED呼吸灯——PWM脉冲
功能 它有基本定时器所有功能,还增加以下功能 TIM2、TIM3、TIM4、TIM5 多种时钟源: 外部时钟源模式1: 每个定时器有四个输入通道 只有通道1和通道2的信号可以作为时钟信号源 通道1 和通道2 的信号经过输入滤液和边缘检测器 外部时钟源…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...