国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

協(xié)議,序列化,反序列化,Json

這篇具有很好參考價(jià)值的文章主要介紹了協(xié)議,序列化,反序列化,Json。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

協(xié)議

協(xié)議究竟是什么呢?首先得知道主機(jī)之間的網(wǎng)絡(luò)通信交互的是什么數(shù)據(jù),像平時(shí)使用聊天APP聊天可以清楚,用戶看到的不僅僅是聊天的文字,還能夠看到用戶的頭像昵稱等其他屬性。也就可以證明網(wǎng)絡(luò)通信不僅僅是交互字符串那么簡(jiǎn)單。事實(shí)上網(wǎng)絡(luò)通信還可能會(huì)通過一個(gè)結(jié)構(gòu)化的數(shù)據(jù)去交互,例如聊天軟件里,一臺(tái)主機(jī)向另一臺(tái)發(fā)送消息,這個(gè)消息里面就包含了頭像等其他的數(shù)據(jù)。

一臺(tái)主機(jī)發(fā)送數(shù)據(jù)會(huì)把所有的數(shù)據(jù)整合成一個(gè)結(jié)構(gòu)化數(shù)據(jù)統(tǒng)一發(fā)送,而收到數(shù)據(jù)的主機(jī)再將這個(gè)結(jié)構(gòu)化數(shù)據(jù)分解成原始的每個(gè)獨(dú)立的數(shù)據(jù)。而為了確保主機(jī)之間收到數(shù)據(jù)后能夠成功的分解,整合和分解兩個(gè)過程必須是按照統(tǒng)一的約定來執(zhí)行,而這個(gè)約定就是協(xié)議

序列化和反序列化

上述的將所有需要發(fā)送的數(shù)據(jù)整合到一起的過程就稱為序列化過程而分解的過程就稱為反序列化過程

網(wǎng)絡(luò)的通信就可以理解為:

協(xié)議,序列化,反序列化,Json,網(wǎng)絡(luò),json,網(wǎng)絡(luò),服務(wù)器

本篇文章就利用編寫一個(gè)最簡(jiǎn)單的網(wǎng)絡(luò)計(jì)算器來感受這個(gè)通信的過程

網(wǎng)絡(luò)計(jì)算器

protocol.hpp

這個(gè)頭文件用來編寫協(xié)議及序列化反序列化的過程。

  1. 首先因?yàn)槭且粋€(gè)計(jì)算器所以肯定需要兩個(gè)數(shù)和一個(gè)計(jì)算符號(hào),但是作為服務(wù)端不能要求客戶怎么樣去輸入這個(gè)計(jì)算的格式,可能客戶會(huì)輸入 1+1 也可能會(huì)輸入 1 + 1 。因此作為服務(wù)端要將客戶的輸入識(shí)別成自己的規(guī)定。這里就規(guī)定數(shù) 計(jì)算符號(hào) 數(shù)。所以可以先規(guī)定好分隔符,利用宏定義方便修改。
  2. 因?yàn)門CP是面向字節(jié)流的,所以要明確通信的數(shù)據(jù)的邊界,不能讀多也不能讀少。因?yàn)?strong>對(duì)于TCP而言,它是全雙工的,所以就會(huì)出現(xiàn)接收方來不及讀,導(dǎo)致整個(gè)緩沖區(qū)里有大量的數(shù)據(jù),因此就要規(guī)定好邊界保證讀到的是一個(gè)完整的數(shù)據(jù)
  3. 對(duì)于計(jì)算器而言,首先是要獲得到需要計(jì)算的數(shù)據(jù),然后處理得到計(jì)算結(jié)果。因此可以定義兩個(gè)結(jié)構(gòu)體,一個(gè)結(jié)構(gòu)體負(fù)責(zé)發(fā)送請(qǐng)求也就是發(fā)送計(jì)算的數(shù)據(jù),另一個(gè)結(jié)構(gòu)體負(fù)責(zé)響應(yīng)請(qǐng)求也就是處理計(jì)算結(jié)果。
  4. 在發(fā)送請(qǐng)求的結(jié)構(gòu)體里保存了兩個(gè)數(shù)和計(jì)算符號(hào),但是由于需要網(wǎng)絡(luò)通信,所以要在結(jié)構(gòu)體里定義好序列化過程的方法。同時(shí)服務(wù)端拿到數(shù)據(jù)后要想處理數(shù)據(jù)就必須要先反序列化,所以結(jié)構(gòu)體里也定義好反序列化過程的方法
  5. 在響應(yīng)請(qǐng)求的結(jié)構(gòu)體里,同樣的服務(wù)端需要將計(jì)算好的數(shù)據(jù)發(fā)回給客戶端也需要定義好序列化過程的方法,而客戶端要獲取數(shù)據(jù)也需要反序列化過程的方法
  6. 因?yàn)橐_保讀到的數(shù)據(jù)是完整的一個(gè)數(shù)據(jù),因此可以定義一個(gè)函數(shù),將序列化好的數(shù)據(jù)加上一個(gè)報(bào)頭,也就是這個(gè)數(shù)據(jù)的長(zhǎng)度,用來標(biāo)識(shí)這個(gè)數(shù)據(jù)的長(zhǎng)度,讀的時(shí)候就可以根據(jù)長(zhǎng)度的依據(jù)去讀取。
  7. 當(dāng)然因?yàn)檫@個(gè)報(bào)頭并不是需要真正要傳輸?shù)臄?shù)據(jù),所以需要再定義一個(gè)函數(shù)用來去掉這個(gè)報(bào)頭
  8. 當(dāng)兩端讀取到數(shù)據(jù)時(shí)就需要判斷讀到的是否是一個(gè)完整的數(shù)據(jù)。如果還沒讀到完整的數(shù)據(jù)就繼續(xù)讀。可以定義一個(gè)函數(shù)用來讀取數(shù)據(jù)并且判斷是否讀到完整數(shù)據(jù),這個(gè)依據(jù)就是讀到的數(shù)據(jù)的長(zhǎng)度是否和原數(shù)據(jù)的長(zhǎng)度相等,這個(gè)原數(shù)據(jù)長(zhǎng)度為報(bào)頭加上正文加上分隔符的長(zhǎng)度
#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <jsoncpp/json/json.h>

using namespace std;

// 定義好分隔符
#define SEP " "                       // 一條數(shù)據(jù)里每個(gè)元素的分隔符
#define SEP_LEN strlen(SEP)           // 分隔符的大小
#define LINE_SEP "\r\n"               // 數(shù)據(jù)與數(shù)據(jù)的分隔符
#define LINE_SEP_LEN strlen(LINE_SEP) // 分隔符大小

// 為通信的數(shù)據(jù)加上數(shù)據(jù)的長(zhǎng)度和分割
// 確保每條數(shù)據(jù)都能精確的讀取到,不讀多也不讀少
// "text.size()"\r\n"text"\r\n
string enlength(const string &text)
{
    string res = to_string(text.size());
    res += LINE_SEP;
    res += text;
    res += LINE_SEP;

    return res;
}

// 將全部的一條數(shù)據(jù)去掉前面的數(shù)據(jù)長(zhǎng)度
// 提取出原始數(shù)據(jù)
bool delength(const string &package, string *text)
{
    // 找到第一個(gè)元素分隔符,棄掉前面的長(zhǎng)度
    auto pos = package.find(LINE_SEP);
    if (pos == string::npos)
        return false;

    // 確認(rèn)正文的長(zhǎng)度
    int len = stoi(package.substr(0, pos));

    // 從第一個(gè)分割符往后開始到記錄的字符串長(zhǎng)度就是原始的數(shù)據(jù)
    *text = package.substr(pos + LINE_SEP_LEN, len);

    return true;
}

// 請(qǐng)求
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)
    {
    }

    // 序列化過程
    // 因?yàn)橥ㄐ诺臄?shù)據(jù)一開始分為了好幾個(gè)獨(dú)立的元素
    // 所以將這些獨(dú)立的元素合并成一個(gè)數(shù)據(jù)
    bool serialize(string *out)
    {
        *out = "";
        *out += to_string(_x);
        *out += SEP;
        *out += _op;
        *out += SEP;
        *out += to_string(_y);
        return true;
    }

    // 反序列化過程
    // 將合并的一整個(gè)數(shù)據(jù)分解回原始的幾個(gè)獨(dú)立數(shù)據(jù)
    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;
        // 因?yàn)閷?duì)于計(jì)算器而言,計(jì)算符號(hào)只有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;
    }
};

// 響應(yīng)請(qǐng)求
class Response
{
public:
    int _exitcode; // 返回碼
    int _result;   // 返回結(jié)果

    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;
    }
};

// 讀取數(shù)據(jù)并且判斷是否是個(gè)完整的數(shù)據(jù)的方法
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)
        {
            // 找到報(bào)頭和正文之間的分隔符
            buffer[n] = 0;
            buff += buffer;
            auto pos = buff.find(LINE_SEP);
            if (pos == string::npos)
                continue;

            // 拿到正文的長(zhǎng)度
            int len = stoi(buff.substr(0, pos));
            // 判斷inbuff的長(zhǎng)度是否等于整個(gè)數(shù)據(jù)的長(zhǎng)度
            // 如果相等說明讀到了完成的數(shù)據(jù)
            int max_len = len + 2 * LINE_SEP_LEN + buff.substr(0, pos).size(); // 整個(gè)數(shù)據(jù)的長(zhǎng)度
            if (buff.size() < max_len)
                continue;
            cout << "目前拿到的所有報(bào)文:\n" << buff << endl;

            // 到這一步說明至少有一個(gè)完整的數(shù)據(jù)
            // 將整個(gè)完整的數(shù)據(jù)傳回指針
            *text = buff.substr(0, max_len);
            cout << "完整的報(bào)文:\n" << *text << endl;
            buff.erase(0, max_len);
            return true;
        }

        else
            return false;
    }

    return true;
}

Server.hpp

服務(wù)端就定義一個(gè)函數(shù)將整個(gè)的讀取、反序列化、計(jì)算、序列化、發(fā)送的過程全部編寫好,然后服務(wù)端啟動(dòng)加上一個(gè)函數(shù)參數(shù),也就是計(jì)算過程的函數(shù)。

#pragma once

#include "log.hpp"
#include "Protocol.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <sys/types.h>
#include <sys/wait.h>

typedef function<bool(const Request &req, Response &res)> func_t;

void HandlerEntery(int sock, func_t func)
{
    string buff;
    while (1)
    {
        // 讀取
        // 需要保證讀到的是一個(gè)完整的請(qǐng)求
        string req_text;
        if (!recvPackage(sock, buff, &req_text))
            return;
        cout << "帶報(bào)頭的請(qǐng)求: \n" << req_text << endl;

        // 將讀到的數(shù)據(jù)的頭部去掉,也就是數(shù)據(jù)長(zhǎng)度
        string req_str;
        if (!delength(req_text, &req_str))
            return;
        cout << "原始數(shù)據(jù): " << req_str << endl;

        // 對(duì)讀到的原始數(shù)據(jù)進(jìn)行反序列化
        Request req;
        if (!req.unserialize(req_str))
            return;

        // 將反序列化后的結(jié)果計(jì)算出來后
        // 將結(jié)果放到響應(yīng)類對(duì)象里
        // 通過響應(yīng)類對(duì)象提取到結(jié)果
        Response res;
        func(req, res);

        string res_str;
        // 得到響應(yīng)類對(duì)象序列化結(jié)果
        res.serialize(&res_str);
        cout << "計(jì)算完成,結(jié)果序列化:" << res_str << endl;

        // 再將得到的結(jié)果加上報(bào)頭
        // 也就是數(shù)據(jù)長(zhǎng)度確保數(shù)據(jù)的精確讀取
        // 得到最終的序列化數(shù)據(jù)
        res_str = enlength(res_str);
        cout << "構(gòu)建完整序列化數(shù)據(jù)完成:\n" << res_str << endl;

        // 將最終的數(shù)據(jù)發(fā)送回去
        send(sock, res_str.c_str(), res_str.size(), 0);
        cout << "服務(wù)端發(fā)送完成" << endl;
    }
}

class Server
{
public:
    Server(const uint16_t &port = 8000)
        : _port(port)
    {
    }

    void Init()
    {
        // 創(chuàng)建負(fù)責(zé)監(jiān)聽的套接字 面向字節(jié)流
        _listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenSock < 0)
        {
            LogMessage(FATAL, "create socket error!");
            exit(1);
        }
        LogMessage(NORMAL, "create socket %d success!", _listenSock);

        // 綁定網(wǎng)絡(luò)信息
        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!");

        // 設(shè)置socket為監(jiān)聽狀態(tài)
        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);
            // 創(chuàng)建通信的套接字
            // accept的返回值才是真正用于通信的套接字
            _sock = accept(_listenSock, (struct sockaddr *)&peer, &len);
            if (_sock < 0)
            {
                // 獲取通信的套接字失敗并不影響未來的操作,只是當(dāng)前的鏈接失敗而已
                LogMessage(ERROR, "accept socket error, next");
                continue;
            }
            LogMessage(NORMAL, "accept socket %d success", _sock);
            cout << "sock: " << _sock << endl;

            // 利用多進(jìn)程實(shí)現(xiàn)
            pid_t id = fork();
            if (id == 0) // child
            {
                close(_listenSock);
                // 調(diào)用方法包括讀取、反序列化、計(jì)算、序列化、發(fā)送
                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; // 負(fù)責(zé)監(jiān)聽的套接字
    int _sock;       // 通信的套接字
    uint16_t _port;  // 端口號(hào)
};

Server.cc

這個(gè)服務(wù)端的計(jì)算函數(shù)就通過結(jié)構(gòu)體的對(duì)象去作為參數(shù)完成,因?yàn)樾枰臄?shù)據(jù)都在結(jié)構(gòu)體里

#include "Server.hpp"
#include <memory>

// 輸出命令錯(cuò)誤函數(shù)
void Usage(string proc)
{
    cout << "Usage:\n\t" << proc << " local_ip local_port\n\n";
}

// 計(jì)算方式
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[])
{
    // 啟動(dòng)服務(wù)端不需要指定IP
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }

    uint16_t port = atoi(argv[1]);

    unique_ptr<Server> server(new Server(port));

    // 服務(wù)端初始化
    server->Init();
    //服務(wù)端啟動(dòng)
    server->start(cal);

    return 0;
}

Client.hpp

客戶端和服務(wù)端一樣也需要接收發(fā)送,不過客戶端是先發(fā)送再接收。并且上述提過因?yàn)榭蛻舻妮斎敕绞綗o法控制,所以要定義一個(gè)函數(shù)將客戶輸入的數(shù)據(jù)提取到兩個(gè)數(shù)和計(jì)算符號(hào)才能夠構(gòu)造請(qǐng)求的結(jié)構(gòu)體對(duì)象

    #pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#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()
    {
        // 創(chuàng)建套接字
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_sock < 0)
        {
            LogMessage(FATAL, "create socket error");
            exit(1);
        }

        // TCP的客戶端也不需要顯示綁定端口,讓操作系統(tǒng)隨機(jī)綁定
        // TCP的客戶端也不需要監(jiān)聽,因?yàn)椴]有去主動(dòng)鏈接客戶端,所以不需要accept
        // TCP的客戶端也不需要監(jiān)聽,因?yàn)椴]有去主動(dòng)鏈接客戶端,所以不需要accept
    }

    void start()
    {
        // 向服務(wù)端發(fā)起鏈接請(qǐng)求
        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");

        // 和服務(wù)端通信
        else
        {
            string line;
            string buffer;
            while (1)
            {
                cout << "Please cin: " << endl;
                getline(cin, line);

                Request req = ParseLine(line);
                string text;
                req.serialize(&text);
                cout << "序列化后的數(shù)據(jù):" << text << endl;
                string send_str = enlength(text);
                cout << "添加報(bào)頭后的數(shù)據(jù): \n" << send_str << endl;

                send(_sock, send_str.c_str(), send_str.size(), 0);

                // read
                // 拿到完整報(bào)文
                string package;
                if (!recvPackage(_sock, buffer, &package))
                    continue;
                cout << "拿到的完整報(bào)文: \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);
    }

    // 將客戶輸入的數(shù)據(jù)提取構(gòu)造請(qǐng)求結(jié)構(gòu)體對(duì)象
    Request ParseLine(const string &line)
    {
        auto it = line.begin();
        // 提取左邊的數(shù)字
        string left;
        while (it != line.end() && *it >= '0' && *it <= '9')
        {
            left += *it;
            ++it;
        }
        int leftnum = atoi(left.c_str());

        // 提取符號(hào)
        while (it != line.end() && *it != '+' && *it != '-' && *it != '+' && *it != '/')
            ++it;
        char op = *it;

        // 提取右邊數(shù)字
        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 <memory>

// 輸出命令錯(cuò)誤函數(shù)
void Usage(string proc)
{
    cout << "Usage:\n\t" << proc << " local_ip local_port\n\n";
}

int main(int argc, char *argv[])
{
    // 再運(yùn)行客戶端時(shí),輸入的指令需要包括主機(jī)ip和端口號(hào)
    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

這里還加了一個(gè)記錄日志的方法,可加可不加

#pragma once

#include <iostream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>

using namespace std;

#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4

const 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];
    // 可變參數(shù)
    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);
    }
}

通過結(jié)果再次理解通信過程

協(xié)議,序列化,反序列化,Json,網(wǎng)絡(luò),json,網(wǎng)絡(luò),服務(wù)器

所以最終的流程可以分解為幾個(gè)步驟:

協(xié)議,序列化,反序列化,Json,網(wǎng)絡(luò),json,網(wǎng)絡(luò),服務(wù)器

Json

上面的序列化和反序列化過程呢都是自己定義的,所以看起來并不好看,而且可讀性也不美觀。

其實(shí)也會(huì)第三方庫是幫我們做好了序列化和反序列化工作的,例如 Json,protobuf。因?yàn)镴son的使用比較簡(jiǎn)單,所以這里就使用Json

首先需要安裝第三方的 Jsoncpp的庫

yum install jsoncpp-devel

安裝好之后就可以使用第三方庫了,需要注意因?yàn)槭堑谌綆旌途€程庫一樣,編譯的時(shí)候需要加上 -lJsoncpp的選項(xiàng)

肯定下面的代碼注釋就可以了解到Json的使用了,注:為了不修改上述的一些代碼,下面使用條件編譯,只看Json部分即可

// 請(qǐng)求
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)
    {
    }

    // 序列化過程
    // 因?yàn)橥ㄐ诺臄?shù)據(jù)一開始分為了好幾個(gè)獨(dú)立的元素
    // 所以將這些獨(dú)立的元素合并成一個(gè)數(shù)據(jù)
    bool serialize(string *out)
    {
#ifdef MYSELF
        *out = "";
        *out += to_string(_x);
        *out += SEP;
        *out += _op;
        *out += SEP;
        *out += to_string(_y);
#else
        // Value是萬能類型
        // 需要先定義出對(duì)象
        Json::Value root;
        // Json是kv結(jié)構(gòu)存儲(chǔ)的,所以需要定義k值標(biāo)識(shí)v值
        root["first"] = _x;
        root["second"] = _y;
        root["op"] = _op;

        // Json要寫入值給別的變量也需要先定義對(duì)象
        // 寫的對(duì)象類型可以有幾種,這里采用FastWriter
        Json::FastWriter w;
        // 調(diào)用write方法就可以寫入
        *out = w.write(root);
#endif
        return true;
    }

    // 反序列化過程
    // 將合并的一整個(gè)數(shù)據(jù)分解回原始的幾個(gè)獨(dú)立數(shù)據(jù)
    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;
        // 因?yàn)閷?duì)于計(jì)算器而言,計(jì)算符號(hào)只有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
        // 同樣的需要先定義對(duì)象
        // 讀的對(duì)象也需要定義
        Json::Value root;
        Json::Reader reader;
        // 調(diào)用讀方法,將root的值讀到in中
        reader.parse(in, root);

        // asInt表示切換為整形類型
        // 通過k值就可以得到v值
        _x = root["first"].asInt();
        _y = root["second"].asInt();
        _op = root["op"].asInt();
#endif
        return true;
    }
};

// 響應(yīng)請(qǐng)求
class Response
{
public:
    int _exitcode; // 返回碼
    int _result;   // 返回結(jié)果

    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;
    }
};

只需要更改序列化反序列化過程即可,外面的協(xié)定不需要改變

效果

協(xié)議,序列化,反序列化,Json,網(wǎng)絡(luò),json,網(wǎng)絡(luò),服務(wù)器

使用 Json序列化的就很美觀文章來源地址http://www.zghlxwxcb.cn/news/detail-636117.html

到了這里,關(guān)于協(xié)議,序列化,反序列化,Json的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 序列化協(xié)議:JSON和XML

    作者:CARROT 鏈接:https://www.zhihu.com/question/604811576/answer/3100483698 來源:知乎 著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。 ? json和xml都是數(shù)據(jù)傳輸?shù)母袷?。比如我們開發(fā)過程中需要和網(wǎng)頁交換數(shù)據(jù),我們既可以使用json格式也可以使用xml格式。再

    2024年02月11日
    瀏覽(19)
  • 【Linux后端服務(wù)器開發(fā)】協(xié)議定制(序列化與反序列化)

    【Linux后端服務(wù)器開發(fā)】協(xié)議定制(序列化與反序列化)

    目錄 一、應(yīng)用層協(xié)議概述 二、序列化與反序列化 Protocal.h頭文件 Server.h頭文件 Client.h頭文件 server.cpp源文件 client.cpp源文件 什么是應(yīng)用層 ?我們通過編寫程序解決一個(gè)個(gè)實(shí)際問題、滿足我們?nèi)粘P枨蟮木W(wǎng)絡(luò)程序,都是應(yīng)用層程序。 協(xié)議是一種“約定”,socket的api接口,在讀

    2024年02月16日
    瀏覽(21)
  • Go語言網(wǎng)絡(luò)編程入門:TCP、HTTP、JSON序列化、Gin、WebSocket、RPC、gRPC示例

    Go語言網(wǎng)絡(luò)編程入門:TCP、HTTP、JSON序列化、Gin、WebSocket、RPC、gRPC示例

    在本文中,我們將介紹Go語言中的網(wǎng)絡(luò)編程的不同方式,包括TCP、HTTP、Gin框架、WebSocket、RPC、gRPC的介紹與連接實(shí)例,并對(duì)所有示例代碼都給出了詳細(xì)的注釋,最后對(duì)每種模式進(jìn)行了總結(jié)。 TCP(傳輸控制協(xié)議)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,提供

    2024年02月16日
    瀏覽(28)
  • iOS處理json,序列化和反序列化

    Mantle 是一個(gè)開源的 Objective-C 框架,用于在 iOS 和 macOS 應(yīng)用程序中實(shí)現(xiàn)模型層的序列化和反序列化。它提供了一種簡(jiǎn)單而強(qiáng)大的方式來將 JSON數(shù)據(jù)格式轉(zhuǎn)換為自定義的數(shù)據(jù)模型對(duì)象,以及將數(shù)據(jù)模型對(duì)象轉(zhuǎn)換為字典或 JSON 格式。 Mantle具有如下特點(diǎn) 自動(dòng)映射 Mantle自動(dòng)將 JSON 數(shù)據(jù)

    2024年02月11日
    瀏覽(24)
  • c#示例-json序列化和json樹

    c#示例-json序列化和json樹

    由于指針和引用類型的存在,在運(yùn)行中的程序中,數(shù)據(jù)不一定是整塊的。 可能東一塊西一塊散落在內(nèi)存的各個(gè)地方。 序列,是指連續(xù)且有序的一個(gè)整體。序列化就是把數(shù)據(jù)變?yōu)檫B續(xù)有序整體的過程。 經(jīng)過這樣處理后的數(shù)據(jù)就可以方便的進(jìn)行傳輸和儲(chǔ)存了。 json是一種文本數(shù)

    2024年02月16日
    瀏覽(36)
  • rust學(xué)習(xí)-json的序列化和反序列化

    由于 serde 庫默認(rèn)使用 JSON 格式進(jìn)行序列化和反序列化 因此程序?qū)⑹褂?JSON 格式對(duì)數(shù)據(jù)進(jìn)行序列化和反序列化 JSON:廣泛使用的 JavaScript 對(duì)象符號(hào),用于許多 HTTP API Postcard:no_std 和嵌入式系統(tǒng)友好的緊湊二進(jìn)制格式。 CBOR:用于小消息大小且無需版本協(xié)商的簡(jiǎn)潔二進(jìn)制對(duì)象表示

    2024年02月12日
    瀏覽(19)
  • JSON序列化與反序列化NULL值丟失問題

    做項(xiàng)目一般都會(huì)有一些特殊的需求,例如保留json中的null值,但是fastjson都會(huì)把null值得屬性給過濾掉 json序列化保留null值 json反序列化保留null值 使用hutool的Json工具時(shí)

    2024年02月15日
    瀏覽(37)
  • 使用nlohmann json庫進(jìn)行序列化與反序列化

    nlohmann源碼倉庫:https://github.com/nlohmann/json 使用方式:將其nlohmann文件夾加入,包含其頭文件json.hpp即可 demo

    2024年02月10日
    瀏覽(21)
  • delphi JSON序列化(五)

    關(guān)于TJSONConverters的使用 結(jié)果: {\\\"value\\\":\\\"haha\\\",\\\"createTime\\\":\\\"2024-01-10T17:15:33.588Z\\\"} 注:REST. XXX單元的序列化類感覺不好用, 想把TObjectListTPerson序列化沒有好的切處點(diǎn)。

    2024年02月03日
    瀏覽(31)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包