Linux -- 自定义协议体会序列化和反序列化

思路介绍

网络版计算器:

1、客户端发送 两个操作数 和 操作符

2、根据协议,在发送时,对数据进行序列化,再加上报头,形成请求并发送给 服务器;

3、服务器收到 请求 后,判断收到的 请求 是否完整

4、若 请求 完整,则从 请求 中分离出有效载荷,再对有效载荷进行 反序列化

5、服务器处理完数据后,把 结果 进行序列化,并加上报头,作为应答发送给客户端;

6、客户端判断收到的 应答 是否完整;

7、 应答 完整,对 应答 进行解析,从有效载荷中取出计算结果。

gitee

net_cal · zihuixie/Linux_Learning - 码云 - 开源中国https://gitee.com/zihuixie/linux_-learning/tree/master/net_cal

主要代码

序列化 && 反序列化 -- Protocol.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
#include <unistd.h>
#include<memory>

//指定协议

namespace protocol_ns
{
    const std::string SEP = "\r\n"; // 分隔符

    // 添加报头(有效载荷已经序列化)
    // 报文:len\r\n{       }\r\n
    std::string Encode(const std::string &json_str)
    {
        int json_str_len = json_str.size(); // 计算长度

        std::string proto_str = std::to_string(json_str_len); // 报文

        proto_str += SEP;      // 分隔符
        proto_str += json_str; // 有效载荷
        proto_str += SEP;

        return proto_str;
    }

    // len\r\n{
    // len\r\n{       }
    // len\r\n{       }\r\n
    // len\r\n{       }\r\nlen\r\n{
    // len\r\n{       }\r\nlen\r\n{       }\r
    // len\r\n{       }\r\nlen\r\n{       }\r\n
    // len\r\n{       }\r\nlen\r\n{       }\r\nlen\r
    // len\r\n{       }\r\nlen\r\n{       }\r\nlen\r\n{       }\r\n

    //  分离出有效载荷(但收到的报文不一定是完整的,判断收到的报文中有一个完整的报文,就可以进行分离)
    std::string Decode(std::string &inbuffer)
    {
        auto pos = inbuffer.find(SEP);

        // 报文不完整
        if (pos == std::string::npos)
            return std::string();

        std::string len_str = inbuffer.substr(0, pos);
        if (len_str.empty())
            return std::string();

        int packlen = std::stoi(len_str); // 有效载荷的长度

        int total = packlen + 2 * SEP.size() + len_str.size(); // 完整报文的长度

        if (inbuffer.size() < total)
            return std::string(); // 不是完整报文

        std::string package = inbuffer.substr(pos + SEP.size(), packlen); // 有效载荷

        inbuffer.erase(0, total); // 在报文中去掉完整的一段,避免重复处理
        return package;
    }

    // 请求,客户端向服务器发送请求
    class Request
    {
    public:
        Request()
        {
        }
        Request(int x, int y, char oper)
            : _x(x), _y(y), _oper(oper)
        {
        }

        // 序列化,out 是输出型参数
        // 客户端发送请求时,将请求序列化
        bool Serialize(std::string *out)
        {
            Json::Value root;
            root["x"] = _x;
            root["y"] = _y;
            root["oper"] = _oper;

            Json::FastWriter writer;
            *out = writer.write(root); // 将 root 序列化为字符串

            return true;
        }

        // 反序列化
        // 服务器收到请求,将请求反序列化
        bool Deserialize(const std::string &in)
        {
            Json::Value root;
            Json::Reader reader; // 从字符串中读取 Json 数据

            bool res = reader.parse(in, root); // 解析

            // 解析失败
            if (!res)
                return false;

            // 解析成功
            _x = root["x"].asInt();
            _y = root["y"].asInt();
            _oper = root["oper"].asInt();

            return true;
        }

    public:
        int _x;     // 左操作数
        int _y;     // 右操作数
        char _oper; // 操作符
    };

    // 应答
    // 服务器处理完请求,向客户端发送应答
    class Response
    {
    public:
        Response()
        {
        }
        Response(int result, int code)
            : _result(result), _code(code)
        {
        }
        // 序列化,out 是输出型参数
        // 将应答序列化,发送给客户端
        bool Serialize(std::string *out)
        {
            Json::Value root;
            root["result"] = _result;
            root["code"] = _code;

            Json::FastWriter writer;
            *out = writer.write(root); // 将 root 序列化为字符串

            return true;
        }

        // 反序列化
        // 客户端收到应答,将应答反序列化
        bool Deserialize(const std::string &in)
        {
            Json::Value root;
            Json::Reader reader; // 从字符串中读取 Json 数据

            bool res = reader.parse(in, root); // 解析

            // 解析失败
            if (!res)
                return false;

            // 解析成功
            _result = root["result"].asInt();
            _code = root["code"].asInt();

            return true;
        }

    public:
        int _result; // 运算结果
        int _code;   // 结果是否可信,0:可信,1:除0,2:非法操作
    };

    // 工厂,创造数据
    // 模拟客户端
    class Factory
    {
    public:
        Factory()
        {
            srand(time(nullptr) ^ getpid());
            opers = "+/*/%^&|";
        }
        std::shared_ptr<Request> BuildRequest()
        {
            int x = rand() % 10 + 1;
            usleep(x * 10);
            int y = rand() % 5; 
            usleep(y * x * 5);
            char oper = opers[rand() % opers.size()];
            std::shared_ptr<Request> req = std::make_shared<Request>(x, y, oper);
            return req;
        }
        std::shared_ptr<Response> BuildResponse()
        {
            return std::make_shared<Response>();
        }
        ~Factory()
        {
        }

    private:
        std::string opers;
    };
}

运行结果:

服务器:

客户端:

相关推荐
搬码后生仔4 分钟前
x86-64架构的Linux服务器上运行.NET 6.0应用程序,安装runtimes
linux·服务器·.net
冰箱的大米8 分钟前
LSA更新、撤销
运维·网络
十年磨一剑~33 分钟前
ansible之playbook实战
服务器·ansible·apache
dd要努力变优秀!2 小时前
Nginx简述
运维·nginx
hhzz2 小时前
nginx的可视化配置工具nginxWebUI的使用
运维·nginx
非凡的世界6 小时前
linux命令行连接Postgresql常用命令
linux·数据库·postgresql
茉莉玫瑰花茶6 小时前
gdb/cgdb调试技巧加餐
linux
SafePloy安策7 小时前
商用服务器密码机的加密技术与优势
运维·服务器·github
安的列斯凯奇9 小时前
JavaRestClient 客户端初始化+索引库操作
运维·jenkins
惟长堤一痕9 小时前
黑马linux入门笔记(01)初始Linux Linux基础命令 用户和权限 实用操作
linux·运维·笔记