【探索Linux】P.32(自定义协议)

news2024/11/19 4:24:01

在这里插入图片描述

阅读导航

  • 引言
  • 一、自定义协议概念
  • 二、自定义协议需要注意的事项
  • 三、自定义协议示例(跨网络计算器协议)
    • ✅协议代码(Protocol.hpp)
    • 1. 计算器协议简单介绍
    • 2. 序列化部分
    • 3. 反序列化部分
    • 4. 请求和响应数据结构
    • 5. 使用自定义协议
  • 四、总结
  • 温馨提示

引言

在上一篇文章中,我们深入探讨了守护进程的稳定性、序列化与反序列化在数据传输中的关键作用。这篇文章和下篇文章将继续扩展我们的技术视野,聚焦于自定义协议的设计与实现。自定义协议为网络通信提供了高度的灵活性和针对性,允许我们根据特定应用的需求来定制数据交换的规则。我们将通过一个实际的例子:跨网络计算器,用它作为例子来展示自定义协议的强大功能和应用潜力。通过这种方式,我们不仅能够加深对网络通信原理的理解,还能探索如何将这些原理应用于解决现实世界中的复杂问题。

一、自定义协议概念

所谓自定义就是指在软件开发和网络通信领域中,由开发者或组织根据特定的需求和规则自行定义的一套通信规则或数据交换格式。这种协议通常是为了满足特定的业务场景或技术要求而设计的,它可能包括数据的传输方式、数据的编码和解码规则、消息的结构和内容等。

二、自定义协议需要注意的事项

  1. 数据格式:自定义协议需要定义数据的存储和传输格式,常见的格式包括文本格式如JSON、XML,以及二进制格式如Protocol Buffers、Thrift等。

  2. 通信规则:协议需要规定数据在网络中的传输方式,比如TCP、UDP等,以及如何建立连接、传输数据、关闭连接等。

  3. 消息结构:自定义协议需要定义消息的组成结构,包括消息头和消息体,以及各种控制信息和数据内容的排列方式。

  4. 错误处理:协议还需要包含错误处理机制,定义在出现错误或异常情况时的应对策略,比如重传机制、错误码等。

  5. 安全性:在设计自定义协议时,还需要考虑数据的安全性,包括数据的加密、认证和完整性保护等。

  6. 扩展性:一个好的自定义协议应该具有良好的扩展性,能够适应未来业务的发展和技术的更新。

三、自定义协议示例(跨网络计算器协议)

✅协议代码(Protocol.hpp)

#pragma once // 确保头文件在整个程序中只被包含一次

#include <iostream> // 包含标准输入输出流
#include <string> // 包含字符串类
#include <jsoncpp/json/json.h> // 包含JSONCPP库,用于JSON数据的处理

// 宏定义,用于序列化和反序列化过程中的数据分隔
const std::string blank_space_sep = " "; // 空格分隔符
const std::string protocol_sep = "\n"; // 换行符作为协议的分隔符

// 序列化函数,将内容字符串包装成网络传输的格式
std::string Encode(std::string &content)
{
    std::string package; // 创建一个字符串用于存储包装后的数据
    package = std::to_string(content.size()); // 将内容的长度转换为字符串
    package += protocol_sep; // 添加协议分隔符
    package += content; // 添加内容本身
    package += protocol_sep; // 再次添加协议分隔符,表示数据结束

    return package; // 返回包装后的字符串
}

// 反序列化函数,将接收到的网络数据解析为内容字符串
bool Decode(std::string &package, std::string *content)
{
    std::size_t pos = package.find(protocol_sep); // 查找协议分隔符的位置
    if(pos == std::string::npos) return false; // 如果找不到分隔符,解析失败

    std::string len_str = package.substr(0, pos); // 提取长度字符串
    std::size_t len = std::stoi(len_str); // 将长度字符串转换为数字
    std::size_t total_len = len_str.size() + len + 2; // 计算总长度(长度字符串 + 内容 + 分隔符)
    if(package.size() < total_len) return false; // 如果实际数据长度小于预期,解析失败

    *content = package.substr(pos+1, len); // 提取内容字符串
    package.erase(0, total_len); // 从原始数据中移除已解析的部分

    return true; // 解析成功
}

// 请求数据结构
class Request
{
public:
    // 构造函数,初始化请求的参数
    Request(int data1, int data2, char oper) : x(data1), y(data2), op(oper) {}
    // 默认构造函数
    Request() {}

public:
    // 序列化方法,将请求对象转换为字符串
    bool Serialize(std::string *out)
    {
#ifdef MySelf
        // 使用简单的字符串拼接方式
        std::string s = std::to_string(x); // 将操作数x转换为字符串
        s += blank_space_sep; // 添加空格分隔符
        s += op; // 添加操作符
        s += blank_space_sep; // 再次添加空格分隔符
        s += std::to_string(y); // 将操作数y转换为字符串
        *out = s; // 将拼接后的字符串赋值给输出参数
        return true; // 返回成功
#else
        // 使用JSON格式
        Json::Value root; // 创建JSON值对象
        root["x"] = x; // 添加操作数x
        root["y"] = y; // 添加操作数y
        root["op"] = op; // 添加操作符
        Json::StyledWriter w; // 创建JSON格式化写入器
        *out = w.write(root); // 将JSON对象转换为格式化的字符串
        return true; // 返回成功
#endif
    }

    // 反序列化方法,将字符串转换为请求对象
    bool Deserialize(const std::string &in)
    {
#ifdef MySelf
        // 使用简单的字符串拼接方式
        std::size_t left = in.find(blank_space_sep); // 查找第一个空格分隔符
        if (left == std::string::npos) return false; // 如果找不到分隔符,解析失败
        std::string part_x = in.substr(0, left); // 提取操作数x的字符串

        std::size_t right = in.rfind(blank_space_sep); // 查找最后一个空格分隔符
        if (right == std::string::npos) return false; // 如果找不到分隔符,解析失败
        std::string part_y = in.substr(right + 1); // 提取操作数y的字符串

        if (left + 2 != right) return false; // 如果分隔符之间的长度不符合预期,解析失败
        op = in[left + 1]; // 提取操作符
        x = std::stoi(part_x); // 将操作数x的字符串转换为数字
        y = std::stoi(part_y); // 将操作数y的字符串转换为数字
        return true; // 返回成功
#else
        // 使用JSON格式
        Json::Value root;
        Json::Reader r;
        if (!r.parse(in, root)) return false; // 解析JSON字符串,如果失败则返回

        x = root["x"].asInt(); // 提取操作数x
        y = root["y"].asInt(); // 提取操作数y
        op = root["op"].asInt(); // 提取操作符
        return true; // 返回成功
#endif
    }

    // 调试方法,打印请求对象的内容
    void DebugPrint()
    {
        std::cout << "新请求构建完成:  " << x << op << y << "=?" << std::endl; // 打印操作数和操作符
    }

public:
    // 请求的数据成员
    int x; // 操作数x
    int y; // 操作数y
    char op; // 操作符,可以是 + - * / %
};

// 响应数据结构
class Response
{
public:
    // 构造函数,初始化响应的参数
    Response(int res, int c) : result(res), code(c) {}
    // 默认构造函数
    Response() {}

public:
    // 序列化方法,将响应对象转换为字符串
    bool Serialize(std::string *out)
    {
#ifdef MySelf
        // 使用简单的字符串拼接方式
        std::string s = std::to_string(result); // 将结果转换为字符串
        s += blank_space_sep; // 添加空格分隔符
        s += std::to_string(code); // 将错误代码转换为字符串
        *out = s; // 将拼接后的字符串赋值给输出参数
        return true; // 返回成功
#else
        // 使用JSON格式
        Json::Value root; // 创建JSON值对象
        root["result"] = result; // 添加结果
        root["code"] = code; // 添加错误代码
        Json::StyledWriter w; // 创建JSON格式化写入器
        *out = w.write(root); // 将JSON对象转换为格式化的字符串
        return true; // 返回成功
#endif
    }

    // 反序列化方法,将字符串转换为响应对象
    bool Deserialize(const std::string &in)
    {
#ifdef MySelf
        // 使用简单的字符串拼接方式
        std::size_t pos = in.find(blank_space_sep); // 查找空格分隔符
        if (pos == std::string::npos) return false; // 如果找不到分隔符,解析失败
        std::string part_left = in.substr(0, pos); // 提取结果字符串
        std::string part_right = in.substr(pos + 1); // 提取错误代码字符串

        result = std::stoi(part_left); // 将结果字符串转换为数字
        code = std::stoi(part_right); // 将错误代码字符串转换为数字
        return true; // 返回成功
#else
        // 使用JSON格式
        Json::Value root;
        Json::Reader r;
        if (!r.parse(in, root)) return false; // 解析JSON字符串,如果失败则返回

        result = root["result"].asInt(); // 提取结果
        code = root["code"].asInt(); // 提取错误代码
        return true; // 返回成功
#endif
    }

    // 调试方法,打印响应对象的内容
    void DebugPrint()
    {
        std::cout << "结果响应完成, result: " << result << ", code: " << code << std::endl; // 打印结果和错误代码
    }

public:
    int result; // 计算结果
    int code; // 错误代码,0表示成功,非0表示错误
};

1. 计算器协议简单介绍

我们的代码实现了一个简单的自定义网络通信协议,它通过定义RequestResponse类来封装客户端请求和服务器响应的数据结构。协议使用Encode函数将数据对象序列化为字符串格式,以便在网络上传输,并通过Decode函数将接收到的字符串反序列化回数据对象。这种设计支持灵活的序列化选项,允许根据需要选择简单的文本格式或JSON格式。

2. 序列化部分

在自定义协议中,Encode函数负责将content(内容字符串)序列化为网络传输的格式。这个过程包括以下几个步骤:

  1. 计算content的长度,并将其转换为字符串。
  2. 将长度字符串、协议分隔符protocol_sepcontent本身拼接起来形成完整的数据包。
  3. content后再次添加协议分隔符,以标识数据包的结束。
std::string Encode(std::string &content)
{
    std::string package = std::to_string(content.size()); // 步骤1: 计算内容长度
    package += protocol_sep; // 步骤2: 添加分隔符
    package += content; // 步骤2: 添加内容
    package += protocol_sep; // 步骤3: 添加结束分隔符

    return package; // 返回序列化后的数据包
}

3. 反序列化部分

反序列化是将已序列化的数据转换回原始数据结构的过程。Decode函数负责将接收到的网络数据(package)反序列化为content字符串。这个过程包括以下几个步骤:

  1. package中查找第一个协议分隔符protocol_sep的位置。
  2. 根据找到的位置,提取长度字符串(len_str)。
  3. 将长度字符串转换为数字,得到content的长度。
  4. 根据长度提取实际的content,并从原始package中移除已处理的部分。
bool Decode(std::string &package, std::string *content)
{
    std::size_t pos = package.find(protocol_sep); // 查找分隔符位置
    if(pos == std::string::npos) return false; // 如果找不到分隔符,反序列化失败

    std::string len_str = package.substr(0, pos); // 提取长度字符串
    std::size_t len = std::stoi(len_str); // 将长度字符串转换为数字
    if(package.size() < (len_str.size() + len + 2)) return false; // 验证长度

    *content = package.substr(pos+1, len); // 提取内容
    package.erase(0, len + len_str.size() + 2); // 移除已处理的部分

    return true; // 反序列化成功
}

4. 请求和响应数据结构

自定义协议还定义了请求和响应的数据结构。Request类封装了客户端发送的请求数据,而Response类封装了服务器返回的响应数据。这些类提供了序列化和反序列化的方法,允许将对象状态转换为字符串形式,或从字符串形式恢复对象状态。

class Request {
    // ... 省略构造函数、DebugPrint、Serialize、Deserialize 方法 ...

public:
    int x; // 请求的操作数1
    int y; // 请求的操作数2
    char op; // 请求的操作符(如 +、-、*、/)
};

class Response {
    // ... 省略构造函数、DebugPrint、Serialize、Deserialize 方法 ...

public:
    int result; // 响应的结果
    int code; // 响应的状态码(0表示成功,非0表示错误)
};

5. 使用自定义协议

自定义协议的使用涉及到将Request对象通过Serialize方法转换为字符串,然后通过网络传输发送给服务器。服务器接收到字符串后,使用Decode方法将其反序列化为Response对象,然后通过Serialize方法将Response对象转换为字符串回传给客户端。

四、总结

示例这种自定义协议的设计允许开发者控制数据的格式和结构,同时提供了灵活性,可以根据需要选择使用简单的文本格式或更复杂的格式(如JSON)。此外,通过在序列化和反序列化过程中进行错误检查,协议确保了数据的完整性和正确性。

温馨提示

感谢您对博主文章的关注与支持!如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。如果感兴趣的话可以关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。我们期待与您建立更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!

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

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

相关文章

第三届 SWCTF-Web 部分 WP

写在前面 题目主要涉及的是前端 php 内容知识&#xff0c;仅以本篇博客记录自己 Web 出题的奇思妙想。 Copyright © [2024] [Myon⁶]. All rights reserved. 目录 1、HTTP 2、再见了晚星 3、myon123_easy_php 4、baby_P0P 5、LOGIN!!! 1、HTTP 首页文件默认就是 ind…

BTP连接cloud connector中配置的SAP

登录地址 登录之后可以看到我们已经配置成功的后端系统SAP。 从cloud connector中获取location ID ,然后在BTP中配置Destination 选择目标标签页&#xff0c;点击‘新建目标’&#xff0c;如下图&#xff1a; 新建连接 暂时不知道错误原因 创建目标-HTTP  新建目标&…

Leetcode 第 394 场周赛

Leetcode 第 394 场周赛 1. [统计特殊字母的数量 I](https://leetcode.cn/problems/count-the-number-of-special-characters-i/)2. [统计特殊字母的数量 II](https://leetcode.cn/problems/count-the-number-of-special-characters-ii/)3. [使矩阵满足条件的最少操作次数](htt…

大一考核题解

在本篇中&#xff0c;将尽力使用多种解法&#xff0c;来达到一题多练的效果。 1&#xff1a; 1.原题链接&#xff1a; 238. 除自身以外数组的乘积 - 力扣&#xff08;LeetCode&#xff09; 这道题首先一眼肯定想到拿整体的积除以当前元素&#xff0c;将结果作为ans&#xff0c;…

护眼台灯哪个牌子最好?盘点五款目前比较好用的护眼台灯

护眼台灯哪个牌子好&#xff1f;护眼台灯比较好的牌子有书客、雷士、爱德华医生等。这些护眼台灯得益于强大的研发实力&#xff0c;不仅具备基础的照明功能&#xff0c;更在护眼效果上表现卓越。它们能够真正起到保护眼睛的作用&#xff0c;有效缓解眼部疲劳&#xff0c;为阅读…

Unity3D 羊了个羊等游戏工程源码/3D资源 大合集

Unity3D休闲益智游戏工程源码大合集 一、关卡类游戏工程源码二、跑酷类游戏工程源码三、消除合成类游戏工程源码四、棋牌类游戏工程源码五、RPG(角色扮演)类游戏工程源码六、FPS&#xff08;射击&#xff09;类游戏工程源码十、Unity3D工艺仿真六、Unity游戏资源1、Unity3D 吃鸡…

怎样快速打造二级分销小程序

乔拓云是一个专门开发小程序模板的平台&#xff0c;致力于帮助商家快速上线自己的小程序。通过套用乔拓云提供的精美模板&#xff0c;商家无需具备专业的技术背景&#xff0c;也能轻松打造出功能齐全、美观大方的小程序。 在乔拓云的官网&#xff0c;商家可以免费注册账号并登录…

【批量区域识别内容重命名】批量识别图片区域文字并重命名,批量图片部分识别内容重命文件,PDF区域识别提取重命名

我们在工作和生活中经常遇到这样的需求&#xff1a;比如将以下的图片区域识别进行重命名&#xff0c;批量识别后改成以时间和工作内容重命名&#xff0c;便于日后检索&#xff0c;快速查询 首先我们拍摄照片用到的是水印相机&#xff0c;这里的文字呢我们需要加个背景&#xff…

[数字人]唇形驱动,不生成头部动作算法总结

安全验证 - 知乎知乎&#xff0c;中文互联网高质量的问答社区和创作者聚集的原创内容平台&#xff0c;于 2011 年 1 月正式上线&#xff0c;以「让人们更好的分享知识、经验和见解&#xff0c;找到自己的解答」为品牌使命。知乎凭借认真、专业、友善的社区氛围、独特的产品机制…

【数据结构】时间复杂度的例题

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;数据结构 &#x1f337;追光的人&#xff0c;终会万丈光芒 前言&#xff1a; 这篇文章是关于时间复杂度的一些例题&#xff0c;关于时间复杂度和空间复杂度和算法的计算效率的基本知识点我放在…

【题解】NC1 大数加法(高精度加法)

https://editor.csdn.net/md?not_checkout1&spm1015.2103.3001.8066&articleId138048516 class Solution { public:string solve(string s, string t) {// write code herestring ret;int tmp 0; // 进位 本次累加和int i s.size()-1, j t.size()-1;while (i &g…

Token 在 LLM

大语言模型不能理解原始文本,所以需要把原始文本转换成大语言模型可以理解的数字表示形式,经过大模型处理以后,需要将该数字表示形式转换为人可以理解的文本。 原始文本到 token 原始文本转换为token序列的过程通常是在LLM的预处理阶段完成的。 在大型语言模型(LLM)中,tok…

五一营销新趋势:出海品牌如何利用TikTok掀起热潮

数字化时代&#xff0c;TikTok作为当下全球热门的短视频社交平台&#xff0c;以其独特的内容生态和庞大的用户基础&#xff0c;成为出海品牌进行营销的重要阵地。2024年五一国际劳动节即将来临&#xff0c;如何利用TikTok平台进行有效的营销活动&#xff0c;是每个出海品牌都需…

【埋点探针】微信小程序SDK安装

一、下载微信小程序SDK埋点代码 选择Wechat&#xff0c;复制sdk代码 在项目根目录下&#xff0c;创建sdk文件&#xff0c;webfunny.event.js 二、在app.js文件中&#xff0c;引入埋点SDK代码 首先引入sdk代码 require("./webfunny.event.js")引入兼容代码&#x…

TCP传输的粘包问题和各种异常情况

文章目录 粘包问题用分隔符用数字代表长度 TCP传输可能遇到的异常情况进程终止电脑关机正常情况下关机非正常情况关机&#xff08;停电&#xff09;电脑断网 粘包问题 粘包问题其实并不是TCP独有的的问题而是每一个面向字节流都会出现的问题&#xff0c;那么什么是粘包问题呢&…

最近做的一些套利操作

最近做的套利不多&#xff0c;主要是两个品种&#xff1a;全球芯片LOF&#xff0c;标普500LOF&#xff0c;一共盈利1360元。 盈利不多&#xff0c;但是每天我只花了3分钟点几下就赚到了&#xff0c;捡钱就像呼吸一样简单&#xff0c;还要啥自行车&#xff1f; 整理交易记录的…

如何增强Java GCExcel API 的导入和导出性能

前言 GrapeCity Documents for Excel (以下简称GcExcel) 是葡萄城公司的一款服务端表格组件&#xff0c;它提供了一组全面的 API 以编程方式生成 Excel (XLSX) 电子表格文档的功能&#xff0c;支持为多个平台创建、操作、转换和共享与 Microsoft Excel 兼容的电子表格&#xf…

JavaScript —— APIs(三)

一、事件流 &#xff08;一&#xff09;定义 &#xff08;二&#xff09;事件捕获 事件捕获&#xff0c;对话框从大到小弹出&#xff0c;先弹出爷爷&#xff0c;最后弹出儿子 &#xff08;三&#xff09;事件冒泡 冒泡事件&#xff0c;对话框从小到大弹出&#xff0c;先弹出…

【C++】C++11 包装器

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 function包装器 fu…

大模型接口管理和分发系统One API

老苏就职于一家专注于音视频实时交互技术和智能算法的创新企业。公司通过提供全面的 SDK 和解决方案&#xff0c;助力用户轻松实现实时音视频通话和消息传递等功能。尽管公司网站上有详细的文档中心&#xff0c;但在实际开发中&#xff0c;仍面临大量咨询工作。 鉴于此&#x…