网络数据通信—ProtoBuf实现序列化和反序列化

这篇具有很好参考价值的文章主要介绍了网络数据通信—ProtoBuf实现序列化和反序列化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言

1.环境搭建

2. centos下编写的注意事项

3.约定双端交互接口

4.约定双端交互req/resp

5. 客户端代码实现

6.服务端代码实现


前言

Protobuf还常用于通讯协议、服务端数据交换场景。那么在这个示例中,我们将实现一个网络版本的通讯录,模拟实现客户端与服务端的交互,通过Protobuf来实现各端之间的协议序列化。
需求如下:

●客户端可以选择对通讯录进行以下操作:
●新增一个联系人
●删除一个联系人
●查询通讯录列表
●查询一个联系人的详细信息

●服务端提供增删查能力,并需要持久化通讯录。
●客户端、服务端间的交互数据使用Protobuf来完成。

1.环境搭建

Httplib库: cpp-httplib 是个开源的库,是一个c++封装的http库,使用这个库可以在linux、windows平台下完成http客户端、http服务端的搭建。使用起来非常方便,只需要包含头文件httplib.h即可。编译程序时,需要带上-lpthread选项。
源码库地址: https://github.com/yhirose/cpp-httplib

2. centos下编写的注意事项

如果使用centOS环境,yum源带的g++最新版本是4.8.5,发布于2015年,年代久远。编译该项目会出现异常。将gcc/g++升级为更高版本可解决问题。

# 升级参考:https://juejin.cn/post/6844903873111392263
# 安装gcc 8版本
yum install -y devtoolset-8-gcc devtoolset-8-gcc-c++
# 启⽤版本
source /opt/rh/devtoolset-8/enable
# 查看版本已经变成gcc 8.3.1
gcc -v

3.约定双端交互接口

新增一个联系人:

[请求]
        Post /contacts/add AddContactRequest
        Content-Type: application/protobuf
[响应]
        AddContactResponse
        Content-Type: application/protobuf 

删除一个联系人:

[请求]
        Post /contacts/del DelContactRequest
        Content-Type: application/protobuf
[响应]
        DelContactResponse
        Content-Type: application/protobuf

查询通讯录列表:

[请求]
        GET /contacts/find-all
[响应]
        FindAllContactsResponse
        Content-Type: application/protobuf

查询一个联系人的详细信息:

[请求]
        Post /contacts/find-one FindOneContactRequest
        Content-Type: application/protobuf
[响应]
        FindOneContactResponse
        Content-Type: application/protobuf

4.约定双端交互req/resp

base_response.proto

syntax = "proto3";
package base_response;
message BaseResponse {
    bool success = 1; // 返回结果
    string error_desc = 2; // 错误描述
}

add_contact_request.proto

syntax = "proto3";
package add_contact_req;
// 新增联系⼈ 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; // 电话
    map<string, string> remark = 4; // 备注
}

add_contact_response.proto

syntax = "proto3";
package add_contact_resp;
import "base_response.proto"; // 引⼊base_response
message AddContactResponse {
    base_response.BaseResponse base_resp = 1;
    string uid = 2;
}

del_contact_request.proto

syntax = "proto3"; 
package del_contact_req;
// 删除⼀个联系⼈ req
message DelContactRequest {
    string uid = 1; // 联系⼈ID
}

del_contact_response.proto

syntax = "proto3";
package del_contact_resp;
import "base_response.proto"; // 引⼊base_response
// 删除⼀个联系⼈ resp
message DelContactResponse {
    base_response.BaseResponse base_resp = 1;
    string uid = 2;
}

find_one_contact_request.proto

syntax = "proto3";
package find_one_contact_req;
// 查询⼀个联系⼈ req
message FindOneContactRequest {
    string uid = 1; // 联系⼈ID
}

find_one_contact_response.proto

syntax = "proto3";
package find_one_contact_resp;
import "base_response.proto"; // 引⼊base_response
// 查询⼀个联系⼈ resp
message FindOneContactResponse {
    base_response.BaseResponse base_resp = 1;
    string uid = 2; // 联系⼈ID
    string name = 3; // 姓名
    int32 age = 4; // 年龄
    message Phone {
        string number = 1; // 电话号码
        enum PhoneType {
            MP = 0; // 移动电话
            TEL = 1; // 固定电话
        }
        PhoneType type = 2; // 类型
    }
    repeated Phone phone = 5; // 电话
    map<string, string> remark = 6; // 备注
}

find_all_contacts_response.proto

syntax = "proto3";
package find_all_contacts_resp;
import "base_response.proto"; // 引⼊base_response
// 联系⼈摘要信息
message PeopleInfo {
    string uid = 1; // 联系⼈ID
    string name = 2; // 姓名
}
// 查询所有联系⼈ resp
message FindAllContactsResponse {
    base_response.BaseResponse base_resp = 1;
    repeated PeopleInfo contacts = 2;
}

5. 客户端代码实现

main.cc

#include <iostream>
#include "ContactsServer.h"
#include "ContactException.h"

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 OPERATE {ADD=1, DEL, FIND_ALL, FIND_ONE};
    ContactsServer contactsServer;
    while (true) {
        menu();
        std::cout << "---> 请选择:";
        int choose;
        std::cin >> choose;
        std::cin.ignore(256, '\n');
        try {
            switch (choose) {
                case OPERATE::ADD:
                    contactsServer.addContact();
                    break;
                case OPERATE::DEL:
                    contactsServer.delContact();
                    break;
                case OPERATE::FIND_ALL:
                    contactsServer.findContacts();
                    break;
                case OPERATE::FIND_ONE:
                    contactsServer.findContact();
                    break;
                case 0:
                    std::cout << "---> 程序已退出" << std::endl;
                    return 0;
                default:
                    std::cout << "---> 无此选项,请重新选择!" << std::endl;
                    break;
            }
        } catch (const ContactException& e) {
            std::cerr << "---> 操作通讯录时发现异常!!!" << std::endl
                 << "---> 异常信息:" << e.what() << std::endl;
        } catch (const std::exception& e) {
            std::cerr << "---> 操作通讯录时发现异常!!!" << std::endl
                << "---> 异常信息:" << e.what() << std::endl;
        }
    }
}

ContactException.h:定义异常类

// 自定义异常类
class ContactException
{
private:
    std::string message;

public:
    ContactException(std::string str = "A problem") : message{str} {}
    std::string what() const { return message; }
};

ContactsServer.h:客户端通讯录服务端定义

#include <iostream>
#include "./request/add_contact_request.pb.h"
#include "./response/add_contact_response.pb.h"
#include "./request/find_one_contact_request.pb.h"
#include "./response/find_one_contact_response.pb.h"
#include "./response/find_all_contacts_response.pb.h"
#include "./request/del_contact_request.pb.h"
#include "./response/del_contact_response.pb.h"

class ContactsServer
{
public:
    void addContact();
    void delContact();
    void findContacts();
    void findContact();
private:
    void buildAddContactRequest(add_contact_req::AddContactRequest* req);
    void printFindOneContactResponse(find_one_contact_resp::FindOneContactResponse& resp);
    void printFindAllContactsResponse(find_all_contacts_resp::FindAllContactsResponse& resp);
};

ContactsServer.cc:客户端通讯录服务实现

#include "ContactsServer.h"
#include "ContactException.h"
#include "httplib.h"

#define CONTACTS_IP "139.159.150.152"
#define CONTACTS_PORT 8123


void ContactsServer::addContact() {
    httplib::Client cli(CONTACTS_IP, CONTACTS_PORT);
    // 构建 request 请求
    add_contact_req::AddContactRequest req;
    buildAddContactRequest(&req);

    // 序列化 request
    std::string req_str;
    if (!req.SerializeToString(&req_str)) {
        throw ContactException("AddContactRequest序列化失败!");
    }

    // 发起 post 请求
    auto res = cli.Post("/contacts/add", req_str, "application/protobuf");
    if (!res) {
        std::string err_desc;
        err_desc.append("/contacts/add 链接错误!错误信息:")
                .append(httplib::to_string(res.error()));
        throw ContactException(err_desc);
    }

    // 反序列化 response
    add_contact_resp::AddContactResponse resp;
    bool parse = resp.ParseFromString(res->body);
    // 处理异常
    if (res->status != 200 && !parse) {
        std::string err_desc;
        err_desc.append("post '/contacts/add/' 失败:")
                .append(std::to_string(res->status))
                .append("(").append(res->reason)
                .append(")");
        throw ContactException(err_desc);
    }
    else if (res->status != 200) {
        // 处理服务异常
        std::string err_desc;
        err_desc.append("post '/contacts/add/' 失败 ")
            .append(std::to_string(res->status))
            .append("(").append(res->reason)
            .append(")  错误原因:")
            .append(resp.base_resp().error_desc());
        throw ContactException(err_desc);
    }
    else if (!resp.base_resp().success()) {
        // 处理结果异常
        std::string err_desc;
        err_desc.append("post '/contacts/add/' 结果异常:")
            .append("异常原因:")
            .append(resp.base_resp().error_desc());
        throw ContactException(err_desc);
    }

    // 正常返回,打印结果
    std::cout << "---> 新增联系人成功,联系人ID:" << resp.uid() << std::endl;
}

void ContactsServer::delContact() {
    httplib::Client cli(CONTACTS_IP, CONTACTS_PORT);
    // 构建 request 请求
    del_contact_req::DelContactRequest req;
    std::cout << "请输入要删除的联系人id: ";
    std::string uid;
    getline(std::cin, uid);
    req.set_uid(uid);

    // 序列化 request
    std::string req_str;
    if (!req.SerializeToString(&req_str)) {
        throw ContactException("DelContactRequest序列化失败!");
    }

    // 发起 post 请求
    auto res = cli.Post("/contacts/del", req_str, "application/protobuf");
    if (!res) {
        std::string err_desc;
        err_desc.append("/contacts/del 链接错误!错误信息:")
            .append(httplib::to_string(res.error()));
        throw ContactException(err_desc);
    }

    // 反序列化 response
    del_contact_resp::DelContactResponse resp;
    bool parse = resp.ParseFromString(res->body);
    // 处理异常
    if (res->status != 200 && !parse) {
        std::string err_desc;
        err_desc.append("post '/contacts/del' 失败:")
            .append(std::to_string(res->status))
            .append("(").append(res->reason)
            .append(")");
        throw ContactException(err_desc);
    }
    else if (res->status != 200) {
        std::string err_desc;
        err_desc.append("post '/contacts/del' 失败 ")
            .append(std::to_string(res->status))
            .append("(").append(res->reason)
            .append(")  错误原因:")
            .append(resp.base_resp().error_desc());
        throw ContactException(err_desc);
    }
    else if (!resp.base_resp().success()) {
        // 结果异常
        std::string err_desc;
        err_desc.append("post '/contacts/del' 结果异常:")
            .append("异常原因:")
            .append(resp.base_resp().error_desc());
        throw ContactException(err_desc);
    }

    // 正常返回,打印结果
    std::cout << "---> 成功删除联系人,被删除的联系人ID为:" << resp.uid() << std::endl;
}

void ContactsServer::findContacts() {
    httplib::Client cli(CONTACTS_IP, CONTACTS_PORT);
    // 发起 get 请求
    auto res = cli.Get("/contacts/find-all");
    if (!res) {
        std::string err_desc;
        err_desc.append("/contacts/find-all 链接错误!错误信息:")
            .append(httplib::to_string(res.error()));
        throw ContactException(err_desc);
    }

    // 反序列化 response
    find_all_contacts_resp::FindAllContactsResponse resp;
    bool parse = resp.ParseFromString(res->body);
    // 处理异常
    if (res->status != 200 && !parse) {
        std::string err_desc;
        err_desc.append("get '/contacts/find-all' 失败:")
            .append(std::to_string(res->status))
            .append("(").append(res->reason)
            .append(")");
        throw ContactException(err_desc);
    }
    else if (res->status != 200) {
        // 服务端异常
        std::string err_desc;
        err_desc.append("post '/contacts/find-all' 失败 ")
            .append(std::to_string(res->status))
            .append("(").append(res->reason)
            .append(")  错误原因:")
            .append(resp.base_resp().error_desc());
        throw ContactException(err_desc);
    }
    else if (!resp.base_resp().success()) {
        // 结果异常
        std::string err_desc;
        err_desc.append("post '/contacts/find-all' 结果异常:")
            .append("异常原因:")
            .append(resp.base_resp().error_desc());
        throw ContactException(err_desc);
    }
    // 正常返回,打印结果
    printFindAllContactsResponse(resp);
}

void ContactsServer::findContact() {
    httplib::Client cli(CONTACTS_IP, CONTACTS_PORT);
    // 构建 request 请求
    find_one_contact_req::FindOneContactRequest req;
    std::cout << "请输入要查询的联系人id: ";
    std::string uid;
    getline(std::cin, uid);
    req.set_uid(uid);

    // 序列化 request
    std::string req_str;
    if (!req.SerializeToString(&req_str)) {
        throw ContactException("FindOneContactRequest序列化失败!");
    }

    // 发起 post 请求
    auto res = cli.Post("/contacts/find-one", req_str, "application/protobuf");
    if (!res) {
        std::string err_desc;
        err_desc.append("/contacts/find-one 链接错误!错误信息:")
            .append(httplib::to_string(res.error()));
        throw ContactException(err_desc);
    }

    // 反序列化 response
    find_one_contact_resp::FindOneContactResponse resp;
    bool parse = resp.ParseFromString(res->body);

    // 处理异常
    if (res->status != 200 && !parse) {
        std::string err_desc;
        err_desc.append("post '/contacts/find-one' 失败:")
            .append(std::to_string(res->status))
            .append("(").append(res->reason)
            .append(")");
        throw ContactException(err_desc);
    }
    else if (res->status != 200) {
        std::string err_desc;
        err_desc.append("post '/contacts/find-one' 失败 ")
            .append(std::to_string(res->status))
            .append("(").append(res->reason)
            .append(")  错误原因:")
            .append(resp.base_resp().error_desc());
        throw ContactException(err_desc);
    }
    else if (!resp.base_resp().success()) {
        // 结果异常
        std::string err_desc;
        err_desc.append("post '/contacts/find-one' 结果异常:")
            .append("异常原因:")
            .append(resp.base_resp().error_desc());
        throw ContactException(err_desc);
    }

    // 正常返回,打印结果
    std::cout << "---> 查询到联系人ID为:" << resp.uid() << " 的信息:" << std::endl;
    printFindOneContactResponse(resp);
}


void ContactsServer::printFindAllContactsResponse(find_all_contacts_resp::FindAllContactsResponse& resp) {
    if (0 == resp.contacts_size()) {
        std::cout << "还未添加任何联系人" << std::endl;
        return;
    }
    for (auto contact : resp.contacts()) {
        std::cout << "联系人姓名: " << contact.name() << " 联系人ID:" << contact.uid() << std::endl;
    }
}

void ContactsServer::buildAddContactRequest(add_contact_req::AddContactRequest* req) {
    std::cout << "请输入联系人姓名: ";
    std::string name;
    getline(std::cin, name);
    req->set_name(name);

    std::cout << "请输入联系人年龄: ";
    int age;
    std::cin >> age;
    req->set_age(age);
    std::cin.ignore(256, '\n'); 

    for(int i = 1; ; i++) {
        std::cout << "请输入联系人电话" << i << "(只输入回车完成电话新增): ";
        std::string number;
        getline(std::cin, number);
        if (number.empty()) {
            break;
        }
        add_contact_req::AddContactRequest_Phone* phone = req->add_phone();
        phone->set_number(number);

        std::cout << "选择此电话类型 (1、移动电话   2、固定电话) : " ;
        int type;
        std::cin >> type;
        std::cin.ignore(256, '\n');
        switch (type) {
            case 1:
                  phone->set_type(add_contact_req::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP);
                  break;
            case 2:
                  phone->set_type(add_contact_req::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL);
                  break;
            default:
                  std::cout << "----非法选择,使用默认值!" << std::endl;
                  break;
        }
    }

    for(int i = 1; ; i++) {
        std::cout << "请输入备注" << i << "标题 (只输入回车完成备注新增): ";
        std::string remark_key;
        getline(std::cin, remark_key);
        if (remark_key.empty()) {
            break;
        }

        std::cout << "请输入备注" << i << "内容: ";
        std::string remark_value;
        getline(std::cin, remark_value);
        req->mutable_remark()->insert({remark_key, remark_value}); 
    }
}

void ContactsServer::printFindOneContactResponse(find_one_contact_resp::FindOneContactResponse& resp) {
    std::cout << "姓名:" << resp.name() << std::endl;
    std::cout << "年龄:" << resp.age() << std::endl;
    for (auto& phone : resp.phone()) {
        int j = 1;
        std::cout << "电话" << j++ << ": " << phone.number();
        std::cout << "  (" << phone.PhoneType_Name(phone.type()) << ")" << std::endl;
    }
    if (resp.remark_size()) {
        std::cout << "备注信息: " << std::endl;
    }
    for (auto it = resp.remark().cbegin(); it != resp.remark().cend(); ++it) {
        std::cout << "    " << it->first << ": " << it->second << std::endl;
    }
}

6.服务端代码实现

服务端存储通讯录结构定义: contacts. proto

syntax = "proto3";
package contacts;
// 联系⼈
message PeopleInfo {
    string uid = 1; // 联系⼈ID
    string name = 2; // 姓名
    int32 age = 3; // 年龄
    message Phone {
        string number = 1; // 电话号码
        enum PhoneType {
            MP = 0; // 移动电话
            TEL = 1; // 固定电话
        }
        PhoneType type = 2; // 类型
    }
    repeated Phone phone = 4; // 电话
    map<string, string> remark = 5; // 备注
}
// 通讯录
message Contacts {
    map<string, PeopleInfo> contacts = 1;
}

main.cc

#include <iostream>
#include "httplib.h"
#include "../server/ContactsServer.h"
#include "../common/ContactException.h"
#include "request/add_contact_request.pb.h"
#include "response/add_contact_response.pb.h"
#include "request/find_one_contact_request.pb.h"
#include "response/find_one_contact_response.pb.h"
#include "response/find_all_contacts_response.pb.h"
#include "request/del_contact_request.pb.h"
#include "response/del_contact_response.pb.h"

using std::cout;
using std::endl;
using std::cerr;
using namespace httplib;

int main() {
  
    cout << "---> 服务启动..." << endl;
    Server srv;  // 创建服务端对象
    ContactsServer contactsServer;
    srv.Post("/contacts/add", [contactsServer](const Request& req, Response& res) {
        add_contact_req::AddContactRequest request;
        add_contact_resp::AddContactResponse response;
        try {
            // 反序列化 request
            if (!request.ParseFromString(req.body)) {
                throw ContactException("Parse AddContactRequest error!");
            }
            // 新增联系人
            contactsServer.add(request, &response);
            // 序列化 resp
            std::string response_str;
            if (!response.SerializeToString(&response_str)) {
                throw ContactException("Serialize AddContactResponse error");
            }
            res.body = response_str;
            res.set_header("Content-Type", "application/protobuf");
            res.status = 200;
        } catch (ContactException &e) {
            cerr << "---> /contacts/add 发现异常!!!" << endl
                << "---> 异常信息:" << e.what() << endl;
            res.status = 500;
            base_response::BaseResponse* baseResponse = response.mutable_base_resp();
            baseResponse->set_success(false);
            baseResponse->set_error_desc(e.what());
            std::string response_str;
            if (response.SerializeToString(&response_str)) {
              res.body = response_str;
              res.set_header("Content-Type", "application/protobuf");
            }
        }
    });	

    srv.Post("/contacts/del", [contactsServer](const Request& req, Response& res) {
        del_contact_req::DelContactRequest request;
        del_contact_resp::DelContactResponse response;
        try {
            // 反序列化 request
            if (!request.ParseFromString(req.body)) {
                throw ContactException("Parse DelContactRequest error!");
            }
            // 删除联系人
            contactsServer.del(request, &response);
            // 序列化 response
            std::string response_str;
            if (!response.SerializeToString(&response_str)) {
                throw ContactException("Serialize DelContactResponse error");
            }
            res.body = response_str;
            res.set_header("Content-Type", "application/protobuf");
            res.status = 200;
        } catch (ContactException &e) {
            cerr << "---> /contacts/del 发现异常!!!" << endl
                << "---> 异常信息:" << e.what() << endl;
            res.status = 500;
            base_response::BaseResponse* baseResponse = response.mutable_base_resp();
            baseResponse->set_success(false);
            baseResponse->set_error_desc(e.what());
            std::string response_str;
            if (response.SerializeToString(&response_str)) {
              res.body = response_str;
              res.set_header("Content-Type", "application/protobuf");
            }
        }
    });	

    srv.Post("/contacts/find-one", [contactsServer](const Request& req, Response& res) {
        find_one_contact_req::FindOneContactRequest request;
        find_one_contact_resp::FindOneContactResponse response;
        try {
            // 反序列化 request
            if (!request.ParseFromString(req.body)) {
                throw ContactException("Parse FindOneContactRequest error!");
            }
            // 查询联系人详细信息
            contactsServer.findOne(request, &response);
            // 序列化 response
            std::string response_str;
            if (!response.SerializeToString(&response_str)) {
                throw ContactException("Serialize FindOneContactResponse error");
            }
            res.body = response_str;
            res.set_header("Content-Type", "application/protobuf");
            res.status = 200;
        } catch (ContactException &e) {
            cerr << "---> /contacts/find-one 发现异常!!!" << endl
                << "---> 异常信息:" << e.what() << endl;
            res.status = 500;
            base_response::BaseResponse* baseResponse = response.mutable_base_resp();
            baseResponse->set_success(false);
            baseResponse->set_error_desc(e.what());
            std::string response_str;
            if (response.SerializeToString(&response_str)) {
              res.body = response_str;
              res.set_header("Content-Type", "application/protobuf");
            }
        }
    });	

    srv.Get("/contacts/find-all", [contactsServer](const Request& req, Response& res) {
        find_all_contacts_resp::FindAllContactsResponse response;
        try {
            // 查询所有联系人
            contactsServer.findAll(&response);
            // 序列化 response
            std::string response_str;
            if (!response.SerializeToString(&response_str)) {
                throw ContactException("Serialize FindAllContactsResponse error");
            }
            res.body = response_str;
            res.set_header("Content-Type", "application/protobuf");
            res.status = 200;
        } catch (ContactException &e) {
            cerr << "---> /contacts/find-all 发现异常!!!" << endl
                << "---> 异常信息:" << e.what() << endl;
            res.status = 500;
            base_response::BaseResponse* baseResponse = response.mutable_base_resp();
            baseResponse->set_success(false);
            baseResponse->set_error_desc(e.what());
            std::string response_str;
            if (response.SerializeToString(&response_str)) {
              res.body = response_str;
              res.set_header("Content-Type", "application/protobuf");
            }
        }
    });

    srv.listen("0.0.0.0", 8123); 
}

ContactException.h:定义异常类

// 自定义异常类
class ContactException
{
private:
    std::string message;

public:
    ContactException(std::string str = "A problem") : message{str} {}
    std::string what() const { return message; }
};

ContactsServer.h:通讯录服务定义

#pragma once

#include <iostream>
#include "httplib.h"
#include "../controller/request/add_contact_request.pb.h"
#include "../controller/response/add_contact_response.pb.h"
#include "../controller/request/find_one_contact_request.pb.h"
#include "../controller/response/find_one_contact_response.pb.h"
#include "../controller/response/find_all_contacts_response.pb.h"
#include "../controller/request/del_contact_request.pb.h"
#include "../controller/response/del_contact_response.pb.h"
#include "../dao/contacts.pb.h"
#include "../dao/ContactsMapper.h"


using namespace httplib;

class ContactsServer {
public:
    ContactsMapper contactsMapper;

public:
    void add(add_contact_req::AddContactRequest& request,
             add_contact_resp::AddContactResponse* response) const;

    void del(del_contact_req::DelContactRequest& request,
             del_contact_resp::DelContactResponse* response) const;

    void findOne(find_one_contact_req::FindOneContactRequest request, 
                 find_one_contact_resp::FindOneContactResponse* response) const;

    void findAll(find_all_contacts_resp::FindAllContactsResponse* rsp) const;

private:
    void printAddContactRequest(add_contact_req::AddContactRequest& request) const;
    void buildPeopleInfo(contacts::PeopleInfo* people, add_contact_req::AddContactRequest& request) const;
    void buildFindOneContactResponse(const contacts::PeopleInfo& people, 
                                     find_one_contact_resp::FindOneContactResponse* response) const;
    void buildFindAllContactsResponse(contacts::Contacts& contacts, 
                                      find_all_contacts_resp::FindAllContactsResponse* rsp) const;
};

ContactsServer.cc:通讯录服务实现

#include "ContactsServer.h"
#include "../common/ContactException.h"
#include "../common/Utils.h"

using std::cout;
using std::endl;

void ContactsServer::add(add_contact_req::AddContactRequest& request, 
                         add_contact_resp::AddContactResponse* response) const {
    // 打印日志
    printAddContactRequest(request);      
    // 先读取已存在的 contacts
    contacts::Contacts contacts;
    contactsMapper.selectContacts(&contacts);

    // 转换为存入文件的消息对象  
    google::protobuf::Map<std::string, contacts::PeopleInfo>* map_contacts = contacts.mutable_contacts();
    contacts::PeopleInfo people;
    buildPeopleInfo(&people, request);   
    map_contacts->insert({people.uid(), people});   

    // 向磁盘文件写入新的 contacts
    contactsMapper.insertContacts(contacts);
    response->set_uid(people.uid());
    response->mutable_base_resp()->set_success(true);
    // 打印日志
    cout << "---> (ContactsServer::add) Success to write contacts." << endl;
}

void ContactsServer::del(del_contact_req::DelContactRequest& request,
                         del_contact_resp::DelContactResponse* response) const {
    // 打印日志
    cout << "---> (ContactsServer::del) DelContactRequest: uid: " << request.uid() << endl;  
    // 先读取已存在的 contacts
    contacts::Contacts contacts;
    contactsMapper.selectContacts(&contacts);
    // 不含uid直接返回
    if (contacts.contacts().find(request.uid()) == contacts.contacts().end()) {  
        cout << "---> (ContactsServer::del) not find uid: " << request.uid() << endl; 
        response->set_uid(request.uid());
        response->mutable_base_resp()->set_success(false);
        response->mutable_base_resp()->set_error_desc("not find uid");
        return;
    }
    // 删除用户
    contacts.mutable_contacts()->erase(request.uid());
    // 向磁盘文件写入新的 contacts
    contactsMapper.insertContacts(contacts);
    // 构造resp
    response->set_uid(request.uid());
    response->mutable_base_resp()->set_success(true);
    // 打印日志
    cout << "---> (ContactsServer::del) Success to del contact, uid: " << request.uid() << endl;
}

void ContactsServer::findOne(find_one_contact_req::FindOneContactRequest request, 
                             find_one_contact_resp::FindOneContactResponse* response) const {
    // 打印日志
    cout << "---> (ContactsServer::findOne) FindOneContactRequest: uid: " << request.uid() << endl;  
    // 获取通讯录
    contacts::Contacts contacts;
    contactsMapper.selectContacts(&contacts);
    // 转换resp消息对象
    const google::protobuf::Map<std::string, contacts::PeopleInfo>& map_contacts = contacts.contacts();
    auto it = map_contacts.find(request.uid());
    // 查找的联系人不存在
    if (it == map_contacts.end()) {
        cout << "---> (ContactsServer::findOne) not find uid: " << request.uid() << endl;
        response->mutable_base_resp()->set_success(false);
        response->mutable_base_resp()->set_error_desc("uid not exist");
        return;
    }
    // 构建resp
    buildFindOneContactResponse(it->second, response);
    // 打印日志
    cout << "---> (ContactsServer::findOne) find uid: " << request.uid() << endl;
}

void ContactsServer::findAll(find_all_contacts_resp::FindAllContactsResponse* rsp) const {
    // 打印日志
    cout << "---> (ContactsServer::findAll) " << endl;  

    // 获取通讯录
    contacts::Contacts contacts;
    contactsMapper.selectContacts(&contacts);

    // 转换resp消息对象
    buildFindAllContactsResponse(contacts, rsp);
}

void ContactsServer::buildFindAllContactsResponse(contacts::Contacts& contacts, 
                                  find_all_contacts_resp::FindAllContactsResponse* rsp) const {
    if (nullptr == rsp) {
        return;
    }
    rsp->mutable_base_resp()->set_success(true);
    for (auto it = contacts.contacts().cbegin(); it != contacts.contacts().cend(); ++it) {
        find_all_contacts_resp::PeopleInfo* people = rsp->add_contacts();
        people->set_uid(it->first);
        people->set_name(it->second.name());
    }
}

void ContactsServer::buildFindOneContactResponse(const contacts::PeopleInfo& people, 
                                 find_one_contact_resp::FindOneContactResponse* response) const {
    if (nullptr == response) {
        return;
    }
    response->mutable_base_resp()->set_success(true);
    response->set_uid(people.uid());
    response->set_name(people.name());
    response->set_age(people.age());
    for (auto& phone : people.phone()) {
        find_one_contact_resp::FindOneContactResponse_Phone* resp_phone = response->add_phone();
        resp_phone->set_number(phone.number());
        switch (phone.type()) {
            case contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP:
                  resp_phone->set_type(find_one_contact_resp::FindOneContactResponse_Phone_PhoneType::FindOneContactResponse_Phone_PhoneType_MP);
                  break;
            case contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL:
                  resp_phone->set_type(find_one_contact_resp::FindOneContactResponse_Phone_PhoneType::FindOneContactResponse_Phone_PhoneType_TEL);
                  break;
            default:
                  break;
        }
    }
    Utils::map_copy(response->mutable_remark(), people.remark());
}

void ContactsServer::printAddContactRequest(add_contact_req::AddContactRequest& request) const {
    cout << "---> (ContactsServer::add) AddContactRequest:" << endl;
    cout << "姓名:" << request.name() << endl;
    cout << "年龄:" << request.age() << endl;
    for (auto& phone : request.phone()) {
      int j = 1;
      cout << "电话" << j++ << ": " << phone.number();
      cout << "  (" << phone.PhoneType_Name(phone.type()) << ")" << endl;
    }
    if (request.remark_size()) {
      cout << "备注信息: " << endl;
    }
    for (auto it = request.remark().cbegin(); it != request.remark().cend(); ++it) {      
      cout << "    " << it->first << ": " << it->second << endl;      
    }      
}

void ContactsServer::buildPeopleInfo(contacts::PeopleInfo* people, add_contact_req::AddContactRequest& request) const {
    std::string uid = Utils::generate_hex(10);
    people->set_uid(uid);
    people->set_name(request.name());
    people->set_age(request.age());
    for (auto& phone : request.phone()) {
        contacts::PeopleInfo_Phone* peo_phone = people->add_phone();
        peo_phone->set_number(phone.number());
        switch (phone.type()) {
            case add_contact_req::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_MP:
                  peo_phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP);
                  break;
            case add_contact_req::AddContactRequest_Phone_PhoneType::AddContactRequest_Phone_PhoneType_TEL:
                  peo_phone->set_type(contacts::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);
                  break;
            default:
                  break;
        }
    }
    Utils::map_copy(people->mutable_remark(), request.remark());
}

Utils.h:定义工具类

#include <sstream>
#include <random>
#include <google/protobuf/map.h>

class Utils
{
public:
    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();
    }

    static void map_copy(google::protobuf::Map<std::string, std::string>* target, 
                         const google::protobuf::Map<std::string, std::string>& source) {
        if (nullptr == target) {
            std::cout << "map_copy warning, target is nullptr!" << std::endl;
            return;
        }
        for (auto it = source.cbegin(); it != source.cend(); ++it) {    
            target->insert({it->first, it->second});
        }  
    }
};

ContactsMapper.h:持久化存储通讯录方法定义

#include "contacts.pb.h"

class ContactsMapper {
public:
    void selectContacts(contacts::Contacts* contacts) const;
    void insertContacts(contacts::Contacts& contacts) const;
};

ContactsMapper.cc:持久化存储通讯录方法实现
注:本应该存入数据库中,在这里为了简化流程,将通讯录存入本地文件
文章来源地址https://www.toymoban.com/news/detail-757916.html

#include "ContactsMapper.h"
#include "../common/ContactException.h"
#include <fstream>

#define TEXT_NAME "contacts.bin"

using std::ios;
using std::cout;
using std::endl;

// 本应该存入数据库中,在这里为了简化流程,将通讯录存入本地文件
void ContactsMapper::selectContacts(contacts::Contacts* contacts) const{
    std::fstream input(TEXT_NAME, ios::in | ios::binary);
    if (!input) {
        cout << "---> (ContactsMapper::selectContacts) " << TEXT_NAME << ": File not found.  Creating a new file." << endl;
    }
    else if (!contacts->ParseFromIstream(&input)) {
        input.close();
        throw ContactException("(ContactsMapper::selectContacts) Failed to parse contacts.");
    }
    input.close();
}


void ContactsMapper::insertContacts(contacts::Contacts& contacts) const {
    std::fstream output(TEXT_NAME, ios::out | ios::trunc | ios::binary);
    if (!contacts.SerializeToOstream(&output)) {
        output.close();
        throw ContactException("(ContactsMapper::insertContacts) Failed to write contacts.");
    }
    output.close();
}

到了这里,关于网络数据通信—ProtoBuf实现序列化和反序列化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity基于Google Protobuf序列化和反序列化小案例

    1.协议定义,简单实现传玩家的2D坐标    2.在Unity的Assets目录下创建一个Plugins文件夹(必须这样命名),此文件专门存放扩展文件, 再新建文件夹BaseInfolibrary,将Google.Protobuf.dll拖入  3.新建一个Test.cs脚本  脚本中引入命名空间 代码改进:通用序列化模板(只用来序列化Message)

    2024年02月15日
    浏览(34)
  • Protobuf协议初级详解(python使用)从安装到序列化-反序列化

    Protobuf是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。 可以简单理解为,是一种跨语言、跨平台的数据传输格式。与j

    2024年02月04日
    浏览(43)
  • 在Unity中使用Protobuf进行序列化

    目录 1.介绍 1.1 什么是Protobuf 1.2 Protobuf和其他数据序列化方案对比 2.下载Protobuf 2.1 方案一 使用VS的Nuget包管理器进行安装(推荐) 2.1.1安装Protobuff包 2.1.2拷贝.dll文件 2.2 方案二 从Github下载并自己生成.dll 2.2.1 下载Probuff 2.2.2 VS打开解决方案 2.2.3 安装.NET SDK 2.2.4 生成.dll文件 3

    2024年04月12日
    浏览(51)
  • 【RPC 协议】序列化与反序列化 | lua-cjson | lua-protobuf

    在分布式计算,远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关

    2024年02月10日
    浏览(39)
  • Protobuf-net:C#高效序列化工具,助力接口传输与前端解析

      概述: Protobuf-net是C#中高效的二进制序列化工具,以紧凑、跨语言支持和卓越性能著称。通过定义消息类型、序列化和反序列化实现数据传输,并可适用于Web接口。前端可使用protobuf.js库解析Protobuf格式数据。 Protobuf-net(Protocol Buffers)是一种高效的二进制序列化工具,具有

    2024年03月09日
    浏览(40)
  • 网络通信/QTcpSocket/实现一个可在子线程中发送和接收数据的TCP客户端

    近来一直接使用WinSocket做网络编程,有很长一段时间不再使用Qt框架下的相关网路通信类。有不少之前积压的问题直到现在也没怎么弄清楚,在CSDN中乱七八糟的存了好几篇草稿,亟待整理。最近要写一个简单地相机升级程序,于是重操旧业。 网络通信中,尤其是在收发工作较

    2024年02月08日
    浏览(54)
  • 数据通信网络基础

    • 在人类社会的起源和发展过程中,通信就一直伴随着我们。从20世纪七、八十年代开始, 人类社会已进入到信息时代,对于生活在信息时代的我们,通信的必要性更是不言而喻 的。 • 本节课所说的通信,是指借助数据通信网络进行连接的通信。本课程主要介绍通信及数

    2024年02月04日
    浏览(46)
  • 计算机网络-数据通信基础

      目录 前言 一、数据通信基本概念 二、数据通信相关知识1 总结 正在学习计算机网络体系,把每日所学的知识梳理出来,既能够当作读书笔记,又能分享出来和大家一同学习讨论。 基本概念:信源、信道、信宿;数字信号、模拟信号;模拟通信、数字通信(信道中传送)。

    2024年02月19日
    浏览(41)
  • 数据通信网络基础的网络参考模型&华为ICT网络赛道

    目录 网络参考模型 2.1.应用与数据 2.2.网络参考模型与标准协议 2.2.1.OSI参考模型 2.2.2.TCP/IP参考模型 2.2.3.应用层 2.2.4.传输层 2.2.5.TCP和UDP 2.2.6.网络层 2.2.7.数据链路层 2.2.8.物理层 2.3.数据通信过程 应用的存在,是为了满足人们的各种需求,比如访问网页,在线游戏,在线视频等

    2024年02月03日
    浏览(42)
  • 计算机网络概论和数据通信基础

    广义观点:只要是能实现远程信息处理的系统或者进一步能达到资源共享的系统都可以称为计算机网络 资源共享观点:计算机网络必须是由独立功能的计算机组成的,能够实现资源共享的系统 用户透明观点:计算机网络就是一台超级计算机,资源丰富功能强大,使用其方式

    2024年02月20日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包