网络 :序列和反序列化

网络 :序列和反序列化

  • [(一)序列和反序列 概念](#(一)序列和反序列 概念)
  • (二)实例
    • [1. 封装socket 接口](#1. 封装socket 接口)
    • [2. 制定协议(用于实现序列和反序列化)](#2. 制定协议(用于实现序列和反序列化))
    • [3. 计算(实现计算器功能)](#3. 计算(实现计算器功能))
    • [4. 服务器(将上面所有的类功能调用起来)](#4. 服务器(将上面所有的类功能调用起来))
    • [5. 服务端](#5. 服务端)
    • 6.客户端
  • [(三) Json](#(三) Json)
  • 四) OSI7层模型 OSI7层模型)

(一)序列和反序列 概念

序列化:

  • 序列化是指将对象的状态信息转换为可以存储或传输的形式的过程 。这种形式通常是字节流或其他结构化数据格式,便于在网络中传递或保存到持久化介质(如磁盘文件)。通过序列化,对象可以在不同的环境中被安全地传递或长期保存。
    在实际应用中,序列化的核心目标是确保对象的完整性和可传递性。这意味着无论是在同一台机器上的不同进程之间还是跨网络的不同设备之间,都可以无损地传递对象的数据状态

反序列化:

  • 反序列化则是序列化的逆操作,即将已序列化的数据重新还原为原始的对象实例。这一过程涉及解析字节流并依据其中保存的对象状态及描述信息重建对象。反序列化使得程序能够恢复先前保存的对象状态,从而继续执行后续逻辑或完成特定任务。

序列化和反序列化就是用户规定的一种约定 ,他是用户层上的 "约定" ,这个约定由程序员自己来规定,而通信双方都要遵守这种协议。

(二)实例

下面我们通过实现一个简易的 通信计算器 来更好的理解序列和反序列化。

1. 封装socket 接口

这里的通信时基于TCP协议。封装socket接口方便客户端和使用端更好的使用。

Sock.hpp头文件封装套接字接口 , Log.hpp是一个日志系统。

c 复制代码
#pragma Once
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#include "Log.hpp"

Log lg;

enum
{
    SOCK_ERR = 1,
    BIND_ERR,
    LISTEN_ERR,
    S
};

class Sock
{
public:
    void Socket()
    {
        socketfd = socket(AF_INET, SOCK_STREAM, 0);
        if (socketfd < 0)
        {
            lg(Fatal, "socket errno : %d ,%s", errno, strerror(errno));
            exit(SOCK_ERR);
        }
    }

    void Bind(uint16_t &port)
    {
        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(socketfd, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind errno : %d ,%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
    }

    void Listen()
    {
        int n = listen(socketfd, 10);
        if (n < 0)
        {
            lg(Fatal, "listen errno : %d ,%s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
    }
				//输出型参数                    输出型参数
    int Accept(std::string *clientip, uint16_t *clientport)
    {
        struct sockaddr_in remote;
        socklen_t len = sizeof(remote);

        int newfd = accept(socketfd, (struct sockaddr *)&remote, &len);
        if (newfd < 0)
        {
            lg(Warning, "accept errno : %d ,%s", errno, strerror(errno));
            return -1;
        }

        char buffip[64];
        inet_ntop(AF_INET, &remote.sin_addr, buffip, sizeof(buffip));
        *clientip = buffip;
        *clientport = ntohs(remote.sin_port);

        return newfd;
    }

    bool Connect(std::string &serverip, uint16_t &serverport)
    {
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(serverport);
        inet_pton(AF_INET, serverip.c_str(), &server.sin_addr);
        int n = connect(socketfd, (const struct sockaddr *)&server, sizeof(server));
        if (n < 0)
        {
            lg(Warning, "connect errno : %d ,%s", errno, strerror(errno));
            return false;
        }

        return true;
    }

    int Getfd()
    {
        return socketfd;
    }

    void Close()
    {
        close(socketfd);
    }

private:
    int socketfd;
};

Log.hpp 日志系统代码如下:

c 复制代码
#pragma once
#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// PrintMethod
#define Screen 1
#define Onefile 2
#define Muchfile 3

// leve,指的是日志等级,等级不同处理的方式也不同
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define LogFile "log.txt"
class Log
{
public:
    Log()
    {
        path = "./log/";
        _PrintMethod = Screen;
    }

    // 用户指定打印方式
    void AppontPrint(int PrintMethod)
    {
        _PrintMethod = PrintMethod;
    }

    std::string levelToString(int level)
    {
        switch (level)
        {
        case Info:
            return "Info";
        case Debug:
            return "Debug";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "None";
        }
    }

    void printLog(int level, const std::string &logtxt)
    {
        switch (_PrintMethod)
        {
        case Screen:
            std::cout << logtxt << std::endl;
            break;
        case Onefile:
            printOneFile(LogFile, logtxt);
            break;
        case Muchfile:
            printClassFile(level, logtxt);
            break;
        default:
            break;
        }
    }

    void printOneFile(const std::string &logname, const std::string &logtxt)
    {
        std::string filename = path + logname;
        int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
        if(fd < 0)
        {
            return;
        }

        int n = write(fd,logtxt.c_str(),logtxt.size());
        close(fd);
    }

    void printClassFile(int level, const std::string &logtxt)
    {
        std::string filename = LogFile;
        filename += ".";
        filename += levelToString(level);   // "logtxt.Info/Fatal"

        printOneFile(filename,logtxt);
    }

    void operator()(int level, const char *format, ...)
    {
        // 自定义部分
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        char leftbuffer[1024];
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

        // 默认添加部分
        va_list s;
        va_start(s, format);
        char rightbuffer[1024];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
        va_end(s);

        char logtxt[2024];
        snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);

        printLog(level, logtxt);
    }

    ~Log()
    {

    }

private:
    std::string path; // 将路径信息打印到某个路径文件下
    int _PrintMethod; // 打印的方法(打印到屏幕或文件或多个文件等)
};

2. 制定协议(用于实现序列和反序列化)

Request类实现的是将 运算的数据 和 运算符 进行序列和反序化。他有三个成员(x_,y_表示运算数据,op_表示运算符)。

c 复制代码
const std::string blank_sep = " ";   //用于分割 x op y
class Request
{
public:
    Request(int x, int y, char op)
        : x_(x), y_(y), op_(op)
    {}

    Request()
    {}

    bool Serialize(std::string *out) // x op y , 将对象成员构造成 字符串
    {
        std::string s = std::to_string(x_);
        s += blank_sep;
        s += op_;
        s += blank_sep;
        s += std::to_string(y_);

        *out = s;
        return true;
    }

    bool Deserialize(std::string &in) // x op y  , 反序列将 字符串 构造成对象
    {
        // 找到 x
        size_t left_blank = in.find(blank_sep);
        if (left_blank == std::string::npos)
            return false;
        std::string x = in.substr(0, left_blank);

        // 找到y
        size_t right_blank = in.rfind(blank_sep);
        if (right_blank == std::string::npos)
            return false;
        std::string y = in.substr(right_blank + 1);

        if (left_blank + 2 != right_blank) // 左空格和右空格差两个字符
            return false;

        op_ = in[left_blank + 1];
        x_ = std::stoi(x);
        y_ = std::stoi(y);

        return true;
    }

    void Print()
    {
        std::cout << x_ << " " << op_ << " " << y_ << "=?? "<<std::endl;
    }
public:
    int x_;
    int y_;
    char op_; // + - * /
};

Response类是将 计算后的结构 和 错误码 进行序列和反序列化 。该类有两个成员(result_表示计算的结果, exitcode_表示错误码用于判断结果的合理性)。

c 复制代码
const std::string blank_sep = " ";   //用于分割 "result exitcode"
class Response
{
public:
    Response(int result, int exitcode)
        : result_(result), exitcode_(exitcode)
    {
    }

    Response()
    {}
    bool Serialize(std::string *out) // result exitcode , 将对象成员构造成 字符串
    {
        std::string s = std::to_string(result_);
        s += blank_sep;
        s += std::to_string(exitcode_);

        *out = s;
        return true;
    }

    bool Deserialize(std::string &in) // result exitcode , 反序列将 字符串 构造成对象
    {
        // 找到 result
        size_t blank = in.find(blank_sep);
        if (blank == std::string::npos)
            return false;
        std::string result = in.substr(0, blank);

        // 找到 exitcode
        std::string exitcode = in.substr(blank + 1);

        result_ = std::stoi(result);
        exitcode_ = std::stoi(exitcode);

        return true;
    }

public:
    int result_;
    int exitcode_;
};

如果只是将 数据以"x op y"或者 "result exitcode"的形式进行数据传输 不好拿出传输的数据,因为你不知道传输的数据是否完整。所以我再给这个有效数据加上 一个包头。

c 复制代码
const std::string protocol_sep = "\n";  //用于分割 "len\n"x op y\n

// "len"\n"x op y"\n  或者 "len"\n"result exitcode"\n ,  len表示 "x op y" 或者 "result exitcode" 的长度
std::string Encode(std::string &in)
{
    std::string s = std::to_string(in.size());
    s += protocol_sep;
    s += in;
    s += protocol_sep;

    return s;
}

bool Decode(std::string &package, std::string *out) //"len"\n"x op y"\n
{
    size_t left_line = package.find(protocol_sep);
    if (left_line == std::string::npos)
        return false;

    std::string len_str = package.substr(0, left_line);
    size_t len = std::stoi(len_str);
    size_t total_size = len_str.size() + len + 2; // 一个有效数据的大小
    if (package.size() < total_size)              // package的大小 一定是大于或等于total_size 的大小的
        return false;

    std::string content = package.substr(left_line + 1, left_line + len);  // x op y
    *out = content;
    package.erase(0,total_size); // 移除已经确定的报文
    return true;
}

Encode()函数给有效数据 加上 len\n"有效数据"\n ,其中len表示有效数据的大小。Decode()函数则是 取出传输数据中的有效数据,同时会删除传输数据里已经确定的有效数据(即报文)。

总代码如下:

Protocol.hpp头文件

c 复制代码
#pragma once
#include <iostream>

const std::string blank_sep = " ";   //用于分割 x op y
const std::string protocol_sep = "\n";  //用于分割 "len\n"x op y\n

// "len"\n"x op y"\n  或者 "len"\n"result exitcode"\n ,  len表示 "x op y" 或者 "result exitcode" 的长度
std::string Encode(std::string &in)
{
    std::string s = std::to_string(in.size());
    s += protocol_sep;
    s += in;
    s += protocol_sep;

    return s;
}

bool Decode(std::string &package, std::string *out) //"len"\n"x op y"\n
{
    size_t left_line = package.find(protocol_sep);
    if (left_line == std::string::npos)
        return false;

    std::string len_str = package.substr(0, left_line);
    size_t len = std::stoi(len_str);
    size_t total_size = len_str.size() + len + 2; // 一个有效数据的大小
    if (package.size() < total_size)              // package的大小 一定是大于或等于total_size 的大小的
        return false;

    std::string content = package.substr(left_line + 1, left_line + len);  // x op y
    *out = content;
    package.erase(0,total_size); // 移除已经确定的报文
    return true;
}
class Request
{
public:
    Request(int x, int y, char op)
        : x_(x), y_(y), op_(op)
    {}

    Request()
    {}

    bool Serialize(std::string *out) // x op y , 将对象成员构造成 字符串
    {
        std::string s = std::to_string(x_);
        s += blank_sep;
        s += op_;
        s += blank_sep;
        s += std::to_string(y_);

        *out = s;
        return true;
    }

    bool Deserialize(std::string &in) // x op y  , 反序列将 字符串 构造成对象
    {
        // 找到 x
        size_t left_blank = in.find(blank_sep);
        if (left_blank == std::string::npos)
            return false;
        std::string x = in.substr(0, left_blank);

        // 找到y
        size_t right_blank = in.rfind(blank_sep);
        if (right_blank == std::string::npos)
            return false;
        std::string y = in.substr(right_blank + 1);

        if (left_blank + 2 != right_blank) // 左空格和右空格差两个字符
            return false;

        op_ = in[left_blank + 1];
        x_ = std::stoi(x);
        y_ = std::stoi(y);

        return true;
    }

    void Print()
    {
        std::cout << x_ << " " << op_ << " " << y_ << std::endl;
    }
public:
    int x_;
    int y_;
    char op_; // + - * /
};

class Response
{
public:
    Response(int result, int exitcode)
        : result_(result), exitcode_(exitcode)
    {
    }

    Response()
    {}
    bool Serialize(std::string *out) // result exitcode , 将对象成员构造成 字符串
    {
        std::string s = std::to_string(result_);
        s += blank_sep;
        s += std::to_string(exitcode_);

        *out = s;
        return true;
    }

    bool Deserialize(std::string &in) // result exitcode , 反序列将 字符串 构造成对象
    {
        // 找到 result
        size_t blank = in.find(blank_sep);
        if (blank == std::string::npos)
            return false;
        std::string result = in.substr(0, blank);

        // 找到 exitcode
        std::string exitcode = in.substr(blank + 1);

        result_ = std::stoi(result);
        exitcode_ = std::stoi(exitcode);

        return true;
    }

public:
    int result_;
    int exitcode_;
};

3. 计算(实现计算器功能)

ServerCal.hpp头文件

ServerCal类实现的是计算器的功能 ,服务器得到客户端传输过来的数据后,通过调用该类的函数对数据计算处理 ,并将结果返回给客户端。

c 复制代码
#pragma Once
#include <iostream>
#include "Protocol.hpp"

enum
{
    Div_Zero = 1,
    Mod_Zero,
    Other_Oper
};

class ServerCal
{
public:
    Response Headler(Request &req)
    {
        Response res(0, 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_ = Div_Zero;
            else
                res.result_ = req.x_ / req.y_;
        }
        break;
        case '%':
        {
            if (req.y_ == 0)
                res.exitcode_ = Mod_Zero;
            else
                res.result_ = req.x_ % req.y_;
        }
        break;
        default:
            res.exitcode_ = Other_Oper;
            break;
        }

        return res;
    }
    std::string Calculator(std::string &package) // 将报文处理成我们想要的结构
    {
        std::string content;
        bool n = Decode(package, &content);
        if (!n)
            return ""; // Decode 出现问题返回一个空字符串

        Request req;
        req.Deserialize(content); // 将 x op y 反序列化成 对象。

        Response res = Headler(req); // 处理 request 对象,将 数据转换成 response对象

        content = "";
        res.Serialize(&content);   // 将 response 序列化成字符串
        content = Encode(content); // "len\n"result exitcode

        return content;
    }
};

服务端得到客户端传过来的报文后,调用Calculator()函数,将 计算双方 转换成 结果返回给服务器 。

4. 服务器(将上面所有的类功能调用起来)

TcpServer.hpp头文件

TcpServer类中有三个成员函数,其中 Sock listenfd_表示监听文件描述符(注意它是Sock类型),port_表示服务器端口号 , callback_表示回调函数 (该函数是ServerCal中的Calculator函数,由服务端传递,这样做可以有效解耦)。

c 复制代码
#pragma Once
#include <unistd.h>
#include <signal.h>
#include <functional>
#include "Socket.hpp"
#include "Log.hpp"

extern Log lg;

using func_t = std::function<std::string(std::string &package)>; // 回调函数
                                                                 // 传 ServerCal.hpp类中的 Calulator函数

class TcpServer
{
public:
    TcpServer(uint16_t port, func_t callback)
        : port_(port), callback_(callback)
    {
    }

    // 服务器初始化 TCP协议
    void InitServer()
    {
        listenfd_.Socket();
        listenfd_.Bind(port_);
        listenfd_.Listen();
        lg(Info, " InitServer done errno:%d ,%s", errno, strerror(errno));
    }

    void Start()
    {
        signal(SIGCHLD, SIG_IGN);
        signal(SIGPIPE, SIG_IGN);
        while (true)
        {
            // 连接客户端
            std::string clientip;
            uint16_t clientport;
            int sockfd = listenfd_.Accept(&clientip, &clientport);
            if (sockfd < 0)
            {
                lg(Warning, "accept errno:&d ,%s", errno, strerror(errno));
                continue;
            }
            lg(Info, "accept a new link, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip.c_str(), clientport);

            if (fork() == 0) // 子进程来执行任务
            {
                listenfd_.Close(); // 子进程关闭 listenfd
                std::string inbuffer_stream;  //传输的数据存放处

                while (true)
                {
                    char inbuff[128];
                    ssize_t n = read(sockfd, inbuff, sizeof(inbuff));
                    if (n > 0)
                    {
                        inbuff[n] = 0;
                        inbuffer_stream += inbuff;
                        lg(Debug, "debug:\n%s", inbuffer_stream.c_str());

                        // 将读到的数据 写入到客户端中 , 直到缓存区的数据读取完。
                        while (true)
                        {
                            std::string info = callback_(inbuffer_stream); // "len\n"result exitcode
                            if (info.empty())
                                break;
                            lg(Debug, "debug, response:\n%s", info.c_str());
                            lg(Debug, "debug:\n%s", inbuffer_stream.c_str());
                            ssize_t w = write(sockfd, info.c_str(), info.size());
                        }
                    }
                    else if (n <= 0)
                        break;
                }

                exit(0); // 执行完任务的子进程退出
            }

            // 父进程 -----
            close(sockfd);
        }
    }

private:
    Sock listenfd_;
    uint16_t port_;
    func_t callback_; // 回调函数
};

提供创造子进程来执行任务。

5. 服务端

ServerCal.cc头文件

c 复制代码
#pragma Once
#include "ServerCal.hpp"
#include "TcpServer.hpp"

#include <iostream>
#include <sys/socket.h>
void Usage(std::string proc)
{
    std::cout << "Usage :" << proc << "  serverport" << std::endl;
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return -1;
    }

    uint16_t port = std::stoi(argv[1]);
    ServerCal cal;
    TcpServer* tcp = new TcpServer(port, std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));
    tcp->InitServer();

    tcp->Start();

    return 0;
}

6.客户端

Client.cc头文件

c 复制代码
#pragma Once
// #include <time.h>
#include <unistd.h>
#include "Socket.hpp"
#include "Protocol.hpp"

void Usage(std::string proc)
{
    std::cout << "Usage :" << proc << " serverip serverport" << std::endl;
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return -1;
    }
    uint16_t port = std::stoi(argv[2]);
    std::string ip = argv[1];
	
	//连接服务器
    Sock sockfd;
    sockfd.Socket();
    sockfd.Connect(ip, port);
	// ------
	
    const std::string opers = "+-*/%=-=&^"; //操作符的范围
    srand(time(nullptr));  
    int cnt = 5;
    std::string buff_stream;  //用于存放传输的数据
    while (cnt--)
    {
        std::cout << "------------- 测试开始---cnt: " << cnt << std::endl;
        int x = rand() % 100 + 1;
        int y = rand() % 100;
        usleep(66666);
        char op = opers[rand() % opers.size()];
		
		//发送数据给服务器
        std::string content;  
        Request req(x, y, op);
        req.Serialize(&content); // 序列化
        content = Encode(content);
        req.Print();
        ssize_t n = write(sockfd.Getfd(), content.c_str(), content.size());
        if (n < 0)
        {
            std::cerr << "write fail" << std::endl;
        }
        // std::cout << "这是最新的发出去的请求 "  << "\n" ;
        // write(sockfd.Getfd(), content.c_str(), content.size());
		
		// 读取数据
        char inbuff[1280];
        n = read(sockfd.Getfd(), inbuff, sizeof(inbuff));
        if (n > 0)
        {
            inbuff[n] = 0;
            buff_stream += inbuff;

            // std::cout << buff_stream << std::endl;
            std::string info;
            bool r = Decode(buff_stream, &info);
            if (!r)
            {
                break;
            }

            Response res;
            res.Deserialize(info);

            std::cout << res.result_ << "  " << res.exitcode_ << std::endl;
        }
        else
        {
            break;
        }
        std::cout << "----------------------------------------" << std::endl;
        sleep(1);
    }

    return 0;
}

运行效果图:

(三) Json

自己定义序列化和反序列化过于繁琐,我们可以调用其他的库来完成序列化和反序列化。

比如: Json、Protobuf 。

下面我们提供Json库来完成序列和反序列化。

1.使用例子

提供调用下面命令来现在Json库。

c 复制代码
yum install -y jsoncpp-devel

使用该库要引入<jsoncpp/json/json.h>头文件

实例一:

c 复制代码
#include<iostream>
#include<jsoncpp/json/json.h>
int main()
{
    Json::Value part1;
    part1["haha"] = "haha";
    part1["hehe"] = "hehe";


    Json::Value root;
    root["x"] = 100;
    root["y"] = 200;
    root["op"] = '+';
    root["desc"] = "this is a + oper";
    root["test"] = part1;

    Json::FastWriter w;
    //Json::StyleWriter w;
    std::string res = w.write(root);

    std::cout << res << std::endl;
    return 0;
}

实例二:

c 复制代码
int main()
{
    Json::Value part1;
    part1["haha"] = "haha";
    part1["hehe"] = "hehe";


    Json::Value root;
    root["x"] = 100;
    root["y"] = 200;
    root["op"] = '+';
    root["desc"] = "this is a + oper";
    root["test"] = part1;

    Json::FastWriter w;
    std::string res = w.write(root);
    
    sleep(3);

    Json::Value v;
    Json::Reader r;
    r.parse(res, v);

    int x = v["x"].asInt();
    int y = v["y"].asInt();
    char op = v["op"].asInt();
    std::string desc = v["desc"].asString();
    Json::Value temp = v["test"];
    std::cout << x << std::endl;
    std::cout << y << std::endl;
    std::cout << op << std::endl;
    std::cout << desc << std::endl;
    return 0;
}

2.使用json实现网络计算器的序列和反序列

makefile

c 复制代码
.PHONY:all
all:server client

Flag=#-DMySelf=1

server:ServerCal.cc
	g++ -o $@ $^ -std=c++11 -ljsoncpp $(Flag)
client:Client.cc
	g++ -o $@ $^ -std=c++11  -ljsoncpp $(Flag)

.PHONY:clean
clean:
	rm -f server client

protocol.hpp

c 复制代码
#pragma once
#include <iostream>
#include<jsoncpp/json/json.h>

//#define MySelf 1

const std::string blank_sep = " ";   //用于分割 x op y
const std::string protocol_sep = "\n";  //用于分割 "len\n"x op y\n

// "len"\n"x op y"\n  或者 "len"\n"result exitcode"\n ,  len表示 "x op y" 或者 "result exitcode" 的长度
std::string Encode(std::string &in)
{
    std::string s = std::to_string(in.size());
    s += protocol_sep;
    s += in;
    s += protocol_sep;

    return s;
}

bool Decode(std::string &package, std::string *out) //"len"\n"x op y"\n
{
    size_t left_line = package.find(protocol_sep);
    if (left_line == std::string::npos)
        return false;

    std::string len_str = package.substr(0, left_line);
    size_t len = std::stoi(len_str);
    size_t total_size = len_str.size() + len + 2; // 一个有效数据的大小
    if (package.size() < total_size)              // package的大小 一定是大于或等于total_size 的大小的
        return false;

    std::string content = package.substr(left_line + 1, left_line + len);  // x op y
    *out = content;
    package.erase(0,total_size); // 移除已经确定的报文
    return true;
}
class Request
{
public:
    Request(int x, int y, char op)
        : x_(x), y_(y), op_(op)
    {}

    Request()
    {}

    bool Serialize(std::string *out) // x op y , 将对象成员构造成 字符串
    {
#ifdef MySelf        
        std::string s = std::to_string(x_);
        s += blank_sep;
        s += op_;
        s += blank_sep;
        s += std::to_string(y_);

        *out = s;
#else
        Json::Value root;
        root["x"] = x_;
        root["y"] = y_;
        root["op"] = op_;
        Json::StyledWriter w;
        *out = w.write(root);

#endif
        return true;
    }

    bool Deserialize(std::string &in) // x op y  , 反序列将 字符串 构造成对象
    {
#ifdef MySelf        
        // 找到 x
        size_t left_blank = in.find(blank_sep);
        if (left_blank == std::string::npos)
            return false;
        std::string x = in.substr(0, left_blank);

        // 找到y
        size_t right_blank = in.rfind(blank_sep);
        if (right_blank == std::string::npos)
            return false;
        std::string y = in.substr(right_blank + 1);

        if (left_blank + 2 != right_blank) // 左空格和右空格差两个字符
            return false;

        op_ = in[left_blank + 1];
        x_ = std::stoi(x);
        y_ = std::stoi(y);
#else
        Json::Value root;
        Json::Reader r;
        r.parse(in,root);
        x_ = root["x"].asInt();
        y_ = root["y"].asInt();
        op_ = root["op"].asInt();

#endif
        return true;
    }

    void Print()
    {
        std::cout << x_ << " " << op_ << " " << y_ << "=?? "<<std::endl;
    }
public:
    int x_;
    int y_;
    char op_; // + - * /
};

class Response
{
public:
    Response(int result, int exitcode)
        : result_(result), exitcode_(exitcode)
    {
    }

    Response()
    {}
    bool Serialize(std::string *out) // result exitcode , 将对象成员构造成 字符串
    {
#ifdef MySelf                
        std::string s = std::to_string(result_);
        s += blank_sep;
        s += std::to_string(exitcode_);

        *out = s;
#else
        Json::Value root;
        root["result"] = result_;
        root["exit"] = exitcode_;
        Json::StyledWriter w;
        *out = w.write(root);

#endif
        return true;
    }

    bool Deserialize(std::string &in) // result exitcode , 反序列将 字符串 构造成对象
    {
#ifdef MySelf                
        // 找到 result
        size_t blank = in.find(blank_sep);
        if (blank == std::string::npos)
            return false;
        std::string result = in.substr(0, blank);

        // 找到 exitcode
        std::string exitcode = in.substr(blank + 1);

        result_ = std::stoi(result);
        exitcode_ = std::stoi(exitcode);
#else
        Json::Value root;
        Json::Reader r;
        r.parse(in,root);
        result_ = root["result"].asInt();
        exitcode_ = root["exit"].asInt();
        
#endif
        return true;
    }

public:
    int result_;
    int exitcode_;
};

运行效果:

四) OSI7层模型

我们计算功能(ServerCal.hpp)相当于应用层,制定的序列化和反序列化(Protocol.hpp)相当于表示层,TcpServer.hpp服务器相当于会话层。

这就是为什么上面这三层是由我们自己定义的统称为应用层。

相关推荐
漫谈网络1 小时前
JSON 数据格式详解
网络·python·json·数据格式
CaracalTiger6 小时前
HTTP 协议的基本概念(请求/响应流程、状态码、Header、方法)问题解决方案大全
开发语言·网络·python·深度学习·网络协议·http·pip
aigoushan6 小时前
零基础开始的网工之路第二十一天------性能优化
运维·服务器·网络
芊言芊语7 小时前
CAN2.0、DoIP、CAN-FD汽车协议详解与应用
运维·服务器·网络
杭州泽沃电子科技有限公司7 小时前
母线槽接头过热隐患难防?在线测温方案实时守护电力安全
网络·人工智能·安全
alonetown9 小时前
IEC 62351 第十一部分详情
网络·iec62351·iec62351-10
Tom Boom9 小时前
2. 如何理解互联网七层模型?深度解析。
网络·自动化测试·测试开发·测试用例·自动化测试框架开发
zzc9219 小时前
不同程度多径效应影响下的无线通信网络电磁信号仿真数据生成程序
网络·matlab·数据集·无线信道·无线通信网络拓扑推理·多径效应
1688red10 小时前
IPv4编址及IPv4路由基础
运维·网络·华为
果子⌂10 小时前
LVS+Keepalived高可用群集
网络·智能路由器·lvs