【Socket 编程】应用层自定义协议与序列化

文章目录

  • 再谈协议
  • 序列化和反序列化
  • 理解 read、write、recv、send 和 tcp 为什么支持全双工
  • 自定义协议网络计算器
  • 序列化和反序列化

再谈协议

协议就是约定,协议的内容就是约定好的某种结构化数据。比如,我们要实现一个网络版的计算器,客户端发送一个算术式子,服务端根据算术式子计算出结果,再把结果返回给客户端。在此之前,客户端和服务端可以做出如下两种方案的约定

  • 方案一:
    • 客户端发送一个形如“1+1"的字符串
    • 这个字符串中有两个操作数,都是整型
    • 两个数字之间会有一个字符是运算符,运算符只能是+
    • 数字和运算符之间没有空格
    • 等等
  • 方案二:
    • 定义一个结构体表示一个请求信息,信息内容包括两个整数,和一个操作符
    • 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;
    • 定义一个结构体表示一个服务器的响应,响应的信息包括,计算结果、错误码、错误信息等

为什么要做出诸如此类的约定呢? 一切交给服务器去处理不就行了吗?

做约定的本质就是为了方便读取数据和发送数据。假使客户端和服务端没有做任何约定,在我们这个网络版计算器中,客户端就有可能发送很多无效的信息,比如随便输入一段字符串。很显然,如果客户端不做任何处理直接发送给服务端,服务端虽然也能做一些手段去检测数据的合法性,但是那样太浪费时间了

所以,为了能让整个交互过程有序且高效,客户端一定要与服务端达成某种协议,即规定发送时数据的格式与接收数据的格式。这样一来,客户端发送数据时按照约定好的格式打包好再发送,客户端就不需要考虑服务端怎么读取数据了,因为已经约定好了。相反,服务端接收数据包时按照约定,正确的实施解包的步骤就能读取到正确的数据,因为已经约定好了,客户端发送过来的数据一定是按照约定的格式存储的。
这就是约定,这就是协议

序列化和反序列化

在上面我们谈到,协议是客户端与服务端约定好的某种存储数据的结构,说白了就是一个特定的结构体。但是无论是发送请求还是发送响应,都是需要通过网络来传输的,具体地说,在将传输给网络之前,我们要把上述包含数据的结构体进一步转换成可供网络传输的格式。同样的,从网络读取到数据后,在应用层还需要将其转换回来。前者就叫序列化,后者叫反序列化
简单理解,序列化,就是网络化,就是把数据转换成某种在网络中传输的格式。反序列化,就是逆序列化过程

其中Jsoncpp库中提供了一些供序列化和反序列化的方法。

  • 创建JSON对象
Json::Value root;
  • 设置值
root["name"] = "John Doe";
root["age"] = 30;
  • 访问值
td::string name = root["name"].asString();
int age = root["age"].asInt();
  • 将数据序列化为字符串:
Json::Value root;
root["name"] = "joe";
root["sex"] = "男";
Json::FastWriter writer;
std::string s = writer.write(root);

理解 read、write、recv、send 和 tcp 为什么支持全双工

在这里插入图片描述

  • 在每台主机上,TCP连接维护了两个缓冲区,分别用来发送数据和读取数据。所以发送数据和读取数据可以同时进行。因为这俩操作不是在同一个缓冲区进行的
  • sockfd描述符本质就是一个文件描述符,但是它对应着读写两个缓冲区,操作系统会自动区分读和写操作,所以tcp通过一个sockfd就能进行读和写两种操作
  • 实际数据什么时候发,发多少,处理错误,由TCP协议控制,所以TCP叫做传输控制协议。

自定义协议网络计算器

根据前面的学习,我们可以自己简单模式一个用户层的协议,来实现网络版的计算器。
对于服务端,事件流程如下:

  • 创建套接字
  • 进入监听状态
  • 获取连接
  • 获取请求,将其反序列化,得到一个包含客户端数据的结构体
  • 处理业务
  • 将应答消息序列化,得到一个字符串,发送给客户端
  • 上述步骤重复执行
  • 关闭连接,关闭套接字

对于客户端,事件如下:

  • 创建套接字
  • 连接服务端
  • 将序列化后的数据打包成一个请求发送给服务端
  • 获取服务端的应答,将应答反序列化获取结果
  • 关闭连接,关闭套接字

既然是自定义协议,那么我们可以规定请求和应答的数据格式为:len\r\n{json}\r\njson其实就是一个用Json库处理序列化后的一个字符串(该字符串包含有效数据),len表示的是json字符串的长度

中间的换行符用于区分字节流读取进度。假设读取到的数据流有一个换行符,说明该数据流一定包含len信息,拿到len信息后就可以再判断该数据流是否包含json字符串。

于是,在请求之前,我们需要先把有效数据转换成一个Json处理过的字符串,这叫做序列化。由于是网络中是字节流传输数据,为了辨别拿到的数据是否完整,我们还需要添加一些”标识“数据,这就是报头,整合起来就是一个报文
完整代码可以去我gitee上去获取:点击查看
下面只给出序列化和反序列化实现模块

序列化和反序列化

#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <jsoncpp/json/json.h>static const std::string sep = "\r\n";// 报文格式 len\r\n{joson}\r\n// 将jsonstr格式的字符串加上报头
std::string Encode(const std::string &jsonstr)
{int len = jsonstr.size();std::string lenstr = std::to_string(len);return lenstr + sep + jsonstr + sep;
}// 将接收到的数据中的joson段提取出来
std::string Decode(std::string &packagestream)
{// 分析auto pos = packagestream.find(sep);if (pos == std::string::npos){return std::string();}std::string lenstr = packagestream.substr(0, pos);int len = std::stoi(lenstr);// 计算一个完整的报文应该多长int total = lenstr.size() + 2 * sep.size() + len;if (packagestream.size() < total){return std::string();}// 提取std::string josonstr = packagestream.substr(pos + sep.size(), len);packagestream.erase(0, total);return josonstr;
}class Request
{public:Request() {}~Request() {}Request(int x, int y, char oper): _x(x), _y(y), _oper(oper){}// 序列化bool Serialize(std::string *out){// 使用joson库Json::Value root;root["x"] = _x;root["y"] = _y;root["oper"] = _oper;Json::FastWriter write;std::string s = write.write(root);*out = s;return true;}// 反序列化bool Deserialize(const std::string &josonstr){Json::Value root;Json::Reader reader;bool res = reader.parse(josonstr, root); // 将josonstr的内容提取到root中_x = root["x"].asInt();_y = root["y"].asInt();_oper = root["oper"].asInt();return res;}void Print(){std::cout << _x << std::endl;std::cout << _y << std::endl;std::cout << _oper << std::endl;}int X(){return _x;}int Y(){return _y;}char Oper(){return _oper;}void SetValue(int x, int y, char oper){_x = x;_y = y;_oper = oper;}private:int _x;int _y;char _oper;
};// 应答
class Response
{
public:~Response() {}Response(): _result(0), _code(0), _desc("success"){}// 序列化bool Serialize(std::string *out){// 使用joson库Json::Value root;root["result"] = _result;root["code"] = _code;root["desc"] = _desc;Json::FastWriter write;std::string s = write.write(root);*out = s;return true;}// 反序列化bool Deserialize(const std::string &josonstr){Json::Value root;Json::Reader reader;bool res = reader.parse(josonstr, root); // 将josonstr的内容提取到root中_result = root["result"].asInt();_code = root["code"].asInt();_desc = root["desc"].asString();return res;}void PrintResult(){std::cout << "result: " << _result << " code: " << _code << " desc: " << _desc << std::endl;}int _result;int _code;std::string _desc;
};class Factory
{
public:static std::shared_ptr<Request> BuildRequestDefault(){return make_shared<Request>();}static std::shared_ptr<Response> BuildResponseDefault(){return make_shared<Response>();}
};

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

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

相关文章

LeetCode算法——滑动窗口矩阵篇

1、长度最小的子数组 题目描述&#xff1a; 解法&#xff1a; 设一个 for 循环来改变指向窗口末尾的指针&#xff0c;再不断抛弃当前窗口内的首元素 最终确定满足条件的最小长度 class Solution { public:int minSubArrayLen(int target, vector<int>& nums) {int …

Android --- ContentProvider 内容提供者

理论知识 ContentProvider 是 Android中用于数据共享的机制&#xff0c;主要是用于进程间(App之间)。 如何进行数据共享&#xff1f; 内容提供者 ContentProvider 提供数据&#xff0c;需要继承这个类,&#xff0c;并重写其中的增删改查方法。 继承 ContentProvider 类并重写增…

数组Arrays,排序算法,String类,Stringbulider,正则表达式

## 数组 排序 经典的三大排序&#xff1a;冒泡&#xff0c;选择&#xff0c;插入 &#xff08;一&#xff09;冒泡排序核心&#xff1a;数组中的 相邻 两项比较&#xff0c;交换&#xff08;正序or倒序&#xff09; 正序原理图&#xff1a; 代码实现&#xff1a; public s…

智慧大棚数据库版

创建一个SMartBigHouse数据库 在数据库创建一个表用来存储数据 这边将id设为主键并将标识增量设为1 搭建Winfrom 搭建历史查询界面 串口数据&#xff0c;(这边是用的一个虚拟的串口工具&#xff0c;需要的话私) ModbusSerialMaster master;DataPointCollection wenduValues; //…

opencascade AIS_Line源码学习

前言 AIS_Line 是 OpenCASCADE 库中的一个类&#xff0c;用于表示和操作三维直线。它可以通过几何线&#xff08;Geom_Line&#xff09;或者两个几何点&#xff08;Geom_Point&#xff09;来初始化。 方法 1 //! 初始化直线 aLine。 Standard_EXPORT AIS_Line(const Handl…

单片机学习历程

学习单片机的过程可以分为几个主要阶段&#xff0c;每个阶段都涉及不同的学习内容和技能提升。下面我将以一个典型的学习历程为例进行介绍&#xff1a; 初学阶段 1.入门理论学习&#xff1a; 开始接触单片机的基础知识&#xff0c;学习其工作原理、体系结构和常见的芯片类型…

Linux_make/Makefile的理解

1.make是一个命令&#xff0c;makefile是一个文件, 依赖关系和依赖方法. a.快速使用一下 i.创建一个Makefile文件(首字母也可以小写) b.依赖关系和依赖方法 i.依赖关系: 我为什么要帮你? mybin:mytest.c ii.依赖方法: 怎么帮? gcc -o mybin mytest.c make之前要注意先创建…

IDEA搭建Vue开发环境(安装Node.js、安装vue-cli、创建项目、编译项目、启动项目、yarn启动项目、npm和yarn命令行命令简单使用)

目录 1. 安装Node.js2. 安装vue-cli构建工具3. 使用vue-cli创建项目4. 启动项目5. IDEA启动vue6. 在IDEA编译vue项目7. 用yarn启动vue项目8. npm和yarn命令行命令简单使用8.1 npm8.2 yarn 1. 安装Node.js Node.js基于Google的V8引擎&#xff0c;形成了一个Javascript的运行环境…

AI绘画SD万能模型 ControlNet Union (也称ControlNet++ 或 ControlNetPlus)!10余种控制效果一键生成!

大家好&#xff0c;我是画画的小强 Controlnet 可以说是目前最重要的一款 AI 绘画控制插件&#xff0c;可以帮我们实现轮廓、深度、动作姿势、颜色等多种控制效果。由于每种控制条件都需要调用不同的控制模型&#xff0c;加上 SD1.5 和 SDXL 的生态并不互通&#xff0c;大家肯…

photoshop学习笔记——选区

选区工具快捷键&#xff1a;M shift M 切换 矩形/椭圆选区工具 基本用法 选区框选出的地方被激活&#xff08;其后进行的操作&#xff0c;仅在选区中生效&#xff09; 选区工具选择后&#xff08;以矩形选区为例&#xff09; 按下鼠标左键拖动&#xff0c;画出一块矩形区…

腾讯云COS异步操作上传(Python)

文章目录 相关概念介绍相关术语SDK使用异步上传文件 相关概念介绍 COS全称“云对象存储”&#xff08;Cloud Object Storage&#xff09;&#xff0c;是一种分布式存储服务&#xff0c;通过将数据作为对象存储&#xff0c;可以实现数据的高可靠性和可扩展性。它通常用于存储非…

《后端程序猿 · @Value 注释说明》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

SQL labs-SQL注入(四,sqlmap对于post传参方式的注入)

本文仅作为学习参考使用&#xff0c;本文作者对任何使用本文进行渗透攻击破坏不负任何责任。 序言&#xff1a;本文主要讲解基于SQL labs靶场&#xff0c;sqlmap工具进行的post传参方式的SQL注入。 传参方式有两类&#xff0c;一类是直接在url栏内进行url编码后进行的传参&am…

Android APK混淆处理方案分析

这里写目录标题 一、前言1.1 相关工具二、Apk 分析2.1 apk 解压文件2.2 apk 签名信息2.3 apk AndroidManifest.xml2.4 apk code三、Apk 处理3.1 添加垃圾文件3.2 AndroidManifest.xml 处理3.3 dex 混淆处理3.4 zipalign对齐3.5 apk 重新签名3.6 apk 安装测试四、总结一、前言 提…

sentinel 服务流量控制 、熔断降级

1、什么是 sentinel,可以用来干什么 sentinel是用来在微服务系统中保护微服务对的作用,如何避免服务的雪崩、熔断、降级,说白了就是用来替换hystrix。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 官网:GitHub - alibaba/Se…

Ubuntu安装QQ教程

Ubuntu安装QQ教程 腾讯更新Linux版的QQ&#xff0c;这里安装一下&#xff1b; 首先&#xff0c;进入官网找到合适对应的安装包&#xff1b; QQLinux版本官网&#xff1a;https://im.qq.com/linuxqq/index.shtml 我们是ubuntu系统选择X86下的deb版本&#xff0c;如果是arm开…

5G单北斗定位工卡

UWB卫星定位工卡&#xff08;HXZK-UBK&#xff09;融合定位系统结合了两种定位技术的优势&#xff0c;从而能够在各种环境下实现更精准、更可靠的定位,UWB&#xff08;ULtra-Wideband&#xff0c;超宽带&#xff09;技术和GNGSS&#xff08;GLobal Positioning System&#xff…

上海浦东装修公司推荐:高端选择,品质生活

在上海浦东这样一个经济繁荣、文化多元的地区&#xff0c;居住环境的品质直接关系到家的感觉。为了追求高品质生活的您&#xff0c;我们精心挑选了五大豪华装修公司。1.即住空间装饰即住空间装饰以其“高效、省心、精工”为核心理念&#xff0c;专注于为追求高品质生活的业主提…

【word转pdf】【最新版本jar】Java使用aspose-words实现word文档转pdf

【aspose-words-22.12-jdk17.jar】word文档转pdf 前置工作1、下载依赖2、安装依赖到本地仓库 项目1、配置pom.xml2、配置许可码文件&#xff08;不配置会有水印&#xff09;3、工具类4、效果 踩坑1、pdf乱码2、word中带有图片转换 前置工作 1、下载依赖 通过百度网盘分享的文…

嵌入式linux系统中压力测试的方法

在Linux环境下,确保系统各项资源充分且稳定地运行对任何系统管理员来说都至关重要。特别是在生产环境中,理解如何对系统资源进行基准测试和压力测试可以帮助预防未来的问题,同时也能够优化现有系统的性能。 在本文中,我们将探讨如何使用命令行工具来对Linux系统的CPU、内存…