三.网络版通讯录
2026/5/13 11:56:55 网站建设 项目流程

一.回顾

上篇博客链接:

https://blog.csdn.net/weixin_60668256/article/details/155931223?fromshare=blogdetail&sharetype=blogdetail&sharerId=155931223&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link

二.环境搭建

源码库地址:https://github.com/yhirose/cpp-httplib

镜像仓库: https://gitcode.net/mirrors/yhirose/cpp-httplib?utm_source=csdn_github_accelerator

"service" #include <iostream> #include <httplib.h> using std::cout; using std::endl; using std::cerr; using namespace httplib; int main() { cout << "--------服务启动--------" << endl; Server server; server.Post("/test-post", [](const Request& req, Response& res) { cout << "接收到post请求!" << endl; res.status = 200; }); server.Get("/test-get", [](const Request& req, Response& res) { cout << "接收到get请求!" << endl; res.status = 200; }); // 绑定 8123 端口,并且将端口号对外开放 server.listen("0.0.0.0", 8123); return 0; }
"client" #include <iostream> #include "httplib.h" using std::cout; using std::endl; using std::cerr; using namespace httplib; #define CONTACTS_HOST "192.168.205.128" #define CONTACTS_PORT 8123 int main() { Client cli(CONTACTS_HOST, CONTACTS_PORT); Result res1 = cli.Post("/test-post"); if (res1->status == 200) { cout << "调用post成功!" << endl; } Result res2 = cli.Get("/test-get"); if (res2->status == 200) { cout << "调用get成功!" << endl; } return 0; }

三.约定双端交互接口

四.约定双端交互req/resp

五.客户端代码实现

#include <iostream> #include "httplib.h" #include "ContactException.h" #include "add_contact.pb.h" using namespace std; using namespace httplib; #define CONTACTS_HOST "192.168.205.128" #define CONTACTS_PORT 8123 void buildAddContactRequest(add_contact::AddContactRequest& req) { cout << "---------------新增联系人-------------" << endl; cout << "请输入姓名:" << endl; string name; getline(cin,name); req.set_name(name); cout << "请输入联系人年龄" << endl; int age; cin >> age; req.set_age(age); cin.ignore(256,'\n'); for(int i = 0;;i++) { cout << "请输入" << i+1 << "个联系人电话: " << endl; string phone; getline(cin,phone); if(phone.empty()) { break; } add_contact::AddContactRequest::Phone* phone_number = req.add_phone(); phone_number->set_number(phone); cout << "请输入电话类型(1:移动电话,2:固定电话)" << endl; int type; cin >> type; cin.ignore(256,'\n'); switch(type) { case 1: phone_number->set_type(add_contact::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP); break; case 2: phone_number->set_type(add_contact::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL); break; default: cout << "输入错误,请重新输入!" << endl; break; } } cout << "--------------新增联系人成功-------------" << endl; } void addContact() { //构造req add_contact::AddContactRequest req; buildAddContactRequest(req); //序列化req std::string req_str; if(!req.SerializeToString(&req_str)) { throw ContactException("AddContactRequest 序列化失败!"); } //发起post调用 Client cli(CONTACTS_HOST, CONTACTS_PORT); auto res = cli.Post("/contacts/add", req_str, "application/protobuf"); if(!res) { string err_desc; err_desc.append("/contacts/add 链接失败!错误信息: "); err_desc.append(httplib::to_string(res.error())); throw ContactException(err_desc); } //反序列化 add_contact::AddContactResponse resp; if(!resp.ParseFromString(res->body) && res->status != 200) { string err_desc; err_desc.append("/contacts/add 调用失败"); err_desc.append(httplib::to_string(res.error())); throw ContactException(err_desc); } else if(res->status != 200) { string err_desc; err_desc.append("/contacts/add 调用失败, 状态码: "); err_desc.append(std::to_string(res->status)); err_desc.append("错误原因: ").append(resp.error_desc()); throw ContactException(err_desc); } else if(!resp.success()) { string err_desc; err_desc.append("/contacts/add 结果异常"); err_desc.append("异常原因: ").append(resp.error_desc()); throw ContactException(err_desc); } //结果打印 cout << "新增联系人成功! uid: " << resp.uid() << endl; } void delContact() { } void findAllContacts() { } void findOneContact() { } 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 = 1, DEL = 2, FIND_ALL = 3, FIND_ONE = 4, }; while(true) { menu(); cout << "---> 请选择: "; int option = 0; cin >> option; cin.ignore(256,'\n'); try { switch(option) { case OPTION::QUIT: cout << "退出通讯录客户端!" << endl; break; case OPTION::ADD: cout << "新增联系人!" << endl; addContact(); break; case OPTION::DEL: cout << "删除联系人!" << endl; delContact(); break; case OPTION::FIND_ALL: cout << "查看联系人列表!" << endl; findAllContacts(); break; case OPTION::FIND_ONE: cout << "查看联系人详细信息!" << endl; findOneContact(); break; default: cout << "无效的选择!" << endl; break; } } catch(const ContactException& e) { cout << "操作通讯录时发生异常" << endl; cout << "异常信息: " << e.what() << endl; } } return 0; }

六.服务器的实现

#include <iostream> #include <httplib.h> #include "add_contact.pb.h" using namespace std; using namespace httplib; class ContactException { private: std::string message; public: ContactException(const std::string& str = "A problem") : message(str) {} ~ContactException() = default; const std::string& what() const { return message; } }; void printContact(add_contact::AddContactRequest& request) { cout << "联系人姓名: " << request.name() << endl; cout << "联系人年龄: " << request.age() << endl; for (int j = 0; j < request.phone_size(); j++) { const add_contact::AddContactRequest_Phone& phone = request.phone(j); cout << " 联系人电话" << j+1 << ": " << phone.number(); cout << " (" << phone.PhoneType_Name(phone.type()) << ")" << endl; } } 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(); } int main() { cout << "--------服务启动--------" << endl; Server server; server.Post("/contacts/add", [](const Request& req, Response& res) { cout << "接收到post请求!" << endl; //反序列化req.body add_contact::AddContactRequest request; add_contact::AddContactResponse response; try { if(!request.ParseFromString(req.body)) { throw ContactException("AddContactRequest反序列化失败!"); } //新增联系人,持久化存储通讯录 -> 打印新建的联系人信息 printContact(request); //构造 response res.body response.set_success(true); response.set_uid(generate_hex(16)); //序列化res.body string response_str; if(!response.SerializeToString(&response_str)) { throw ContactException("AddContactResponse序列化失败!"); } res.body = response_str; res.status = 200; res.set_header("Content-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 << "/contacts/add 失败, 错误描述: " << e.what() << endl; } }); // 绑定 8123 端口,并且将端口号对外开放 server.listen("0.0.0.0", 8123); return 0; }

七.代码分析

1.服务端

2.客户端

其他的功能,有兴趣可以自己进行实现

八.总结

1.对比 PB 和 Json 的性能测试

"proto" syntax = "proto3"; package compare_serialization; import "google/protobuf/any.proto"; // 引⼊ any.proto ⽂件 // 地址 message Address{ string home_address = 1; // 家庭地址 string unit_address = 2; // 单位地址 } // 联系⼈ message PeopleInfo { string name = 1; // 姓名 int32 age = 2; // 年龄 message Phone { string number = 1; // 电话号码 enum PhoneType { MP = 0; // 移动电话 TEL = 1; // 固定电话 } PhoneType type = 2; // 类型 } repeated Phone phone = 3; // 电话 google.protobuf.Any data = 4; oneof other_contact { // 其他联系⽅式:多选⼀ string qq = 5; string weixin = 6; } map<string, string> remark = 7; // 备注 }
"compare.cc" #include <iostream> #include <sys/time.h> #include <jsoncpp/json/json.h> #include "contacts.pb.h" using namespace std; using namespace compare_serialization; using namespace google::protobuf; #define TEST_COUNT 100000 void createPeopleInfoFromPb(PeopleInfo *people_info_ptr); void createPeopleInfoFromJson(Json::Value& root); int main(int argc, char *argv[]) { struct timeval t_start,t_end; double time_used; int count; string pb_str, json_str; // ------------------------------Protobuf 序列化------------------------------------ { PeopleInfo pb_people; createPeopleInfoFromPb(&pb_people); count = TEST_COUNT; gettimeofday(&t_start, NULL); // 序列化count次 while ((count--) > 0) { pb_people.SerializeToString(&pb_str); } gettimeofday(&t_end, NULL); time_used=1000000*(t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec; cout << TEST_COUNT << "次 [pb序列化]耗时:" << time_used/1000 << "ms." << " 序列化后的大小:" << pb_str.length() << endl; } // ------------------------------Protobuf 反序列化------------------------------------ { PeopleInfo pb_people; count = TEST_COUNT; gettimeofday(&t_start, NULL); // 反序列化count次 while ((count--) > 0) { pb_people.ParseFromString(pb_str); } gettimeofday(&t_end, NULL); time_used=1000000*(t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec; cout << TEST_COUNT << "次 [pb反序列化]耗时:" << time_used / 1000 << "ms." << endl; } // ------------------------------JSON 序列化------------------------------------ { Json::Value json_people; createPeopleInfoFromJson(json_people); Json::StreamWriterBuilder builder; count = TEST_COUNT; gettimeofday(&t_start, NULL); // 序列化count次 while ((count--) > 0) { json_str = Json::writeString(builder, json_people); } gettimeofday(&t_end, NULL); // 打印序列化结果 // cout << "json: " << endl << json_str << endl; time_used=1000000*(t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec; cout << TEST_COUNT << "次 [json序列化]耗时:" << time_used/1000 << "ms." << " 序列化后的大小:" << json_str.length() << endl; } // ------------------------------JSON 反序列化------------------------------------ { Json::CharReaderBuilder builder; unique_ptr<Json::CharReader> reader(builder.newCharReader()); Json::Value json_people; count = TEST_COUNT; gettimeofday(&t_start, NULL); // 反序列化count次 while ((count--) > 0) { reader->parse(json_str.c_str(), json_str.c_str() + json_str.length(), &json_people, nullptr); } gettimeofday(&t_end, NULL); time_used=1000000*(t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec; cout << TEST_COUNT << "次 [json反序列化]耗时:" << time_used/1000 << "ms." << endl; } return 0; } /** * 构造pb对象 */ void createPeopleInfoFromPb(PeopleInfo *people_info_ptr) { people_info_ptr->set_name("张珊"); people_info_ptr->set_age(20); people_info_ptr->set_qq("95991122"); for(int i = 0; i < 5; i++) { PeopleInfo_Phone* phone = people_info_ptr->add_phone(); phone->set_number("110112119"); phone->set_type(PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP); } Address address; address.set_home_address("陕西省西安市长安区"); address.set_unit_address("陕西省西安市雁塔区"); google::protobuf::Any * data = people_info_ptr->mutable_data(); />

2.总结

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询