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

协议,序列化,反序列化,Json

json网络服务器 2023-09-01 17:09:12 263人浏览 八月长安
摘要

文章目录 协议序列化和反序列化网络计算器protocol.hppServer.hppServer.ccClient.hppClient.cclog.txt通过结果再次理解通信过程 Json

文章目录

协议

协议究竟是什么呢?首先得知道主机之间的网络通信交互的是什么数据,像平时使用聊天APP聊天可以清楚,用户看到的不仅仅是聊天的文字,还能够看到用户的头像昵称等其他属性。也就可以证明网络通信不仅仅是交互字符串那么简单。事实上网络通信还可能会通过一个结构化的数据去交互,例如聊天软件里,一台主机向另一台发送消息,这个消息里面就包含了头像等其他的数据。

一台主机发送数据会把所有的数据整合成一个结构化数据统一发送,而收到数据的主机再将这个结构化数据分解成原始的每个独立的数据。而为了确保主机之间收到数据后能够成功的分解,整合和分解两个过程必须是按照统一的约定来执行,而这个约定就是协议

序列化和反序列化

上述的将所有需要发送的数据整合到一起的过程就称为序列化过程而分解的过程就称为反序列化过程

网络的通信就可以理解为:

image-20230806212144823

本篇文章就利用编写一个最简单的网络计算器来感受这个通信的过程

网络计算器

protocol.hpp

这个头文件用来编写协议及序列化反序列化的过程。

  1. 首先因为是一个计算器所以肯定需要两个数和一个计算符号,但是作为服务端不能要求客户怎么样去输入这个计算的格式,可能客户会输入 1+1 也可能会输入 1 + 1 。因此作为服务端要将客户的输入识别成自己的规定。这里就规定数 计算符号 数。所以可以先规定好分隔符,利用宏定义方便修改。
  2. 因为tcp是面向字节流的,所以要明确通信的数据的边界,不能读多也不能读少。因为对于TCP而言,它是全双工的,所以就会出现接收方来不及读,导致整个缓冲区里有大量的数据,因此就要规定好边界保证读到的是一个完整的数据
  3. 对于计算器而言,首先是要获得到需要计算的数据,然后处理得到计算结果。因此可以定义两个结构体,一个结构体负责发送请求也就是发送计算的数据,另一个结构体负责响应请求也就是处理计算结果。
  4. 在发送请求的结构体里保存了两个数和计算符号,但是由于需要网络通信,所以要在结构体里定义好序列化过程的方法。同时服务端拿到数据后要想处理数据就必须要先反序列化,所以结构体里也定义好反序列化过程的方法
  5. 在响应请求的结构体里,同样的服务端需要将计算好的数据发回给客户端也需要定义好序列化过程的方法,而客户端要获取数据也需要反序列化过程的方法
  6. 因为要确保读到的数据是完整的一个数据,因此可以定义一个函数,将序列化好的数据加上一个报头,也就是这个数据的长度,用来标识这个数据的长度,读的时候就可以根据长度的依据去读取。
  7. 当然因为这个报头并不是需要真正要传输的数据,所以需要再定义一个函数用来去掉这个报头
  8. 当两端读取到数据时就需要判断读到的是否是一个完整的数据。如果还没读到完整的数据就继续读。可以定义一个函数用来读取数据并且判断是否读到完整数据,这个依据就是读到的数据的长度是否和原数据的长度相等,这个原数据长度为报头加上正文加上分隔符的长度
#pragma once#include #include #include #include #include #include using namespace std;// 定义好分隔符#define SEP " "                       // 一条数据里每个元素的分隔符#define SEP_LEN strlen(SEP)           // 分隔符的大小#define LINE_SEP "\r\n"               // 数据与数据的分隔符#define LINE_SEP_LEN strlen(LINE_SEP) // 分隔符大小// 为通信的数据加上数据的长度和分割// 确保每条数据都能精确的读取到,不读多也不读少// "text.size()"\r\n"text"\r\nstring enlength(const string &text){    string res = to_string(text.size());    res += LINE_SEP;    res += text;    res += LINE_SEP;    return res;}// 将全部的一条数据去掉前面的数据长度// 提取出原始数据bool delength(const string &package, string *text){    // 找到第一个元素分隔符,弃掉前面的长度    auto pos = package.find(LINE_SEP);    if (pos == string::npos)        return false;    // 确认正文的长度    int len = stoi(package.substr(0, pos));    // 从第一个分割符往后开始到记录的字符串长度就是原始的数据    *text = package.substr(pos + LINE_SEP_LEN, len);    return true;}// 请求class Request{public:    int _x;    int _y;    char _op;    Request()        : _x(0), _y(0), _op('0')    {    }    Request(int x, int y, char op)        : _x(x), _y(y), _op(op)    {    }    // 序列化过程    // 因为通信的数据一开始分为了好几个独立的元素    // 所以将这些独立的元素合并成一个数据    bool serialize(string *out)    {        *out = "";        *out += to_string(_x);        *out += SEP;        *out += _op;        *out += SEP;        *out += to_string(_y);        return true;    }    // 反序列化过程    // 将合并的一整个数据分解回原始的几个独立数据    bool unserialize(const string &in)    {        auto left = in.find(SEP);        auto right = in.rfind(SEP);        if (left == string::npos || right == string::npos || left == right)            return false;        // 因为对于计算器而言,计算符号只有1位        if (right - left - SEP_LEN != 1)            return false;        _x = stoi(in.substr(0, left));        _y = stoi(in.substr(right + SEP_LEN));        _op = in[left + SEP_LEN];        return true;    }};// 响应请求class Response{public:    int _exitcode; // 返回码    int _result;   // 返回结果    Response()        : _exitcode(0), _result(0)    {    }    Response(int exitcode, int result)        : _exitcode(exitcode), _result(result)    {    }    bool serialize(string *out)    {        *out = "";        *out += to_string(_exitcode);        *out += SEP;        *out += to_string(_result);        return true;    }    bool unserialize(const string &in)    {        auto pos = in.find(SEP);        if (pos == string::npos)            return false;        _exitcode = stoi(in.substr(0, pos));        _result = stoi(in.substr(pos + SEP_LEN));        return true;    }};// 读取数据并且判断是否是个完整的数据的方法bool recvPackage(int sock, string &buff, string *text){    char buffer[1024];    while (1)    {        ssize_t n = recv(sock, buffer, sizeof(buffer), 0);        if (n > 0)        {            // 找到报头和正文之间的分隔符            buffer[n] = 0;            buff += buffer;            auto pos = buff.find(LINE_SEP);            if (pos == string::npos)                continue;            // 拿到正文的长度            int len = stoi(buff.substr(0, pos));            // 判断inbuff的长度是否等于整个数据的长度            // 如果相等说明读到了完成的数据            int max_len = len + 2 * LINE_SEP_LEN + buff.substr(0, pos).size(); // 整个数据的长度            if (buff.size() < max_len)                continue;            cout << "目前拿到的所有报文:\n" << buff << endl;            // 到这一步说明至少有一个完整的数据            // 将整个完整的数据传回指针            *text = buff.substr(0, max_len);            cout << "完整的报文:\n" << *text << endl;            buff.erase(0, max_len);            return true;        }        else            return false;    }    return true;}

Server.hpp

服务端就定义一个函数将整个的读取、反序列化、计算、序列化、发送的过程全部编写好,然后服务端启动加上一个函数参数,也就是计算过程的函数。

#pragma once#include "log.hpp"#include "Protocol.hpp"#include #include #include #include #include #include #include #include typedef function<bool(const Request &req, Response &res)> func_t;void HandlerEntery(int sock, func_t func){    string buff;    while (1)    {        // 读取        // 需要保证读到的是一个完整的请求        string req_text;        if (!recvPackage(sock, buff, &req_text))            return;        cout << "带报头的请求: \n" << req_text << endl;        // 将读到的数据的头部去掉,也就是数据长度        string req_str;        if (!delength(req_text, &req_str))            return;        cout << "原始数据: " << req_str << endl;        // 对读到的原始数据进行反序列化        Request req;        if (!req.unserialize(req_str))            return;        // 将反序列化后的结果计算出来后        // 将结果放到响应类对象里        // 通过响应类对象提取到结果        Response res;        func(req, res);        string res_str;        // 得到响应类对象序列化结果        res.serialize(&res_str);        cout << "计算完成,结果序列化:" << res_str << endl;        // 再将得到的结果加上报头        // 也就是数据长度确保数据的精确读取        // 得到最终的序列化数据        res_str = enlength(res_str);        cout << "构建完整序列化数据完成:\n" << res_str << endl;        // 将最终的数据发送回去        send(sock, res_str.c_str(), res_str.size(), 0);        cout << "服务端发送完成" << endl;    }}class Server{public:    Server(const uint16_t &port = 8000)        : _port(port)    {    }    void Init()    {        // 创建负责监听的套接字 面向字节流        _listenSock = Socket(AF_INET, SOCK_STREAM, 0);        if (_listenSock < 0)        {            LogMessage(FATAL, "create socket error!");            exit(1);        }        LogMessage(NORMAL, "create socket %d success!", _listenSock);        // 绑定网络信息        struct sockaddr_in local;        memset(&local, 0, sizeof(local));        local.sin_family = AF_INET;        local.sin_port = htons(_port);        local.sin_addr.s_addr = INADDR_ANY;        if (bind(_listenSock, (struct sockaddr *)&local, sizeof(local)) < 0)        {            LogMessage(FATAL, "bind socket error!");            exit(3);        }        LogMessage(NORMAL, "bind socket success!");        // 设置socket为监听状态        if (listen(_listenSock, 5) < 0)        {            LogMessage(FATAL, "listen socket error!");            exit(4);        }        LogMessage(NORMAL, "listen socket success!");    }    void start(func_t func)    {        while (1)        {            // server获取建立新连接            struct sockaddr_in peer;            memset(&peer, 0, sizeof(peer));            socklen_t len = sizeof(peer);            // 创建通信的套接字            // accept的返回值才是真正用于通信的套接字            _sock = accept(_listenSock, (struct sockaddr *)&peer, &len);            if (_sock < 0)            {                // 获取通信的套接字失败并不影响未来的操作,只是当前的链接失败而已                LogMessage(ERROR, "accept socket error, next");                continue;            }            LogMessage(NORMAL, "accept socket %d success", _sock);            cout << "sock: " << _sock << endl;            // 利用多进程实现            pid_t id = fork();            if (id == 0) // child            {                close(_listenSock);                // 调用方法包括读取、反序列化、计算、序列化、发送                HandlerEntery(_sock, func);                close(_sock);                exit(0);            }            close(_sock);            // father            pid_t ret = waitpid(id, nullptr, 0);            if (ret > 0)            {                LogMessage(NORMAL, "wait child success"); // ?            }        }    }private:    int _listenSock; // 负责监听的套接字    int _sock;       // 通信的套接字    uint16_t _port;  // 端口号};

Server.cc

这个服务端的计算函数就通过结构体的对象去作为参数完成,因为需要的数据都在结构体里

#include "Server.hpp"#include // 输出命令错误函数void Usage(string proc){    cout << "Usage:\n\t" << proc << " local_ip local_port\n\n";}// 计算方式bool cal(const Request &req, Response &res){    res._exitcode = 0;    res._result = 0;    switch (req._op)    {    case '+':        res._result = req._x + req._y;        break;    case '-':        res._result = req._x - req._y;        break;    case '*':        res._result = req._x * req._y;        break;    case '/':    {        if (req._y == 0)            res._exitcode = 1;        else            res._result = req._x / req._y;    }    break;    default:        res._exitcode = 2;        break;    }    return true;}int main(int arGC, char *argv[]){    // 启动服务端不需要指定IP    if (argc != 2)    {        Usage(argv[0]);        exit(1);    }    uint16_t port = atoi(argv[1]);    unique_ptr<Server> server(new Server(port));    // 服务端初始化    server->Init();    //服务端启动    server->start(cal);    return 0;}

Client.hpp

客户端和服务端一样也需要接收发送,不过客户端是先发送再接收。并且上述提过因为客户的输入方式无法控制,所以要定义一个函数将客户输入的数据提取到两个数和计算符号才能够构造请求的结构体对象

    #pragma once#include #include #include #include #include #include #include #include #include "log.hpp"#include "Protocol.hpp"using namespace std;class Client{public:    Client(const string &serverip, const uint16_t &port)        : _serverip(serverip), _port(port), _sock(-1)    {    }    void Init()    {        // 创建套接字        _sock = socket(AF_INET, SOCK_STREAM, 0);        if (_sock < 0)        {            LogMessage(FATAL, "create socket error");            exit(1);        }        // TCP的客户端也不需要显示绑定端口,让操作系统随机绑定        // TCP的客户端也不需要监听,因为并没有去主动链接客户端,所以不需要accept        // TCP的客户端也不需要监听,因为并没有去主动链接客户端,所以不需要accept    }    void start()    {        // 向服务端发起链接请求        struct sockaddr_in local;        memset(&local, 0, sizeof(local));        local.sin_family = AF_INET;        local.sin_port = htons(_port);        local.sin_addr.s_addr = inet_addr(_serverip.c_str());        if (connect(_sock, (struct sockaddr *)&local, sizeof(local)) < 0)            LogMessage(ERROR, "connect socket error");        // 和服务端通信        else        {            string line;            string buffer;            while (1)            {                cout << "Please cin: " << endl;                getline(cin, line);                Request req = ParseLine(line);                string text;                req.serialize(&text);                cout << "序列化后的数据:" << text << endl;                string send_str = enlength(text);                cout << "添加报头后的数据: \n" << send_str << endl;                send(_sock, send_str.c_str(), send_str.size(), 0);                // read                // 拿到完整报文                string package;                if (!recvPackage(_sock, buffer, &package))                    continue;                cout << "拿到的完整报文: \n" << package << endl;                // 拿到正文                string end_text;                if (!delength(package, &end_text))                    continue;                cout << "拿到的正文:" << end_text << endl;                // 反序列化                Response res;                res.unserialize(end_text);                cout << "exitCode: " << res._exitcode << " result: " << res._result << endl;            }        }    }    ~Client()    {        if (_sock >= 0)            close(_sock);    }    // 将客户输入的数据提取构造请求结构体对象    Request ParseLine(const string &line)    {        auto it = line.begin();        // 提取左边的数字        string left;        while (it != line.end() && *it >= '0' && *it <= '9')        {            left += *it;            ++it;        }        int leftnum = atoi(left.c_str());        // 提取符号        while (it != line.end() && *it != '+' && *it != '-' && *it != '+' && *it != '/')            ++it;        char op = *it;        // 提取右边数字        while (it != line.end() && (*it < '0' || *it > '9'))            ++it;        string right;        while (it != line.end() && *it >= '0' && *it <= '9')        {            right += *it;            ++it;        }        int rightnum = atoi(right.c_str());        return Request(leftnum, rightnum, op);    }private:    int _sock;    string _serverip;    uint16_t _port;};

Client.cc

#include "Client.hpp"#include // 输出命令错误函数void Usage(string proc){    cout << "Usage:\n\t" << proc << " local_ip local_port\n\n";}int main(int argc, char *argv[]){    // 再运行客户端时,输入的指令需要包括主机ip和端口号    if (argc != 3)    {        Usage(argv[0]);        exit(1);    }    string serverip = argv[1];    uint16_t port = atoi(argv[2]);    unique_ptr<Client> client(new Client(serverip, port));    client->Init();    client->start();    return 0;}

log.txt

这里还加了一个记录日志的方法,可加可不加

#pragma once#include #include #include #include #include using namespace std;#define DEBUG 0#define NORMAL 1#define WARNING 2#define ERROR 3#define FATAL 4const char *to_levelstr(int level){    switch (level)    {    case DEBUG:        return "DEBUG";    case NORMAL:        return "NORMAL";    case WARNING:        return "WARNING";    case ERROR:        return "ERROR";    case FATAL:        return "FATAL";    default:        return nullptr;    }}void LogMessage(int level, const char *format, ...){#define NUM 1024    char logpre[NUM];    snprintf(logpre, sizeof(logpre), "[%s][%ld][%d]", to_levelstr(level), (long int)time(nullptr), getpid());    char line[NUM];    // 可变参数    va_list arg;    va_start(arg, format);    vsnprintf(line, sizeof(line), format, arg);    // 保存至文件    FILE* log = fopen("log.txt", "a");    FILE* err = fopen("log.error", "a");    if(log && err)    {        FILE *curr = nullptr;        if(level == DEBUG || level == NORMAL || level == WARNING)             curr = log;        if(level == ERROR || level == FATAL)             curr = err;        if(curr) fprintf(curr, "%s%s\n", logpre, line);        fclose(log);        fclose(err);    }}

通过结果再次理解通信过程

image-20230806220726845

所以最终的流程可以分解为几个步骤:

image-20230806221206524

JSON

上面的序列化和反序列化过程呢都是自己定义的,所以看起来并不好看,而且可读性也不美观。

其实也会第三方库是帮我们做好了序列化和反序列化工作的,例如 json,protobuf。因为Json的使用比较简单,所以这里就使用Json

首先需要安装第三方的 Jsoncpp的库

yum install jsoncpp-devel

安装好之后就可以使用第三方库了,需要注意因为是第三方库和线程库一样,编译的时候需要加上 -lJsoncpp的选项

肯定下面的代码注释就可以了解到Json的使用了,注:为了不修改上述的一些代码,下面使用条件编译,只看Json部分即可

// 请求class Request{public:    int _x;    int _y;    char _op;    Request()        : _x(0), _y(0), _op('0')    {    }    Request(int x, int y, char op)        : _x(x), _y(y), _op(op)    {    }    // 序列化过程    // 因为通信的数据一开始分为了好几个独立的元素    // 所以将这些独立的元素合并成一个数据    bool serialize(string *out)    {#ifdef MYSELF        *out = "";        *out += to_string(_x);        *out += SEP;        *out += _op;        *out += SEP;        *out += to_string(_y);#else        // Value是万能类型        // 需要先定义出对象        Json::Value root;        // Json是kv结构存储的,所以需要定义k值标识v值        root["first"] = _x;        root["second"] = _y;        root["op"] = _op;        // Json要写入值给别的变量也需要先定义对象        // 写的对象类型可以有几种,这里采用FastWriter        Json::FastWriter w;        // 调用write方法就可以写入        *out = w.write(root);#endif        return true;    }    // 反序列化过程    // 将合并的一整个数据分解回原始的几个独立数据    bool unserialize(const string &in)    {#ifdef MYSELF        auto left = in.find(SEP);        auto right = in.rfind(SEP);        if (left == string::npos || right == string::npos || left == right)            return false;        // 因为对于计算器而言,计算符号只有1位        if (right - left - SEP_LEN != 1)            return false;        _x = stoi(in.substr(0, left));        _y = stoi(in.substr(right + SEP_LEN));        _op = in[left + SEP_LEN];#else        // 同样的需要先定义对象        // 读的对象也需要定义        Json::Value root;        Json::Reader reader;        // 调用读方法,将root的值读到in中        reader.parse(in, root);        // asInt表示切换为整形类型        // 通过k值就可以得到v值        _x = root["first"].asInt();        _y = root["second"].asInt();        _op = root["op"].asInt();#endif        return true;    }};// 响应请求class Response{public:    int _exitcode; // 返回码    int _result;   // 返回结果    Response()        : _exitcode(0), _result(0)    {    }    Response(int exitcode, int result)        : _exitcode(exitcode), _result(result)    {    }    bool serialize(string *out)    {#ifdef MYSELF        *out = "";        *out += to_string(_exitcode);        *out += SEP;        *out += to_string(_result);#else        Json::Value root;        root["exitcode"] = _exitcode;        root["result"] = _result;        Json::FastWriter w;        *out = w.write(root);#endif        return true;    }    bool unserialize(const string &in)    {#ifdef MYSELF        auto pos = in.find(SEP);        if (pos == string::npos)            return false;        _exitcode = stoi(in.substr(0, pos));        _result = stoi(in.substr(pos + SEP_LEN));#else        Json::Value root;        Json::Reader reader;        reader.parse(in, root);        _exitcode = root["exitcode"].asInt();        _result = root["result"].asInt();#endif        return true;    }};

只需要更改序列化反序列化过程即可,外面的协定不需要改变

效果

image-20230806222713722

使用 Json序列化的就很美观

来源地址:https://blog.csdn.net/CHJBL/article/details/132136729

--结束END--

本文标题: 协议,序列化,反序列化,Json

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

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

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

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

下载Word文档
猜你喜欢
  • 协议,序列化,反序列化,Json
    文章目录 协议序列化和反序列化网络计算器protocol.hppServer.hppServer.ccClient.hppClient.cclog.txt通过结果再次理解通信过程 Json...
    99+
    2023-09-01
    json 网络 服务器
  • 协议定制 + Json序列化反序列化
    文章目录 协议定制 + Json序列化反序列化1. 再谈 "协议"1.1 结构化数据1.2 序列化和反序列化 2. 网络版计算器2.1 服务端2.2 协议定制(1) 网络发送和读取的正确...
    99+
    2023-10-03
    网络 linux
  • 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 金山文档
  • 【反序列化漏洞-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
  • Go语言对JSON数据进行序列化和反序列化
    golang中对json的序列化/反序列化操作还是比较容易的,序列化操作主要是通过encoding/json包的Marshal()方法来实现,反序列化操作主要是通过encoding/...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作