Linux网络——自定义协议与序列化

一、协议

协议是一种 " 约定 ". socket api 的接口 , 在读写数据时 , 都是按 " 字符串 " 的方式来发送接收的。如
果我们要传输一些 " 结构化的数据 ",依然可以通过协议。
其实,协议就是双方约定好的结构化的数据。

二、网络计算器

假如说,想要制作一个网络计算器,我们就需要给出特定的协议,让客户端发出的运算条件能够被服务端接收并计算再进行返回。

我们已经了解,协议就是通信双方约定好的结构化数据,所以我们自定义协议,就可以通过结构体来实现,例如自制一个专用于加减乘除取模的计算器,我们制定如下协议:

struct Request

{

    int x;

    int y;

    char oper;//+ - * / %        

};

在该协议中,x只用于第一个运算数,y只用于第二个运算数,oper为运算符。

struct Response

{

    int result;

    int code;//0:success 1:dev zero 2.非法操作

};

在该协议中,result为运算结果,code表示运算情况,0表示成功运算,1为除0错误,2为非法使用其他运算符。

但是仅仅有了上述结构体,就可以实现用户与服务器的完美通信了吗,并不能

我们的服务器是Linux系统,但是客户端呢?一定也是Linux系统吗?当然不一定,客户端可以是windows,安卓以及iOS,甚至客户端和服务端所使用的编程语言也不相同,更重要的是,网络通信是以字节流的方式,我们并不能直接传递结构体数据

那么解决这一问题,可以通过下述方法:

定义结构体来表示我们需要交互的信息, 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体,所有的系统,语言,它都认识字符串, 这个过程叫做 "序列化 " "反序列化"

1.序列化和反序列化

在上述结构体中,一个运算是由结构体的三部分共同构成的,而序列化,就是将这三部分整合成一个字符串,即"x oper y"这样一个字符串,三部分之间通过自己规定的字符隔开,比如空格,这样我们就把一个结构体转换成一个字符串,通过网络传输之后,再在接受方将字符串进行分割,重新组成结构体,即反序列化

在库中,封装了很多能够实现序列化和反序列化的工具,包括xml、json、protobuf等等,其中json是c++标准库所封装的,所以本篇文章我们就来分享json实现序列化反序列化。


2.Jsoncpp

Jsoncpp 是一个用于处理 JSON 数据的 C++ 。它提供了将 JSON 数据序列化为字符串以及从字符串反序列化为 C++ 数据结构的功能。

下面是Linux两种不同环境下,按照json库的方法:

ubuntusudo apt-get install libjsoncpp-dev

Centos: sudo yum install jsoncpp-devel


3.序列化

因为刚下载的json库,并没有进行链接,所以使用json库需要包含头文件:

#include <jsoncpp/json/json.h>

下面我们将上边给出的Request类进行序列化,来看代码:

    //序列化
    void Serialize(string *out)
    {
        //1.使用现成的库
        Json::Value root;
        root["x"] = _x;
        root["y"] = _x;
        root["oper"] = _oper;

        Json::FastWriter writer;
        *out = writer.write(root); 
    }

    int main()
    {
        Request req(1,2,'+');
        string out;
        req.Serialize(&out);
        cout << out << endl;
        return 0;
    }

定义一个Json库中的Value对象,该对象中重载了“[]”,通过键值对的方式,将成员变量与其对应的值捆绑,并记录在root对象中,紧接着定义Json库中的FastWriter对象,其中的write函数,能够将Value对象中存放的键值对转换为字符串并返回。

结果如下:

该字符串就是通过Json库序列化之后形成的,称为Json串

当然除了普通的内置类型数据,Json还可以序列化Value对象自己,以及数组等各种类型的数据


 4.反序列化

直接来看代码:

    // 反序列化
    void Deserialize(const string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool res = reader.parse(in, root);
        _x = root["x"].asInt();
        _y = root["y"].asInt();
        _oper = root["oper"].asInt();
        cout << _x << ' ' << _y << ' ' << _oper << endl;
    }
    
    int main()
    {
        string in = "{\"oper\":43,\"x\":1,\"y\":1}";
        Request req;
        req.Deserialize(in);
        return 0;
    }

反序列化,需要定义Json类中的Reader对象,调用其中的parse函数传入要反序列化的Json串,以及Value对象用于接收反序列化后的键值对数据。随后,通过asInt()函数,将数据以整型方式获取,注意单字符也是整型,后续通过ASCLL码转换

结果如下:


5.设计协议报头

单单进行序列化,是无法满足直接通过网络进行传输的,因为在传输过程中,可能出现阻塞,导致最终可能无法得到完整的数据序列。所以我们还需要给协议添加报头,使得得到的整个报文格式完整,这里我们设计报文格式为:

"len"\r\n"{json}"\r\n

  • len:表示json串有效载荷的长度。
  • 中间\r\n:用于区分len和json串。
  • 结尾\r\n:暂时无用,方便debug。

具体方法如下:

static const string sep = "\r\n";
//添加报头
string Encode(const string &jsonstr)
{
    int len = jsonstr.size();
    string strlen = to_string(len);
    return strlen + sep + jsonstr + sep;
}

添加报头较为简单,获取到json串的长度,转为string类型,在进行拼接即可。

//拆解报头
string Decode(string &packagestream)
{
    //是否拥有完整的中间sep
    auto pos = packagestream.find(sep);
    if(pos == string::npos) return string();
    //获得json串长度
    string strlen = packagestream.substr(0,pos);
    int len = stoi(strlen);

    //得到报文完整长度
    int total = strlen.size() + len + 2 * sep.size();
    if(packagestream.size() < total) return string();
    //得到json串
    string jsonstr = packagestream.substr(pos + sep.size(),len);
    //将得到的json串从原数据流中删除
    packagestream.erase(total);
    return jsonstr;
}

在拆解报头中,首先要进行判断,该数据流是否包含完整的中间sep,不包含说明数据不全,不做拆解;进而通过json串的长度,能够计算出整个报文的长度判断数据流的长度是否小于整个报文的长度,如果小于,则说明没有完整的报文,不做拆解;如果大于,说明包含完整的报文,就可以进行拆解,得到json串,最后将拆解的报文从原数据流中删除

随后在进行TCP/UDP通信时,我们只需要将要发送数据先序列化并添加报头,得到完整的报文,再在接收数据时,去除报头,进而将报文反序列化,从而得到数据


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/915038.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《TCP/IP网络编程》学习笔记 | Chapter 6:基于UDP的服务器端/客户端

《TCP/IP网络编程》学习笔记 | Chapter 6&#xff1a;基于UDP的服务器端/客户端 《TCP/IP网络编程》学习笔记 | Chapter 6&#xff1a;基于UDP的服务器端/客户端理解UDPUDP套接字的特点UDP内部工作原理UDP的高效使用 实现基于UDP的服务器端/客户端UDP中的服务器端和客户端没有连…

Linux也有百度云喔~

一、写在前面 经常有粉丝向我抱怨&#xff0c;为什么每次发放资料都用百度云&#xff0c;自己下载了一遍之后还得再上传一遍服务器才能分析。其实大家大可不必这么周转&#xff0c;百度云也有Linux的发行版本&#xff0c;利用python包bypy来管理/传输百度云盘资源也很方便(别问…

从0开始机器学习--Day23--支持向量机

经过前面的学习&#xff0c;我们已经知道在解决问题时&#xff0c;重要的不仅仅是要在算法A或算法B中选择更优的&#xff0c;而是考虑怎么选择用于学习算法的特征和正则化参数&#xff0c;相比神经网络和逻辑回归&#xff0c;支持向量机在这两个方面做得更好。 优化目标(Optimi…

JavaScript 中实例化生成对象的相关探讨

JavaScript 中实例化生成对象的相关探讨 在 JavaScript 世界中&#xff0c;对象的实例化是一个关键且基础的概念。当我们使用构造函数创建对象时&#xff0c;会引发一系列关于对象之间联系、原型链以及相关概念的思考。 让我们通过一段代码来深入探讨这些问题&#xff1a; f…

MatSci-LLM ——潜力和挑战以及大规模语言模型在材料科学中的应用

概述 大规模语言模型的出现正在从根本上改变技术开发和研究的方式。大规模语言模型不仅对自然语言处理领域产生了重大影响&#xff0c;而且对许多相关领域也产生了重大影响&#xff0c;例如从文本生成图像的计算机视觉&#xff08;Zhang 等人&#xff0c;2023 年&#xff09;。…

【C++】C++11特性(上)

✨✨欢迎大家来到Celia的博客✨✨ &#x1f389;&#x1f389;创作不易&#xff0c;请点赞关注&#xff0c;多多支持哦&#x1f389;&#x1f389; 所属专栏&#xff1a;C 个人主页&#xff1a;Celias blog~ 目录 一、列表初始化 二、std::initializer_list 三、右值引用和移…

Linux(光速安装+ubuntu镜像 serve live-serve desktop)

ubuntu镜像_ubuntu下载地址_ubuntu安装教程-阿里巴巴开源镜像站 Index of /ubuntu-releases/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 清华大学的镜像好一点速度比较快&#xff01; 下载镜像 都是推荐使用服务器版&#xff0c;桌面版一般自己用 amd64 就…

Linux命令详解,全网最详细,看这一篇就够了

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

机器情绪及抑郁症算法

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月12日17点02分 点击开启你的论文编程之旅https://www.aspiringcode.com/content?id17230869054974 计算机来理解你的情绪&a…

美食网的设计与实现

摘 要 随着科技的发展、生活水平的提升&#xff0c;人们更加注重饮食搭配和饮食健康。通过网络技术来加强美食与健康知识的普及是当前一种可行的措施。通过网页浏览美食网&#xff0c;不仅可以普及每道美食的做法&#xff0c;通过制作美食来缓解心情&#xff0c;还可以通过美…

Ubuntu[无桌面]——修改Docker镜像源文件

下载镜像的时候&#xff0c;一般有两种方式&#xff1a; &#xff08;1&#xff09;在宿主主机配置相应的文件/etc/docker/daemon.json&#xff0c;配置镜像源环境地址 &#xff08;2&#xff09;进入https://quay.io/search中&#xff0c;输入搜索需要下载的镜像名称&#xff…

ODOO学习笔记(8):模块化架构的优势

灵活性与可定制性 业务流程适配&#xff1a;企业的业务流程往往因行业、规模和管理方式等因素而各不相同。Odoo的模块化架构允许企业根据自身的具体业务流程&#xff0c;选择和组合不同的模块。例如&#xff0c;一家制造企业可以启用采购、库存、生产和销售模块&#xff0c;并通…

Git的使用(基础语句)

首先如果想要使用git的各项功能&#xff0c;我们要下载Git-2.40.1-64-bit.exe这个驱动程序&#xff0c;并安装它&#xff0c;这个资源我没有办法上传是因为有的博主已经上传过了&#xff0c;所以有VIP的或者有钱哥可以去csdn上自行下载&#xff0c;实在不行加我qq我发你4925396…

labview用sql server数据库存取数据到一个单元格

最近有一个项目上需要一个庞大的数据量&#xff0c;需要很多列&#xff0c;但是百度查了一下sqi server最多支持1024列&#xff0c;这一限制适用于大多数表类型&#xff0c;包括常规表&#xff0c;临时表和表变量&#xff0c;要注意的是如果超出这一限制可能会导致数据的完整性…

Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)

作者&#xff1a;来自 Elastic Benjamin Trent Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)。 嵌入模型输出 float32 向量&#xff0c;通常对于高效处理和实际应用来说太大。Elasticsearch 支持 int8 标量量化&#xff0c;以减小向量大小&#xff0c;同时保持性能。其他…

库打包工具 rollup

库打包工具 rollup 摘要 **概念&#xff1a;**rollup是一个模块化的打包工具 注&#xff1a;实际应用中&#xff0c;rollup更多是一个库打包工具 与Webpack的区别&#xff1a; 文件处理&#xff1a; rollup 更多专注于 JS 代码&#xff0c;并针对 ES Module 进行打包webpa…

2024中国游戏出海情况

01 哪里出海更花钱&#xff1f; 报告显示&#xff0c;中国手游在全球不同市场的获客成本不同&#xff0c;整体来看北美市场竞争更加激烈&#xff0c;其安卓和iOS获客成本是拉丁美洲的12倍和7倍。 按具体市场划分&#xff0c;获客成本最高的TOP 3为韩国、美国和日本&#xff0c…

【达梦数据库】MYSQL迁移到DM字符集转换问题-UTF8mb4|转UTF8(UTF8mb3)

目录 背景现象问题原因原因1&#xff1a;字符集不同原因2&#xff1a;以字节为单位 解决办法方法1&#xff1a;扩大长度 结果验证MYSQLDTSDM 背景 迁移过程环境信息如下&#xff1a; 数据库版本字符集补充MYSQL8.0.xxUTF8mb4DM8.1.3.162UTF8&#xff08;UTF8mb3的简称&#x…

Qt_day10_程序打包(完结)

目录 1. 设置图标 2. Debug和Release版本 3. 动态链接库 4. 打包 5. 联系项目要求 Qt开发的程序最终都是要给用户使用的&#xff0c;用户的电脑上不可能装一个Qt的开发环境导入项目使用。因此项目项目开发完成后需要打包——制作成安装包&#xff0c;用户直接下载并安装即可使用…

RT-DETR融合[ECCV2024]自调制特征聚合SMFA模块及相关改进思路

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《SMFANet: A Lightweight Self-Modulation Feature Aggregation Network for Efficient Image Super-Resolution》 一、 模块介绍 论文链接&#xff1a;https://link.springer.…