【Linux】自定协议和序列化与反序列化

目录

一、序列化与反序列化概念

二、自定协议实现一个加法网络计算器

(一)TCP如何保证接收方的接收到数据是完整性呢?

(二)自定义协议

(三)自定义协议的实现

1、基础类

2、序列化与反序列化

3、报头的增删

4、从缓冲区内提取完整报文

5、自定协议在服务器与客户端的实现

三、使用Json进行序列化和反序列化

(一)概念

(二)Json的安装

(三)针对序列化和反序列化的改造


一、序列化与反序列化概念

        在之前的文章中,我们调用网络接口进行数据传输时都是使用字符串作为数据直接进行传输的,但是在一些场景中单个字符串是不能满足需要的,可能需要一个结构体或是数据结构,那么如何将结构体或数据结构进行网络传输呢?

        序列化:将数据结构或对象转换为字节流,以便它们可以存储到磁盘上、通过网络传输或在不同的程序间共享;

        反序列化:存储的字节流或文本数据重新转换为原来的数据结构或对象。这个过程用于恢复存储的或传输的数据,以便程序能再次使用它。

        当我们需要发送一个结构体时,我们可以先将其进行序列化转换为字节流再进行网络传输,而接收方可以通过将其反序列化从而恢复传输的数据,以此达到数据传输的目的。

二、自定协议实现一个加法网络计算器

(一)TCP如何保证接收方的接收到数据是完整性呢?

       UDP是面向数据报,而TCP是面向字节流的。

        在UDP协议传输数据时,由于数据的发送和接收都是按数据报的格式进行的,每个数据报是独立传输的。虽然UDP协议本身不保证数据的完整性和可靠性,但在网络传输没有出现丢包或错误的情况下,数据是完整的。

        但在使用TCP协议传输数据时,由于数据的发送和接收都是按字节流的形式进行的,发送和接收到的数据不一定是完整的,假如TCP的服务端读取速度小于TCP客户端的发送速度,那么在缓冲区一定堆积了大量的报文,那么如何从缓冲区内提取到一条完整的报文数据呢?

        其实我们调用接口进行网络数据传输本质实际是拷贝。发送方发送数据,接收方接收数据,本质实际是发送方缓冲区内容拷贝给接收方缓冲区。

        对于上述的问题,如果发送方发送数据的速度过快,导致接收方缓冲区内堆积了大量的报文,那么如何从大量数据中提取出一个完整的报文呢?

        实际上可以定制协议,以下是协议设计方式:

  • 定长(规定每个报文的固定长度)
  • 特殊符合(在报文之间加上特殊符合用于分割报文)
  • 自描述方式(数据本身描述其格式、大小等)

(二)自定义协议

        本文采用自描述方式设计自定协议:

        首先本文是针对加法计算器而做的协议,而这个协议不仅要包括数据的序列化和反序列化,还要有增添减少报头分割符的接口。除此之外,因为使用面向字节流进行输出,还要保证如何从缓冲区内提取一个完整的结构。

(三)自定义协议的实现

1、基础类

class Request
{
public:
    int _x;    //左操作数
    int _y;    //右操作数
    char _op;  //操作符
};
class Response
{
public:
    int _exitcode;  //退出码
    int _result;    //计算结果
};

2、序列化与反序列化

class Request
{
public:
    Request() {}
    Request(int x, int y, char op) : _x(x), _y(y), _op(op) {}
    //序列化
    bool serialize(string &out)
    {
        out.clear();
        out += to_string(_x);
        out += SEP;
        out += _op;
        out += SEP;
        out += to_string(_y);
        return true;
    }
    //反序列化
    bool deserialize(const string &in)
    {
        auto left = in.find(SEP);
        auto right = in.rfind(SEP);
        if (left == string::npos || right == string::npos || left == right)
            return false;
        if (right - left - SEP_LEN != 1)
            return false;
        string x_string = in.substr(0, left);
        string y_string = in.substr(right + SEP_LEN);
        if (x_string.empty() || y_string.empty())
            return false;
        _x = stoi(x_string);
        _y = stoi(y_string);
        _op = in[left + SEP_LEN];
        return true;
    }
public:
    int _x;
    int _y;
    char _op;
};
class Response
{
public:
    Response() {}
    Response(int exitcode, int result) : _exitcode(exitcode), _result(result) {}
    //序列化
    bool serialize(string &out)
    {
        out.clear();
        out += to_string(_exitcode);
        out += SEP;
        out += to_string(_result);
        return true;
    }
    //反序列化
    bool deserialize(const string &in)
    {
        auto index = in.find(SEP);
        if (index == string::npos)
            return false;
        string code = in.substr(0, index);
        string result = in.substr(index + SEP_LEN);
        if (code.empty() || result.empty())
            return false;
        _exitcode = stoi(code);
        _result = stoi(result);
        return true;
    }
public:
    int _exitcode;
    int _result;
};

3、报头的增删

#define SEP " "
#define SEP_LEN strlen(SEP)
#define SEP_LINE "\r\n"
#define SEP_LINE_LEN strlen(SEP_LINE)
//"text_len/r/text/r/n"
//增添报头
bool enLength(string &text)
{
    if (text.empty())
        return false;
    int len = text.size();
    string len_string = to_string(len);
    if (len_string.empty())
        return false;
    string ret;
    ret += len_string;
    ret += SEP_LINE;
    ret += text;
    ret += SEP_LINE;
    text = ret;
    return true;
}
//删除报头
bool deLength(const string &package, string &text)
{
    auto index = package.find(SEP_LINE);
    if (index == string::npos)
        return false;
    string len_string = package.substr(0, index);
    int len = stoi(len_string);
    text.clear();
    text = package.substr(index + SEP_LINE_LEN, len);
    return true;
}

4、从缓冲区内提取完整报文

bool recvPackage(const int &fd, string &inbuffer, string &text)
{
    while (true)
    {
        char buffer[1024];
        ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            inbuffer += buffer;
            auto index = inbuffer.find(SEP_LINE);
            string len_string = inbuffer.substr(0, index);
            int len = stoi(len_string);
            int total = len + SEP_LINE_LEN * 2 + len_string.size();
            if (inbuffer.size() < total)
            {
                cout << "正在等待后续消息" << endl;
                continue;
            }
            text = inbuffer.substr(0, total);
            inbuffer.erase(0, total);
            break;
        }
        else
            return false;
    }
    return true;
}

5、自定协议在服务器与客户端的实现

//服务端
void handlerEntery(const int &fd, func_t func)
    {
        string inbuffer;
        while (1)
        {
            // 收到Req
            Request req;
            string req_str, req_text;
            if (recvPackage(fd, inbuffer, req_str) == false)
                return;
            // 去报头
            cout << "接收数据:" << req_str << endl;
            bool ret = deLength(req_str, req_text);
            if (ret == false)
                logMessage(ERROR, "Server : deLength fail");
            cout << "接收数据正文:" << req_text << endl;
            // 反序列化
            ret = req.deserialize(req_text);
            if (ret == false)
                logMessage(ERROR, "Server : deserialize fail");
            // 计算
            Response resp;
            func(req, resp);
            // 序列化
            string resp_text;
            ret = resp.serialize(resp_text);
            if (ret == false)
                logMessage(ERROR, "Server : deserialize fail");
            cout << "发送数据:" << resp_text << endl;
            // 加报头
            ret = enLength(resp_text);
            if (ret == false)
                logMessage(ERROR, "Server : enLength fail");
            cout << "发送数据正文:" << resp_text << endl;
            // 发送Resp
            send(fd, resp_text.c_str(), resp_text.size(), 0);
        }
    }
//客户端
void run()
    {
        struct sockaddr_in addr;
        bzero(&addr, 0);
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(_Sip.c_str());
        addr.sin_port = htons(_Sport);
        if (connect(_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
        {
            cerr << strerror(errno) << endl;
            exit(2);
        }
        else
        {
            string inbuffer;
            while (true)
            {
                // 读取数据
                string line;
                cout << " please input#\n";
                getline(cin, line);
                Request req = parseLine(line);
                // 序列化
                string req_text;
                bool ret = req.serialize(req_text);
                if (ret == false)
                    logMessage(ERROR, "Client : serialize fail");
                // 加报头
                ret = enLength(req_text);
                if (ret == false)
                    logMessage(ERROR, "Client : enLength fail");
                // 发数据
                send(_fd, req_text.c_str(), req_text.size(), 0);
                // 接收数据
                string resp_str, resp_text;
                if (!recvPackage(_fd, inbuffer, resp_str))
                    continue;
                // 去报头
                ret = deLength(resp_str, resp_text);
                if (ret == false)
                    logMessage(ERROR, "Client : deLength fail");
                // 反序列化
                Response resp;
                ret = resp.deserialize(resp_text);
                if (ret == false)
                    logMessage(ERROR, "Client : deserialize fail");
                cout << "exitcode :" << resp._exitcode << "\tresulet : " << resp._result << endl;
            }
        }
    }

三、使用Json进行序列化和反序列化

(一)概念

        从上文可以得知,手动进行序列化和反序列化非常繁杂。实际上我们可以使用现成的方案。

        Json是一种轻量级的数据交换格式,广泛用于网络应用中,尤其是在客户端与服务器之间的数据交换。它易于阅读和编写,同时也便于机器解析和生成。

(二)Json的安装

        首先登录云服务器后输入指令:

sudo yum install -y jsonapp-devel

        使用Json除了需要引入头文件以外还需要在编译中加入 -ljsoncpp 选项。

#include <jsoncpp/json/json.h>
g++ -g -o Server Server.cpp -std=c++11 -ljsoncpp

(三)针对序列化和反序列化的改造

class Request
{
public:
    Request() {}
    Request(int x, int y, char op) : _x(x), _y(y), _op(op) {}
    //序列化
    bool serialize(string &out)
    {
        Json::Value root;
        root["left"] = _x;
        root["right"] = _y;
        root["op"] = _op;
        Json::FastWriter writer;
        out = writer.write(root);        
        return true;
    }
    //反序列化
    bool deserialize(const string &in)
    {
        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root);
        _x = root["left"].asInt();
        _y = root["right"].asInt();
        _op = root["op"].asInt();
        return true;
    }
public:
    int _x;
    int _y;
    char _op;
};
class Response
{
public:
    Response() {}
    Response(int exitcode, int result) : _exitcode(exitcode), _result(result) {}
    //序列化
    bool serialize(string &out)
    {
        Json::Value root;
        root["exitcode"] = _exitcode;
        root["result"] = _result;
        Json::FastWriter writer;
        out = writer.write(root);
        return true;
    }
    //反序列化
    bool deserialize(const string &in)
    {
        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root);
        _exitcode = root["exitcode"].asInt();
        _result = root["result"].asInt();
        return true;
    }

public:
    int _exitcode;
    int _result;
};

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

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

相关文章

hive之LEAD 函数详解

1. 函数概述 LEAD 是 Hive 中的窗口函数&#xff0c;用于获取当前行之后指定偏移量处的行的值。常用于分析时间序列数据、计算相邻记录的差异或预测趋势。 2. 语法 LEAD(column, offset, default) OVER ([PARTITION BY partition_column] [ORDER BY order_column [ASC|DESC]…

ZYNQ-PL学习实践(二)按键和定时器控制LED闪烁灯

ZYNQ-PL学习实践&#xff08;二&#xff09;按键和定时器控制LED闪烁灯&#xff09; 1 创建工程2 verilog 代码3 约束4 综合5 生成bit总结 1 创建工程 2 verilog 代码 添加key_led.v 文件&#xff0c; module key_led(input sys_clk , //系统时钟50MHzinput …

【Python爬虫】利用代理IP爬取跨境电商AI选品分析

引言 随着DeepSeek的流行&#xff0c;越来越多的用户开始尝试将AI工具融入到日常工作当中&#xff0c;借助AI的强大功能提高工作效率。最近又掀起了一波企业出海的小高潮&#xff0c;那么如果是做跨境电商业务&#xff0c;怎么将AI融入工作流中呢&#xff1f;在做跨境电商的时候…

设计一个SVF下载器之一:整体思路

CPLD或者FPGA开发工具会生成SVF文件用以通过JTAG口配置CPLD或者FPGA。这里有些基本控制JTAG状态机的指令&#xff0c;其实就是主要两条SIR和SDR分别实现对IR寄存器和DR寄存器的写。 这样我们的这个下载器的基本工作变成了解析SVF文件之后对JTAG的TAP状态机进行操作实现对IR和D…

计算机视觉算法实战——图像配准(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​ ​​​ 1. 领域简介 图像配准&#xff08;Image Registration&#xff09;是计算机视觉中的一个重要研究方向&#xff0c;旨在将两幅或多幅…

ArcGIS操作:07 绘制矢量shp面

1、点击目录 2、右侧显示目录 3、选择要存储的文件夹&#xff0c;新建shp 4、定义名称、要素类型、坐标系 5、点击开始编辑 6、点击创建要素 7、右侧选择图层、创建面 8、开始绘制&#xff0c;双击任意位置结束绘制

靶场(二)---靶场心得小白分享

开始&#xff1a; 看一下本地IP 21有未授权访问的话&#xff0c;就从21先看起 PORT STATE SERVICE VERSION 20/tcp closed ftp-data 21/tcp open ftp vsftpd 2.0.8 or later | ftp-anon: Anonymous FTP login allowed (FTP code 230) |_Cant get dire…

一周学会Flask3 Python Web开发-WTForms表单验证

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们可以通过WTForms表单类属性的validators属性来实现表单验证。 常用的WTForms验证器 验证器说明DataRequired(messageNo…

C 语 言 --- 猜 数 字 游 戏

C 语 言 --- 猜 数 字 游 戏 代 码 全 貌 与 功 能 介 绍游 戏 效 果 展 示游 戏 代 码 详 解头 文 件 引 入菜单函数游 戏 逻 辑 函 数 gamerand 函 数 详 解逻 辑 函 数 game 主 函 数 总结 &#x1f4bb;作 者 简 介&#xff1a;曾 与 你 一 样 迷 茫&#xff0c;现 以 经 验…

深入探索C++17文件系统库:std::filesystem全面解析

前言 在C编程中&#xff0c;文件系统操作是许多应用程序的基础功能之一。无论是读写文件、创建目录&#xff0c;还是遍历文件系统&#xff0c;文件系统操作几乎无处不在。然而&#xff0c;在C17之前&#xff0c;标准库并没有提供一个统一、高效且易用的文件系统操作接口。开发…

C++学习之C++初识、C++对C语言增强、对C语言扩展

一.C初识 1.C简介 2.第一个C程序 //#include <iostream> //iostream 相当于 C语言下的 stdio.h i - input 输入 o -output 输出 //using namespace std; //using 使用 namespace 命名空间 std 标准 &#xff0c;理解为打开一个房间&#xff0c;房间里有我们所需…

transformer架构解析{掩码,(自)注意力机制,多头(自)注意力机制}(含代码)-3

目录 前言 掩码张量 什么是掩码张量 掩码张量的作用 生成掩码张量实现 注意力机制 学习目标 注意力计算规则 注意力和自注意力 注意力机制 注意力机制计算规则的代码实现 多头注意力机制 学习目标 什么是多头注意力机制 多头注意力计算机制的作用 多头注意力机…

【大模型基础_毛玉仁】1.3 基于Transformer 的语言模型

【大模型基础_毛玉仁】1.3 基于Transformer 的语言模型 1.3 基于Transformer 的语言模型1.3.1 Transformer1&#xff09;注意力层&#xff08;AttentionLayer&#xff09;2&#xff09;全连接前馈层&#xff08;Fully-connected Feedforwad Layer&#xff09;3&#xff09;层正…

Beeline的使用和Hive JDBC

目录 1. 引言1.1 Hadoop1.2 HBase1.3 Hive 2. Beeline2.1 使用Beeline访问Hive2.1.1 通过beeline直接连接Hive2.1.2 先进入beeline客户端再连接Hive2.1.3 先进入beeline客户端再连接MySQL 2.2 Beeline命令 3. Hive JDBC3.1 pom.xml中依赖配置3.2 Util工具类3.3 代码3.4 结果 参…

分布式多卡训练(DDP)踩坑

多卡训练最近在跑yolov10版本的RT-DETR&#xff0c;用来进行目标检测。 单卡训练语句&#xff08;正常运行&#xff09;&#xff1a; python main.py多卡训练语句&#xff1a; 需要通过torch.distributed.launch来启动&#xff0c;一般是单节点&#xff0c;其中CUDA_VISIBLE…

30秒从零搭建机器人管理系统(Trae)

1. 安装 [Trae官网】(https://www.trae.com.cn/) 2. 提示词 创建一个BS架构的机器人远程操控系统&#xff0c;具备机器人状态及位置实时更新&#xff0c;可以实现机器人远程遥控&#xff0c;可以对机器人工作日志进行统计分析&#xff0c;以及其它管理系统的常用功能3. 模型…

软考-数据库开发工程师-3.1-数据结构-线性结构

第3章内容比较多&#xff0c;内容考试分数占比较大&#xff0c;6分左右 线性表 1、线性表的定义 一个线性表是n个元素的有限序列(n≥0)&#xff0c;通常表示为(a1&#xff0c;a2, a3,…an). 2、线性表的顺序存储(顺序表) 是指用一组地址连续的存储单元依次存储线性表中的数据元…

解锁数据潜能,永洪科技以数据之力简化中粮可口可乐决策之路

企业数字化转型是指企业利用数字技术和信息通信技术来改变自身的商业模式、流程和增值服务&#xff0c;以提高企业的竞争力和创新能力。数字化转型已经成为企业发展的重要战略&#xff0c;尤其在当前信息技术高速发展的时代。数字化转型还涉及到企业与消费者之间的互动和沟通。…

Vue 3 整合 WangEditor 富文本编辑器:从基础到高级实践

本文将详细介绍如何在 Vue 3 项目中集成 WangEditor 富文本编辑器&#xff0c;实现图文混排、自定义扩展等高阶功能。 一、为什么选择 WangEditor&#xff1f; 作为国内流行的开源富文本编辑器&#xff0c;WangEditor 具有以下优势&#xff1a; 轻量高效&#xff1a;压缩后仅…

游戏引擎学习第137天

演示资产系统中的一个 bug 我们留下了个问题&#xff0c;你现在可以看到&#xff0c;移动时它没有选择正确的资产。我们知道问题的原因&#xff0c;就在之前我就预见到这个问题会出现。问题是我们的标签系统没有处理周期性边界的匹配问题。当处理像角度这种周期性的标签时&…