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

news2024/11/14 0:37:44

一、协议

协议是一种 " 约定 ". 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通信时,我们只需要将要发送数据先序列化并添加报头,得到完整的报文,再在接收数据时,去除报头,进而将报文反序列化,从而得到数据


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows,虚拟机Ubuntu和开发板三者之间的NFS服务器搭建

Windows,虚拟机Ubuntu和开发板三者之间的NFS服务器搭建 &#xff08;1&#xff09;虚拟机 ubuntu 要使用桥接模式&#xff0c;不能使用其他模式 &#xff08;2&#xff09;通过网线将PC和开发板网口直连:这样的连接&#xff0c;开发板是无法连接外网的 &#xff08;3&#xff…

更改Ubuntu22.04锁屏壁纸

更改Ubuntu22.04锁屏壁纸 sudo apt install gnome-shell-extensions gnome-shell-extension-manager安装Gnome Shell 扩展管理器后&#xff0c;打开“扩展管理器”并使用搜索栏找到“锁屏背景”扩展

大模型推理优化技术-KV Cache

近两年大模型火出天际&#xff1b;同时&#xff0c;也诞生了大量针对大模型的优化技术。本系列将针对一些常见大模型优化技术进行讲解。 大模型推理优化技术-KV Cache大模型推理服务调度优化技术-Continuous batching大模型底显存推理优化-Offload技术大模型推理优化技术-KV C…

力扣 LeetCode 24. 两两交换链表中的节点(Day2:链表)

解题思路&#xff1a; 暂存节点tmp和tmp1 注意&#xff1a;while (cur.next ! null && cur.next.next ! null)表示为偶数和奇数时的循环停止条件&#xff0c;并且while语句中的顺序不可交换&#xff0c;交换会报空指针异常 class Solution {public ListNode swapPai…

动态规划-背包问题——494.目标和

1.状态表示 题目来源 494.目标和——力扣 测试用例 2.算法原理 1.状态表示 首先我们需要将问题简化&#xff0c;这里需要找到能将数组组合计算成为指定数字target的添加方式&#xff0c;那么我们就可以将数字分为两类&#xff0c;一类是前面添加""的&#xff0c;另…

哪些因素会导致充电器的充电速度变慢?-纳米软件

充电器的充电速度变慢可能由多种原因引起。以下是一些常见的因素&#xff1a; 一、充电器本身的问题 充电头功率不足&#xff1a;不同的充电头有不同的输出功率&#xff0c;如果使用的充电头功率较低&#xff0c;那么充电速度就会变慢。例如&#xff0c;一些老旧的充电头可能…

刷题强训(day06) -- 大数加法、链表相加、大数乘法

目录 1、大数加法 1.1 题目 1.2 思路 1.3 代码实现 2、链表相加&#xff08;二&#xff09; 2.1 题目 2.2 思路 2.3 代码实现 3、大数乘法 3.1 题目 3.2 思路 3.3 代码实现 1、大数加法 1.1 题目 1.2 思路 这道题可以模拟列竖式相加解答&#xff0c; 将每一位都转…

数字后端教程之Innovus report_property和get_property使用方法及应用案例

数字IC后端实现Innovus中使用report_property可以报告出各种各样object的属性&#xff0c;主要有cell&#xff0c;net&#xff0c;PG Net&#xff0c;Pin&#xff0c;时钟clock&#xff0c;时序库lib属性&#xff0c;Design属性&#xff0c;timing path&#xff0c;timin arc等…

网络基础 - 网段划分篇

我们知道&#xff0c;IP 地址(IPv4 地址)由 “网络标识(网络地址)” 和 “主机标识(主机地址)” 两部分组成&#xff0c;例如 192.168.128.10/24&#xff0c;其中的 “/24” 表示从第 1 位开始到多少位属于网络标识&#xff0c;那么&#xff0c;剩余位就属于主机标识了&#xf…

python实战(八)——情感识别(多分类)

一、任务目标 本文使用的是来自Kaggle的一个情感识别数据集&#xff0c;这个数据集的总数据量是5934条&#xff0c;标签为anger、fear、joy三种情感的其中一种&#xff0c;很明显是一个多分类任务。这里&#xff0c;我们将使用微调技巧进行深度学习建模&#xff0c;同时我们会比…

23423234

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

opencv入门学习总结

opencv学习总结 不多bb&#xff0c;直接上代码&#xff01;&#xff01;&#xff01; 案例一&#xff1a; import cv2 # 返回当前安装的 OpenCV 库的版本信息 并且是字符串格式 print(cv2.getVersionString()) """ 作用&#xff1a;它可以读取不同格式的图像文…

MySQL 中的索引下推功能

看到索引&#xff0c;应该大家都可以联想到这个是和查询效率有关系的&#xff0c;既然有这个功能&#xff0c;那么那句古话说的好啊&#xff1a;存在即合理。那么这个就是说有了这个功能&#xff0c;可以提升查询效率。 什么是索引下推 我们先有一个大概的理解&#xff1a;在…

重拾CSS,前端样式精读-媒体查询

前言 本文收录于CSS系列文章中&#xff0c;欢迎阅读指正 说到媒体查询&#xff0c;大家首先想到的可能是有关响应式的知识点&#xff0c;除此之外&#xff0c;它还可以用于条件加载资源&#xff0c;字体大小&#xff0c;图像和视频的优化&#xff0c;用户界面调整等等方面&am…

物理设备命名规则(Linux网络服务器 15)

Linux系统中的一切都是文件&#xff0c;硬件设备也不例外。既然都是文件&#xff0c;就必须有文件名称。系统内核中udev设备管理器会自动把硬件名称规范化起来&#xff0c;目的是让用户通过设备文件的名字可以大致了解设备属性以及分区信息。这对于陌生的设备来说特别方便。另外…

NVIDIA NIM 开发者指南:入门

NVIDIA NIM 开发者指南&#xff1a;入门 NVIDIA 开发者计划 想要了解有关 NIM 的更多信息&#xff1f;加入 NVIDIA 开发者计划&#xff0c;即可免费访问任何基础设施云、数据中心或个人工作站上最多 16 个 GPU 上的自托管 NVIDIA NIM 和微服务。 加入免费的 NVIDIA 开发者计…

猿创征文|Inscode桌面IDE:打造高效开发新体验

猿创征文&#xff5c;Inscode桌面IDE&#xff1a;打造高效开发新体验 引言 在当今快速发展的软件开发领域&#xff0c;一个高效、易用的集成开发环境&#xff08;IDE&#xff09;是每个开发者必不可少的工具。Inscode 桌面 IDE 作为一款新兴的开发工具&#xff0c;凭借其强大…

力扣 LeetCode 142. 环形链表II(Day2:链表)

解题思路&#xff1a; 使用set判断是否重复添加&#xff0c;如果set加入不进去证明之前到达过该节点&#xff0c;有环 public class Solution {public ListNode detectCycle(ListNode head) {Set<ListNode> set new HashSet<>();ListNode cur head;while (cur …

激活函数解析:神经网络背后的“驱动力”

神经网络中的激活函数&#xff08;Activation Function&#xff09;是其运作的核心组件之一&#xff0c;它们决定了神经元如何根据输入信号进行“激活”&#xff0c;进而影响整个模型的表现。理解激活函数的工作原理对于设计和优化神经网络至关重要。本篇博客将深入浅出地介绍各…

基于表格滚动截屏(表格全部展开,没有滚动条)

import html2canvasPro from html2canvas // 截图&#xff0c;平辅表格 async function resetAgSize() {const allColumns gridApi.value.getColumns()let totalColumnWidth 0let totalColumnHeight 0// 遍历每一个行节点gridApi.value.forEachNode((rowNode) > {totalCo…