iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > PHP编程 >协议定制 + Json序列化反序列化
  • 825
分享到

协议定制 + Json序列化反序列化

网络linux 2023-10-03 21:10:41 825人浏览 泡泡鱼
摘要

文章目录 协议定制 + Json序列化反序列化1. 再谈 "协议"1.1 结构化数据1.2 序列化和反序列化 2. 网络版计算器2.1 服务端2.2 协议定制(1) 网络发送和读取的正确

文章目录

协议定制 + JSON序列化反序列化

1. 再谈 “协议”

1.1 结构化数据

协议是一种 “约定”,Socket api的接口, 在读写数据时,都是按 “字符串” 的方式来发送接收的。如果我们要传输一些"结构化的数据" 怎么办呢?

结构化数据:

比如我们在QQ聊天时,并不是单纯地只发送了消息本身,是把自己的头像、昵称、发送时间、消息本身一起发送给别人,这种一起发送的就是结构化数据。

1.2 序列化和反序列化

  • 序列化:就是将对象转化成字节序列的过程。便于在传递和保存对象时保证对象的完整性和可传递性同时对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
  • 反序列化:就是将字节序列转化成对象的过程。便于根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。

在这里插入图片描述

  • 在socket编程的基础上,我们发现在实际生活中网络通信并不是单单发送一条消息本身,它包含了很多其他类型的数据,所以我们引入了结构化数据的概念,将这些各种类型的数据都定义在一个结构体中形成结构化数据方便被上层设置与读取。

  • 发送数据时将这个结构体按照一个规则转换成字符串,接收到数据的时候再按照相同的规则把字符串转化回结构体;实现序列化和反序列化方便网络通信。

  • 那么发送方发送的结构化数据序列化成字符串,接收方收到后是怎么知道反序列化成结构化数据呢?这是因为两者间存在定址好的协议。所以协议的本质就是双方约定好的某种格式的数据,常见的就是用结构体或者类来表达。

2. 网络版计算器

我们需要实现一个服务器版的计算器,客户端把要计算的两个数和计算类型发过去, 然后由服务器进行计算, 最后再把结果返回给客户端。

2.1 服务端

服务端创建步骤:

  1. 调用socket,创建套接字
  2. 调用bind,绑定端口
  3. 调用listen,将套接字状态设置为监听
  4. 调用accept,获取新连接
  5. 处理读取与写入的问题(重点)

2.2 协议定制

(1) 网络发送和读取的正确理解

在这里插入图片描述

客户端和服务器通信时,会调用read和write函数,它们是把数据直接发送到对端吗?不是

  • tcp协议有自己的发送缓冲区和接收缓冲区
  • 调用write本质:把用户所对应的数据拷贝到TCP的发送缓冲区
  • 调用read本质:把数据从接收缓冲区拷贝到用户层
  • 所以read和write的本质是拷贝函数
  • 把数据拷贝到TCP发送缓冲区后,剩下的数据怎么发,是由TCP决定的,所以TCP又叫做传输控制协议
  • 因为发送和接收是成对的,可以同时进行的,所以TCP协议是全双工的

综上:

  • TCP通信的本质是自己发送缓冲区的数据经过网络拷贝到对方的接收缓冲区中
  • 网络通信的本质也是拷贝
(2) 协议定制的问题

在定制协议之前先解决一个问题,之前在使用TCP协议时我们只是简单的读取,没有考虑TCP是面向字节流的,读取数据不完整的问题。这里同样存在相同的问题,如果一下子对方发送了很多报文,这些报文都堆积在TCP的接收缓冲区中,你怎么保证自己读到的是一个完整的报文呢?

我们采用这样的方式:

  • 对报文定长
  • 使用特殊符号(在报文与报文之间增加特殊符号)
  • 自描述方式(自己设计协议)

协议设计格式:

在这里插入图片描述

Protocol.hpp

#include#include#include#include#include#include#include"Util.hpp"using namespace std;// 给网络版本计算器定制协议namespace Protocol_ns{    #define SEP " "    #define SEP_LEN strlen(SEP)   // 绝对不能写成sizeof    #define HEADER_SEP "\r\n"    #define HEADER_SEP_LEN strlen("\r\n")    // "长度"\r\n" "_x op _y"\r\n    // "10 + 20" => "7"r\n""10 + 20"\r\n => 报头 + 有效载荷    // 请求/响应 = 报头\r\n有效载荷\r\n    // 请求 = 报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n    // "10 + 20" => "7"r\n""10 + 20"\r\n    string AddHeader(string&str)    {        cout<<"AddHeader 之前:\n"            <<str<<endl;        string s=to_string(str.size());        s+=HEADER_SEP;        s+=str;        s+=HEADER_SEP;        cout<<"AddHeader 之后:\n"            <<s<<endl;        return s;    }    // "7"r\n""10 + 20"\r\n => "10 + 20"     string RemoveHeader(const string&str,int len)    {        cout<<"RemoveHeader 之前:\n"            <<str<<endl;        // 从后面开始截取        string res=str.substr(str.size()-HEADER_SEP_LEN-len,len);         cout<<"RemoveHeader 之后:\n"            <<res<<endl;           return res;    }    int Readpackage(int sock,string&inbuffer,string*package)    {        cout<<"ReadPackage inbuffer 之前:\n"            <<inbuffer<<endl;        // 边读取        char buffer[1024];        ssize_t s=recv(sock,&buffer,sizeof(buffer)-1,0);        if(s<=0)            return -1;        buffer[s]=0;        inbuffer+=buffer;        cout<<"ReadPackage inbuffer 之中:\n"            <<inbuffer<<endl;        // 边分析,  "7"r\n""10 + 20"\r\n        auto pos=inbuffer.find(HEADER_SEP);        if(pos==string::npos)            return 0;                string lenStr=inbuffer.substr(0,pos);    // 获取头部字符串, 没有动inbuffer        int len=Util::toInt(lenStr);             // 得到有效载荷的长度 => "123" -> 123        int targetPackageLen=len+2*HEADER_SEP_LEN+lenStr.size();   // 得到整个报文长度        if(inbuffer.size()<targetPackageLen)     // 不是一个完整的报文            return 0;                *package=inbuffer.substr(0,targetPackageLen);  // 提取到了报文有效载荷, 没有动inbuffer        inbuffer.erase(0,targetPackageLen);      // 从inbuffer中直接移除整个报文        cout<<"ReadPackage inbuffer 之后:\n"            <<inbuffer<<endl;        return len;    }    // Request && Response都要提供序列化和反序列化功能    // 1. 自己手写    // 2. 用别人的 --- json, xml, protobuf    class Request    {    public:        Request()        {        }        Request(int x,int y,char op)            :_x(x)            ,_y(y)            ,_op(op)        {        }        // 序列化: struct->string        bool Serialize(string* outStr)             {            *outStr="";             string x_string=to_string(_x);            string y_string=to_string(_y);            // 手动序列化            *outStr=x_string + SEP + _op + SEP + y_string;            std::cout << "Request Serialize:\n"                      << *outStr << std::endl;            return true;        }        // 反序列化: string->struct        bool Deserialize(const string&inStr)           {            // inStr:  10 + 20 => [0]=>10, [1]=>+, [2]=>20            vector<string> result;            Util::StringSplit(inStr,SEP,&result);            if(result.size()!=3)                return false;            if(result[1].size()!=1)                return false;            _x=Util::toInt(result[0]);            _y=Util::toInt(result[2]);            _op=result[1][0];            return true;        }        ~Request()        {                    }    public:        // _x op _y ==> 10 * 9 ? ==> 10 / 0 ?        int _x;        int _y;        char _op;    };    class Response    {    public:        Response()        {        }                Response(int result,int code)            :_result(result)            ,_code(code)        {                    }        // 序列化: struct->string        bool Serialize(string* outStr)             {            // _result _code            *outStr="";             string res_string = to_string(_result);            string code_string = to_string(_code);            // 手动序列化            *outStr=res_string + SEP + code_string;            return true;        }        // 反序列化: string->struct        bool Deserialize(const string&inStr)           {            // 10 0            vector<string> result;            Util::StringSplit(inStr,SEP,&result);            if(result.size()!=2)                return false;            _result=Util::toInt(result[0]);            _code=Util::toInt(result[1]);            return true;        }        ~Response()        {        }    public:        int _result;        int _code;   // 0 success; 1,2,3,4代表不同错误码    };}

Util.hpp

#pragma once#include#include#includeusing namespace std;class Util{public:    // 输入: const &    // 输出: *    // 输入输出: *    static bool StringSplit(const string &str, const string &sep, vector<string> *result)    {        // 10 + 20        size_t start = 0;        while (start < str.size())        {            auto pos = str.find(sep, start);            if (pos == string::npos)                break;            result->push_back(str.substr(start, pos - start));            // 更新位置            start = pos + sep.size();        }        // 处理最后的字符串        if(start<str.size())            result->push_back(str.substr(start));        return true;    }    static int toInt(const string&s)  // 字符串转整数    {        return atoi(s.c_str());    }};

2.3 客户端

客户端创建步骤:

  1. 调用socket,创建套接字
  2. 客户端不用自己bind端口
  3. 调用connect,连接服务器
  4. 处理读取与写入的问题

2.4 代码

完整的代码:lesson36 · 遇健/Linux - 码云 - 开源中国 (gitee.com)

运行结果:

在这里插入图片描述

3. Json实现序列化反序列化

3.1 简单介绍

上面是自己定制协议实现序列化和反序列化,下面我们使用一些现成的方案来实现序列化和反序列化。c++常用的:protobuf 和 json,这里使用简单的 json。

JSON(javascript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, Java, JavaScript, Perl, python等)。这些特性使JSON成为理想的数据交换语言。Json数据由键值对组成,大括号表示对象,方括号表示数组

在这里插入图片描述

3.2 使用

  • 安装json库
yum install -y jsoncpp-devel
  • 使用json包含的头文件:
#include 

注意makefile文件要包含Json库的名称

在这里插入图片描述

我们在使用的时候直接创建Json对象来进行序列化和反序列化

Protocol.hpp

#include#include#include#include#include#include#include"Util.hpp"#includeusing namespace std;// #define MYSELF 1// 给网络版本计算器定制协议namespace Protocol_ns{    #define SEP " "    #define SEP_LEN strlen(SEP)   // 绝对不能写成sizeof    #define HEADER_SEP "\r\n"    #define HEADER_SEP_LEN strlen("\r\n")    // "长度"\r\n" "_x op _y"\r\n    // "10 + 20" => "7"r\n""10 + 20"\r\n => 报头 + 有效载荷    // 请求/响应 = 报头\r\n有效载荷\r\n    // 请求 = 报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n    // 未来: "长度"\r\n"协议号\r\n""_x op _y"\r\n         // "10 + 20" => "7"r\n""10 + 20"\r\n    string AddHeader(string&str)    {        cout<<"AddHeader 之前:\n"            <<str<<endl;        string s=to_string(str.size());        s+=HEADER_SEP;        s+=str;        s+=HEADER_SEP;        cout<<"AddHeader 之后:\n"            <<s<<endl;        return s;    }    // "7"r\n""10 + 20"\r\n => "10 + 20"     string RemoveHeader(const string&str,int len)    {        cout<<"RemoveHeader 之前:\n"            <<str<<endl;        // 从后面开始截取        string res=str.substr(str.size()-HEADER_SEP_LEN-len,len);         cout<<"RemoveHeader 之后:\n"            <<res<<endl;           return res;    }    int Readpackage(int sock,string&inbuffer,string*package)    {        cout<<"ReadPackage inbuffer 之前:\n"            <<inbuffer<<endl;        // 边读取        char buffer[1024];        ssize_t s=recv(sock,&buffer,sizeof(buffer)-1,0);        if(s<=0)            return -1;        buffer[s]=0;        inbuffer+=buffer;        cout<<"ReadPackage inbuffer 之中:\n"            <<inbuffer<<endl;        // 边分析,  "7"r\n""10 + 20"\r\n        auto pos=inbuffer.find(HEADER_SEP);        if(pos==string::npos)            return 0;                string lenStr=inbuffer.substr(0,pos);    // 获取头部字符串, 没有动inbuffer        int len=Util::toInt(lenStr);             // 得到有效载荷的长度 => "123" -> 123        int targetPackageLen=len+2*HEADER_SEP_LEN+lenStr.size();   // 得到整个报文长度        if(inbuffer.size()<targetPackageLen)     // 不是一个完整的报文            return 0;                *package=inbuffer.substr(0,targetPackageLen);  // 提取到了报文有效载荷, 没有动inbuffer        inbuffer.erase(0,targetPackageLen);      // 从inbuffer中直接移除整个报文        cout<<"ReadPackage inbuffer 之后:\n"            <<inbuffer<<endl;        return len;    }    // Request && Response都要提供序列化和反序列化功能    // 1. 自己手写    // 2. 用别人的    class Request    {    public:        Request()        {        }        Request(int x,int y,char op)            :_x(x)            ,_y(y)            ,_op(op)        {        }        // 序列化: struct->string        bool Serialize(string* outStr)             {            *outStr=""; #ifdef  MYSELF            string x_string=to_string(_x);            string y_string=to_string(_y);            // 手动序列化            *outStr=x_string + SEP + _op + SEP + y_string;            std::cout << "Request Serialize:\n"                      << *outStr << std::endl;#else            Json::Value root;   // Value: 一种万能对象, 接受任意的kv类型            root["x"]=_x;            root["y"]=_y;            root["op"]=_op;            // Json::FastWriter writer;  // writer: 是用来进行序列化的 struct -> string            Json::StyledWriter writer;            *outStr=writer.write(root);#endif            return true;        }        // 反序列化: string->struct        bool Deserialize(const string&inStr)           {#ifdef  MYSELF            // inStr:  10 + 20 => [0]=>10, [1]=>+, [2]=>20            vector<string> result;            Util::StringSplit(inStr,SEP,&result);            if(result.size()!=3)                return false;            if(result[1].size()!=1)                return false;            _x=Util::toInt(result[0]);            _y=Util::toInt(result[2]);            _op=result[1][0];#else            Json::Value root;               Json::Reader reader;  // Reader: 是用来反序列化的            reader.parse(inStr,root);            _x=root["x"].asUInt();            _y=root["y"].asUInt();            _op=root["op"].asUInt();    #endif            Print();            return true;        }        void Print()        {            std::cout << "_x: " << _x << std::endl;            std::cout << "_y: " << _y << std::endl;            std::cout << "_z: " << _op << std::endl;        }        ~Request()        {                    }    public:        // _x op _y ==> 10 * 9 ? ==> 10 / 0 ?        int _x;        int _y;        char _op;    };    class Response    {    public:        Response()        {        }                Response(int result,int code)            :_result(result)            ,_code(code)        {                    }        // 序列化: struct->string        bool Serialize(string* outStr)             {            // _result _code            *outStr=""; #ifdef  MYSELF            string res_string = to_string(_result);            string code_string = to_string(_code);            // 手动序列化            *outStr=res_string + SEP + code_string;#else            Json::Value root;               root["result"]=_result;            root["code"]=_code;            // Json::FastWriter writer;            Json::StyledWriter writer;            *outStr=writer.write(root);#endif            return true;        }        // 反序列化: string->struct        bool Deserialize(const string&inStr)           {#ifdef  MYSELF            // 10 0            vector<string> result;            Util::StringSplit(inStr,SEP,&result);            if(result.size()!=2)                return false;            _result=Util::toInt(result[0]);            _code=Util::toInt(result[1]);#else            Json::Value root;            Json::Reader reader;             reader.parse(inStr, root);            _result = root["result"].asUInt();            _code = root["code"].asUInt();#endif            Print();            return true;        }        void Print()        {            std::cout << "_result: " << _result << std::endl;            std::cout << "_code: " << _code << std::endl;        }        ~Response()        {        }    public:        int _result;        int _code;   // 0 success; 1,2,3,4代表不同错误码    };}

完整代码:lesson36/NetCal_v2 · 遇健/Linux - 码云 - 开源中国 (gitee.com)

来源地址:https://blog.csdn.net/Ryujianli/article/details/132722939

--结束END--

本文标题: 协议定制 + Json序列化反序列化

本文链接: https://www.lsjlt.com/news/423176.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 协议定制 + Json序列化反序列化
    文章目录 协议定制 + Json序列化反序列化1. 再谈 "协议"1.1 结构化数据1.2 序列化和反序列化 2. 网络版计算器2.1 服务端2.2 协议定制(1) 网络发送和读取的正确...
    99+
    2023-10-03
    网络 linux
  • 协议,序列化,反序列化,Json
    文章目录 协议序列化和反序列化网络计算器protocol.hppServer.hppServer.ccClient.hppClient.cclog.txt通过结果再次理解通信过程 Json...
    99+
    2023-09-01
    json 网络 服务器
  • TCP定制协议,序列化和反序列化
    目录 前言 1.理解协议 2.网络版本计算器 2.1设计思路 2.2接口设计 2.3代码实现: 2.4编译测试 总结 前言         在之前的文章中,我们说TCP是面向字节流的,但是可能对于面向字节流这个概念,其实并不理解的,今...
    99+
    2023-08-31
    tcp/ip 网络 java
  • C#对Json进行序列化和反序列化
    一、Json简介 Json(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于JS的一个子集。 Json采用完全独立于语言的文本格式。这使得J...
    99+
    2022-11-13
  • SpringBoot之Json的序列化和反序列化问题
    目录控制json序列化/反序列化1. @JsonIgnoreProperties的用法2. @JsonProperty 注解3. @JsonCreator 注解4. @JsonSet...
    99+
    2022-11-13
  • 序列化与反序列化
    序列化(pickling)   把变量从内存中变成可存储或传输的过程 反序列化(unpickling)   把变量内容从序列化的对象重新读到内存里的过程 序列化&反序列化的意义  在程序运行过程中,对象可在内存中被自由的修改  一...
    99+
    2023-01-31
    序列 化与 序列化
  • C#如何对Json进行序列化和反序列化
    这篇“C#如何对Json进行序列化和反序列化”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C#如何对Json进行序列化和反序...
    99+
    2023-06-30
  • JavaScript中怎么用JSON来序列化与反序列化
    小编给大家分享一下JavaScript中怎么用JSON来序列化与反序列化,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!用JSON...
    99+
    2022-10-19
  • Golang中Json的序列化和反序列化的使用
    目录 JSON:创建格式:基本数据类型序列化:map序列化:切片序列化:反序列化为结构体:反序列化为map:反序列化为切片: JSON: JSON(JavaScr...
    99+
    2022-11-13
  • C#对JSON与对象的序列化与反序列化
    一、利用Web服务中的JavaScriptSerializer 类 System.Web.Script.Serialization空间,位于System.Web.extensions...
    99+
    2022-11-13
  • JSON序列化与反序列化NULL值丢失问题
    做项目一般都会有一些特殊的需求,例如保留json中的null值,但是fastjson都会把null值得属性给过滤掉 json序列化保留null值 com.alibaba.fastjson.JSON.toJSONString(list, ...
    99+
    2023-09-14
    java Powered by 金山文档
  • springboot序列化和反序列化怎么定义
    在Spring Boot中,序列化和反序列化可以通过以下方式进行定义:1. 实现Serializable接口:在需要进行序列化和反序...
    99+
    2023-08-14
    springboot
  • 【反序列化漏洞-01】序列化与反序列化概述
    为什么要序列化 百度百科上关于序列化的定义是,将对象的状态信息转换为可以存储或传输的形式(字符串)的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区(非关系型键值对形式的数据库Redis,与数组类似)。以后,可以通过从存储区中...
    99+
    2023-09-06
    web安全 安全 反序列化 序列化 PHP反序列化漏洞 Powered by 金山文档
  • [Java反序列化]—Shiro反序列化(一)
    环境配置:  IDEA搭建shiro550复现环境_普通网友的博客-CSDN博客 漏洞原理: Apache Shiro框架提供了记住密码的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。在服务端对remem...
    99+
    2023-09-03
    java 开发语言
  • Java序列化与反序列化
    目录Java 序列化与反序列化序列化APIObjectOutputStreamObjectInputStreamserialVersionUIDTransient (瞬态变量)实例理...
    99+
    2023-05-14
    Java序列化 Java反序列化
  • PHP序列化和反序列化
    一.什么是序列化和反序列化 php类与对象 类是定义一系列属性和操作的模板,而对象,就是把属性进行实例化,完事交给类里面的方法,进行处理。 `。尝试构造payload: ...
    99+
    2023-08-31
    php 开发语言
  • Jil、json序列化和反序列化库的示例分析
    这篇文章主要介绍了Jil、json序列化和反序列化库的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Jil很牛,比Newtonsof...
    99+
    2022-10-19
  • Golang中Json的序列化和反序列化怎么使用
    这篇文章主要介绍“Golang中Json的序列化和反序列化怎么使用”,在日常操作中,相信很多人在Golang中Json的序列化和反序列化怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Golang中Js...
    99+
    2023-06-30
  • JavaScript如何实现基于JSON的序列化和反序列化
    这篇文章将为大家详细讲解有关JavaScript如何实现基于JSON的序列化和反序列化,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。基于JSON的序列化和反序列化(ser...
    99+
    2022-10-19
  • Json字符串的序列化与反序列化怎么实现
    今天小编给大家分享一下Json字符串的序列化与反序列化怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作