网络版本的通讯录青春版(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 的信号经过输入滤液和边缘检测器 外部时钟源…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
Windows 下端口占用排查与释放全攻略
Windows 下端口占用排查与释放全攻略 在开发和运维过程中,经常会遇到端口被占用的问题(如 8080、3306 等常用端口)。本文将详细介绍如何通过命令行和图形化界面快速定位并释放被占用的端口,帮助你高效解决此类问题。 一、准…...
python打卡第47天
昨天代码中注意力热图的部分顺移至今天 知识点回顾: 热力图 作业:对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图,展示模…...
ZYNQ学习记录FPGA(二)Verilog语言
一、Verilog简介 1.1 HDL(Hardware Description language) 在解释HDL之前,先来了解一下数字系统设计的流程:逻辑设计 -> 电路实现 -> 系统验证。 逻辑设计又称前端,在这个过程中就需要用到HDL,正文…...
Python异步编程:深入理解协程的原理与实践指南
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 持续学习,不断…...
ubuntu2404 gpu 没接显示器,如何保证远程显示的分辨率
1. 使用 xserver-xorg-video-dummy 创建虚拟显示器 如果系统在无物理显示器连接时无法识别显示输出,可以使用 xserver-xorg-video-dummy 驱动程序创建虚拟显示器。以下是设置步骤: 安装虚拟显示器驱动程序: sudo apt install xserver-xorg-v…...
