【Linux网络编程】序列化与反序列化

2023-09-19 10:21:41

我们网络收发数据实际上只能接收到字符串,但是在现实生活中描述一个客观物体都是以很多属性来描述的,所以在网络中结构体类型的数据更常见,那我们如何发送结构体数据呢?

这里就涉及到协议的概念了。我们想象一个场景,在特种兵执行任务时,他们有特定的战术手语,这样他们就能根据手语进行相应的战术配合了。所以协议也是一样,客户端和服务器都遵循相同的协议,以某种格式把字符串变成结构体或把结构体变成字符串。这个过程中就是序列化与反序列化。

 序列化:结构体类型数据转化成字节序

反序列化:字节序转化成结构体类型数据

话不多说,看图

服务器利用套接字接收请求,进行反序列化后,对请求进行业务处理,处理完成把结果生成响应

然后序列化发送给客户端。自然,客户端接收响应也必须反序列化。

简易网络计算器协议协议部分代码:定制自己的协议,确定数据格式。

下面还利用到了Json这种数据转换格式语言。

#pragma once

#include <vector>
#include <string>
#include "Util.hpp"
#include <iostream>
#include <jsoncpp/json/json.h>
// 利用条件编译,在这里定义一个宏,如果定义了就走a,否则就走b,这相当于是一个开关

// #define HAHA 666
//  给网络版本计算器制定协议
namespace Protocol_ns
{

#define SEP " "
#define SEP_LEN strlen(SEP) // 不能用sizeof
#define HEADER_SEP "\r\n"
#define HEADER_SEP_LEN strlen("\r\n")

    // 请求/响应 = 报头\r\n有效载荷\r\n

    // "10 + 20" => "7"\r\n""10 + 20"\r\n
    std::string AddHeader(std::string &str)
    {
        std::string s = std::to_string(str.size());

        s += HEADER_SEP;
        s += str;
        s += HEADER_SEP;
        return s;
    }

    // 读取一个完整的报文;
    int ReadPackage(int sock, std::string &inbuffer, std::string *package)
    {

        char buffer[1024];
        ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); // 读取
        if (n <= 0)
            return -1;
        buffer[n] = 0; // 添加字符串末尾'/0';
        inbuffer += buffer;

        size_t pos = inbuffer.find(HEADER_SEP);
        if (pos == std::string::npos)
            return 0; // 虽然读到了数据,但是不存在一个完整的报文,应该继续读取

        std::string lenStr = inbuffer.substr(0, pos);
        int len = Util::toInt(lenStr);
        int targetPackageLen = HEADER_SEP_LEN * 2 + len + lenStr.size();
        if (inbuffer.size() < targetPackageLen)
            return 0; // 虽然读到了数据,但是不存在一个完整的报文,应该继续读取

        *package = inbuffer.substr(0, targetPackageLen); // 提取到了整个报文
        inbuffer.erase(0, targetPackageLen);             // 从inbuffer中直接移除整个报文

        return len; // 返回有效载荷长度
    }

    // "7"\r\n""10 + 20"\r\n => "10 + 20"
    void RemoveHeader(std::string *package, int len)
    {
        size_t pos = (*package).find(HEADER_SEP);
        *package = (*package).erase(0, pos);

        *package = (*package).erase(0, HEADER_SEP_LEN);

        *package = (*package).substr(0, len);
    }

    // Request && Response都要提供序列化和反序列化功能
    class Request
    {
    public:
        Request() {}

        Request(int x, int y, char op)
            : _x(x), _y(y), _op(op)
        {
        }

        // struct -> string
        bool Serialization(std::string *outStr)
        {
            *outStr = "";
#ifdef HAHA

            std::string x_string = std::to_string(_x);
            std::string y_string = std::to_string(_y);

            // 手动序列化
            *outStr = x_string + SEP + _op + SEP + y_string; // outstr在这里是输出型参数;
#else
            Json::Value root; // Value: 一种万能对象, 接受任意的kv类型

            root["x"] = _x;
            root["y"] = _y;
            root["op"] = _op;

            Json::StyledWriter writer; // Writer是用来进行序列化的,struct->string
            *outStr = writer.write(root);

#endif
            return true;
        }

        // string->struct
        bool Deserialization(const std::string &inStr)
        {
#ifdef HAHA

            std::vector<std::string> result;
            Util::StringSplit(inStr, SEP, &result); // 对序列化数据进行分割,这相当于是一种解释;
            if (result.size() != 3)
                return false;
            if (result[1].size() != 1)
                return false;

            _x = Util::toInt(result[0]);
            _y = Util::toInt(result[2]);
            _op = result[1][0];
#else
            Json::Value root;
            Json::Reader reader; // Reader:用来进行反序列化的。

            reader.parse(inStr, root);
            _x = root["x"].asInt();
            _y = root["y"].asInt();
            _op = root["op"].asInt();

#endif
            return true;
        }

        ~Request() {}

    public:
        int _x;
        int _y;
        char _op;
    };

    class Response
    {
    public:
        Response() {}

        Response(int result, int code)
            : _result(result), _code(code)
        {
        }

        bool Serialization(std::string *outStr)
        {

            *outStr = "";
#ifdef HAHA
            std::string _result_string = std::to_string(_result);
            std::string _code_string = std::to_string(_code);

            // 手动序列化
            *outStr = _result_string + SEP + _code_string; // outstr在这里是输出型参数;

#else
            Json::Value root;
            root["result"] = _result;
            root["code"] = _code;

            Json::StyledWriter writer; // writer::用来进行序列化的;

            *outStr = writer.write(root);

#endif
            return true;
        }

        bool Deserialization(const std::string &inStr)
        {
#ifdef HAHA
            std::vector<std::string> result;
            Util::StringSplit(inStr, SEP, &result); // 对序列化数据进行分割,这相当于是一种解释;
            if (result.size() != 2)
                return false;

            _result = Util::toInt(result[0]);
            _code = Util::toInt(result[1]);
#else
            Json::Value root;
            Json::Reader reader;

            reader.parse(inStr,root);
            _result=root["result"].asInt();
            _code=root["code"].asInt();


#endif
            return true;
        }

        ~Response() {}

    public:
        int _result;
        int _code; // 0 success, 1,2,3,4代表不同的错误码;
    };
}

更多推荐

想学嵌入式开发,薪资怎么样?

想学嵌入式开发,薪资怎么样?对于嵌入式工程师来说呢,它重点学习内容就是首先一定要打好基础,如果从编程语言角度来讲,那么可以在语言上选C或者C++,你可以选择其中任何一门语言作为你的入门。最近很多小伙伴找我,说想要一些嵌入式机学习资料,然后我根据自己从业十年经验,熬夜搞了几个通宵,精心整理了一份「嵌入式入门到高级教程+工

思腾云计算

全新一代的Atlas是支持ARM架构和X86架构的,像Intel,AMD,海光,鲲鹏,飞腾的CPU都支持。Atlas300IPro,是基于昇腾310芯片开发推理卡,最高功耗72W,被动散热,半高半长单宽,达芬奇架构,作为推理卡需求比较简单,算力和显存平衡就可,所以它支持FP16*70TFLOPS和INT8*140TOP

扩展pytest接口自动化框架-MS数据解析功能

【软件测试行业现状】2023年了你还敢学软件测试?未来已寄..测试人该何去何从?【自动化测试、测试开发、性能测试】开篇MeterSphere的数据源通过html页面上传后,需要将请求方式进行拆分。get接口的参数,常以params的方式进行传参,也就是在url后带上参数。post接口一般是以json字符串的形式传参,也

有效的网络带宽监控策略

世界各地的企业正在采用多种策略来减少瓶颈、增强网络性能并最大限度地提高投资回报率,以跟上不断发展的混合基础架构的步伐。虽然这些策略因组织而异,并提供了自己的好处,但它们可能会使IT基础架构的监控方式复杂化。在设计有效的监控策略时,必须了解各个组件的网络吞吐量、带宽、流量活动、运行状况和性能以及整个网络。网络带宽监视使网

【前端知识】Three 学习日志(三)—— 光源对物体表面的影响

Three学习日志(三)——光源对物体表面的影响一、设置材质为受光照影响//MeshLambertMaterial受光照影响constmaterial=newTHREE.MeshLambertMaterial();此时,场景中一片漆黑,无法看到原来的物体,需要设置光源来照亮物体。二、设置点光源//点光源:两个参数分别表

华为云云耀云服务器L实例使用教学 | 访问控制-安全组配置规则 实例教学

文章目录访问控制-安全组什么叫安全组安全组配置默认安全组配置安全组配置实例安全组创建安全组模板配置安全组模板:通用Web服务器配置安全组规则安全组配置规则功能介绍修改允许特定IP地址访问Web80端口服务建立仅允许访问特定目的地址的安全规则配置网络ACL对实例应用安全组安全组和网络ACL规则验证测试总结华为云耀云服务器

嵌入式MCU学习利器-03-在线做RT-Thread实验

嵌入式MCU学习利器-03-在线做RT-Thread实验很多学生想要学习RT-Thread,但是苦于没有好的学习工具或者物理开发板而选择放弃。现在福利来了,同学们可以基于我们的仿真平台做嵌入式demo,通过调试功能深入学习RT-Thread的原理。本仿真平台基于STM32F103ZE芯片上线了一套RT-Thread课程

【深度学习】Pytorch 系列教程(一):PyTorch数据结构:1、Tensor(张量):维度(Dimensions)、数据类型(Data Types)

目录一、前言二、实验环境三、PyTorch数据结构0、分类1、Tensor(张量)1.维度(Dimensions)0维(标量)1维(向量)2维(矩阵)3维张量2.数据类型(DataTypes)一、前言ChatGPT:PyTorch是一个开源的机器学习框架,广泛应用于深度学习领域。它提供了丰富的工具和库,用于构建和训练各

安卓玩机搞机----不用刷第三方官改固件即可享受“高级设置”的操作 ChiMi安装使用步骤

很多玩友特别喜欢第三方作者修改的带有高级设置的官改包。因为他可以随意修改系统里面的有关设置选项。包括但不限于修改状态栏显示日期秒等等的操作。第三方带高级设置的官改一般官改带高级设置的类似与今天给大家分享下不用刷这些官改包即可享受高级设置的操作。红米k40做个演示步骤机型;红米k40芯片:高通骁龙870安卓版本;安卓12

使用SeaFile搭建私有云盘并公网访问【cpolar内网穿透】

文章目录1.前言2.SeaFile云盘设置2.1Owncould的安装环境设置2.2SeaFile下载安装2.3SeaFile的配置3.cpolar内网穿透3.1Cpolar下载安装3.2Cpolar的注册3.3Cpolar云端设置3.4Cpolar本地设置4.公网访问测试5.结语1.前言现在我们身边的只能设备越来越多

使用Jest搭建自动化单元测试框架为Vue 3项目

前言在Vue3项目中,自动化单元测试是一个非常重要的环节,它可以帮助我们验证代码的正确性、提高代码质量,并且在项目迭代过程中保证代码的稳定性。本文将介绍如何使用Jest搭建自动化单元测试框架为Vue3项目,并提供代码示例。安装Jest首先,我们需要在项目中安装Jest。打开终端,进入项目根目录,执行以下命令:npmin

热文推荐