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;
    };
}

运行结果:

服务器:

客户端:

相关推荐
前进的程序员10 分钟前
Linux 移植 Docker 详解
linux·运维·docker
散着步的码农16 分钟前
RustDesk CentOS自建中继节点
linux·centos
瀚高PG实验室18 分钟前
hghac集群服务器时间同步(chrony同步)
linux·运维·服务器
huangyuchi.21 分钟前
【Linux】初见,基础指令
linux·运维·服务器·笔记·开发工具·指令·基础指令
huangyuchi.29 分钟前
【Linux】初见,基础指令(续)
linux·运维·服务器·开发语言·笔记·指令·linux指令
Eric.Lee202131 分钟前
ROS2 pkg 创建功能包
java·linux·开发语言
OneT1me31 分钟前
SN生成流水号并且打乱
java·linux·前端
nuclear201136 分钟前
Python操作PDF书签详解 - 添加、修改、提取和删除
linux·开发语言·python
大得3691 小时前
liunx定时任务,centos定时任务
linux·运维·centos
zzxxlty1 小时前
Windows Ubuntu 目录映射关系
linux·windows·ubuntu