序列化与反序列化

再谈协议

协议是一种“约定”,socket api的接口,在读写数据时,都是按照“字符串”的方式来发送接收的。

如果我们要传输一些“结构化的数据”怎么办呢?

“多个字符串”  头像url,时间,昵称,消息。序列化成形成一个报文 -->“一个字符串”

通过网络将这个字符串发送给接收方,然后接收方再反序列化。解析成 “多个字符串”  头像url,时间,昵称,消息。

手动实现协议

给请求和响应添加格式


// 退出码+结果 ---->  长度+"\r\n"+退出码和结果+"\r\n"
//"exitcode result" -> "content_len"\r\n"exitcode result"\r\n
const string enLength(const string &text)
{
    string send_string = to_string(text.size());
    send_string += LINE_SEP;
    send_string += text;
    send_string += LINE_SEP;

    return send_string;
}

 给请求和响应去掉格式


// 该函数是通过 ##"content_len"\r\n"exitcode result"\r\n## 来提取出exitcode result
// "content_len"\r\n"exitcode result"\r\n
bool deLength(const string &package, string *text) //*text 是输出型参数
{
    auto pos = package.find(LINE_SEP);

    if (pos == string::npos)
        return false;

    string text_len_string = package.substr(0, pos);
    int text_len = stoi(text_len_string);

    *text = package.substr(pos + LINE_SEP_LEN, text_len);

    return true;
}


操作信息的序列化和反序列化


#define SEP " "
#define SEP_LEN strlen(SEP)
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)



// 操作信息
class Request
{
public:
    // 普通的构造函数
    Request() : x(0), y(0), op(0)
    {
    }
    // 带参的构造函数
    Request(int x_, int y_, char op_) : x(x_), y(y_), op(op_)
    {
    }
    // 1. 自己写
    // 序列化 -- 说白了就是将我们的1 + 1 全部转换成字符然后构成一个字符串
    bool serialize(string *out) // *out是一个输出型参数
    {
#ifdef MYSELF
        *out = "";
        // 结构化 -> "x op y";
        string x_string = to_string(x);
        string y_string = to_string(y);

        *out = x_string;  // "x"
        *out += SEP;      // "x "
        *out += op;       // "x +"
        *out += SEP;      // "x + "
        *out += y_string; // "x + y"
#else
        // 2. 用现成的  -- json
        Json::Value root;

        root["first"] = x;
        root["second"] = y;
        root["oper"] = op;

        Json::FastWriter writer;

        *out = writer.write(root);
#endif
        return true;
    }
    // "x op y\r\n"
    bool deserialize(const string &in)
    {
#ifdef MYSELF
        // 从前往后找空格分隔符
        auto left = in.find(SEP);
        // 从后往前找空格分隔符
        auto right = in.rfind(SEP);

        // 只要有其中一个没找到就返回false
        if (left == string::npos || right == string::npos)
            return false;
        // left == right 说明从前往后找和从后往前找都是一个空格分隔符 因此返回false
        if (left == right)
            return false;
        // 这个计算的其实是我们运算符的大小
        // 如果不为1说明我们的运算符是有错误的因此应该返回false
        if (right - (left + SEP_LEN) != 1)
            return false;

        // "x + y"
        // 走到这一步说明我们传入的是一个格式正确的字符串
        // 分割出来左操作数
        string x_string = in.substr(0, left);
        // 分割出来右操作数
        string y_string = in.substr(right + SEP_LEN);

        // 如果左右操作数为空 则返回false
        if (x_string.empty())
            return false;
        if (y_string.empty())
            return false;

        // 字符转整型取出左右操作数
        x = stoi(x_string);
        y = stoi(y_string);
        // left+SEP_LEN就是操作符的下标
        op = in[left + SEP_LEN];
#else
        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root);

        x = root["first"].asInt();
        y = root["second"].asInt();
        op = root["oper"].asInt();
#endif
        return true;
    }

public:
    //"x op y"
    int x;
    int y;
    char op;
};

返回信息的序列化和反序列化 

#define SEP " "
#define SEP_LEN strlen(SEP)
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)



class Response
{
public:
    // 普通的构造函数
    Response() : exitcode(0), result(0)
    {
    }
    // 带参的构造函数
    Response(int exitcode_, int result_) : exitcode(exitcode_), result(result_)
    {
    }

    // 序列化操作
    bool serialize(string *out)
    {
#ifdef MYSELF

        *out = "";
        string ec_string = to_string(exitcode);
        string res_string = to_string(result);

        *out = ec_string;
        *out += SEP;
        *out += res_string;
#else
        Json::Value root;
        root["exitcode"] = exitcode;
        root["result"] = result;

        Json::FastWriter writer;
        *out = writer.write(root);
#endif
        return true;
    }
    // 反序列化操作
    bool deserialize(const string &in)
    {
#ifdef MYSELF

        // "exitcode result"
        // 找出分隔符的位置
        auto mid = in.find(SEP);
        // 如果找不到返回false

        if (mid == string::npos)
            return false;

        // 将exitcode 和 result分割出来
        string ec_string = in.substr(0, mid);
        string res_string = in.substr(mid + SEP_LEN);

        // 如果exitcode 和 result为空则返false
        if (ec_string.empty() || res_string.empty())
            return false;

        // 字符串转整型
        exitcode = stoi(ec_string);
        result = stoi(res_string);
#else
        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root);

        exitcode = root["exitcode"].asInt();
        result = root["result"].asInt();
#endif

        return true;
    }
    int exitcode; // 0: 计算成功  !0: 表示计算失败,具体是多少,定好标准
    int result;   // 计算结果
};

 计算机处理信息的过程

// req: 里面一定是我们的处理好的一个完整的请求对象
// resp: 根据req,进行业务处理,填充resp,不用管理任何读取和写入,序列化和反序列化等任何细节
bool cal(const Request &req, Response &resp)
{
    // req已经有结构化完成的数据了,你可以直接使用
    resp.exitcode = OK;
    resp.result = OK;

    switch (req.op)
    {
    case '+':
        resp.result = req.x + req.y;
        break;
    case '-':
        resp.result = req.x - req.y;

        break;
    case '*':
        resp.result = req.x * req.y;

        break;
    case '/':
    {
        if (req.y == 0)
            resp.exitcode = DIV_ZERO;
        else
            resp.result = req.x / req.y;
    }
    break;
    case '%':
    {
        if (req.y == 0)
            resp.exitcode = MOD_ZERO;
        else
            resp.result = req.x % req.y;
    }
    break;
    default:
        resp.result = OP_ERROR; 
        break;
    }

    return true;
}

 检查是否是一个完整的请求

// "content_len"\r\n"x op y"\r\n
bool recvPackage(int sock, string &inbuffer, string *text)
{
    char buffer[1024];
    // 死循环接收
    while (true)
    {
        ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            inbuffer += buffer;
            // 分析处理
            auto pos = inbuffer.find(LINE_SEP);
            // 如果没找到行分隔符直接continue只结束本次循环
            if (pos == string::npos)
                continue;

            // 将第一部分(第一个行分隔符前的内容)提取出来
            string text_len_string = inbuffer.substr(0, pos);
            // 转为整型
            int text_len = stoi(text_len_string);
            // text_len_string + "\r\n" + text + "\r\n" <= inbuffer.size(); 至少有一个完整的报文!!
            // 计算一个报文的完整长度是多少
            // 正文长度 + 两个行分隔符的长度 + 表示正文长度字符串的长度
            // "content_len"\r\n"x op y"\r\n
            int total_len = text_len_string.size() + 2 * LINE_SEP_LEN + text_len;
            cout << "处理前#inbuffer: " << endl;
            cout << "content_len\r\nexitcode result\r\n";
            cout << inbuffer;

            if (inbuffer.size() < total_len) // 如果该信息比一个报文的总长度都要小 那么肯定是不符合我们的要求的
            {
                cout << "你输入的消息, 没有严格遵守我们的协议, 正在等待后续的内容, continue" << endl;
                continue;
            }

            // 至少有一个完整的报文
            // 因此取出一个完整的报文,放到*text里面
            *text = inbuffer.substr(0, total_len);
            // 然后将这个报文从inbuffer里面给除去
            inbuffer.erase(0, total_len);

            cout << "处理后#inbuffer: ";
            cout << inbuffer << endl;
            // 取出完毕继续执行
            break;
        }
        else
            return false;
    }
    return true;
}

客户端的处理

将我们输入的信息解析成Request对象的

Request ParseLine(const string &line)
    {
        //"1+1" "123*456" "12/0"
        int status = 0; // 0:操作符之前 1: 碰到了操作符 2: 操作符之后
        int i = 0;
        int cnt = line.size();
        string left, right;
        char op;
        while (i < cnt)
        {
            switch (status)
            {
            case 0:
            {
                if (!isdigit(line[i])) // 不等于数字说明找到操作符了
                {
                    op = line[i];
                    status = 1; // 找到操作符就把status置为1
                }
                else
                    left.push_back(line[i++]); // 给左操作符赋值
            }
            break;
            case 1:
                i++;
                status = 2;
                break;
            case 2:
                right.push_back(line[i++]); // 给右操作符赋值
                break;
            }
        }
        cout << "左操作数 右操作数 操作符" << endl;
        cout << stoi(left) << " " << stoi(right) << " " << op << endl; // 打印一下左操作数 右操作数 操作符
        return Request(stoi(left), stoi(right), op);
    }

客户端 信息的发送与接受

void start()
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        server.sin_addr.s_addr = inet_addr(_serverip.c_str());

        int con = connect(_sock, (struct sockaddr *)&server, sizeof(server));
        if (con != 0)
        {
            cerr << "socket connect error" << endl;
        }
        else
        {
            string line;
            string inbuffer;
            while (true)
            {
                cout << "mycal>>> ";

                // 接收我们输入的操作
                getline(cin, line); // 1+1

                // 将我们输入的式子转化为Request类型的对象
                Request req = ParseLine(line); // 1+1
                string content;
                // 序列化: 将我们输入的式子转化为形如"x op y"形式的字符串
                req.serialize(&content); // content输出型参数
                
                // 加上正文长度信息和行分隔符
                // "x op y" ---> "content_len"\r\n"x op y"\r\n
                string send_string = enLength(content);

                // 将处理好的字符串发给服务端
                send(_sock, send_string.c_str(), send_string.size(), 0); 


                // 检查格式是否正确
                string package, text;
                // 检查是否有一个完整的报文
                if (!recvPackage(_sock, inbuffer, &package))
                    continue;
                else
                // 检查格式是否正确
                if (!deLength(package, &text))
                    continue;

                //"exitcode result"
                Response resp;
                resp.deserialize(text);

                cout << "exitcode: " << resp.exitcode << endl;
                cout << "result: " << resp.result << endl;
            }
        }
    }

Json协议

操作信息添加格式

Json::Value root;

root["first"] = x;
root["second"] = y;
root["oper"] = op;

Json::FastWriter writer;

*out = writer.write(root);

操作信息去掉格式

Json::Value root;
Json::Reader reader;
reader.parse(in, root);

x = root["first"].asInt();
y = root["second"].asInt();
op = root["oper"].asInt();

返回信息添加格式

Json::Value root;
root["exitcode"] = exitcode;
root["result"] = result;

Json::FastWriter writer;
*out = writer.write(root);

返回信息去掉格式

Json::Value root;
Json::Reader reader;
reader.parse(in, root);

exitcode = root["exitcode"].asInt();
result = root["result"].asInt();

完整代码

calServer.cc

#include "calServer.hpp"
#include 

using namespace server;
using namespace std;

static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}

// req: 里面一定是我们的处理好的一个完整的请求对象
// resp: 根据req,进行业务处理,填充resp,不用管理任何读取和写入,序列化和反序列化等任何细节
bool cal(const Request &req, Response &resp)
{
    // req已经有结构化完成的数据了,你可以直接使用
    resp.exitcode = OK;
    resp.result = OK;

    switch (req.op)
    {
    case '+':
        resp.result = req.x + req.y;
        break;
    case '-':
        resp.result = req.x - req.y;

        break;
    case '*':
        resp.result = req.x * req.y;

        break;
    case '/':
    {
        if (req.y == 0)
            resp.exitcode = DIV_ZERO;
        else
            resp.result = req.x / req.y;
    }
    break;
    case '%':
    {
        if (req.y == 0)
            resp.exitcode = MOD_ZERO;
        else
            resp.result = req.x % req.y;
    }
    break;
    default:
        resp.result = OP_ERROR; 
        break;
    }

    return true;
}

// tcp服务器,启动上和udp server一模一样
// ./tcpServer local_port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]); // 字符串转整型

    unique_ptr tsvr(new CalServer(port));

    tsvr->initServer();
    tsvr->start(cal);

    return 0;
}

calServer.hpp

#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "log.hpp"
#include "Protocol.hpp"

using namespace std;

namespace server
{

    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR
    };

    static const uint16_t gport = 8080;
    static const int gbacklog = 5;

    // const Request &req: 输入型
    // Response &resp: 输出型

    // 保证解耦
    typedef function func_t;

    void handlerEntery(int sock, func_t func)
    {
        string inbuffer;

        while (true)
        {
            // 1. 读取: "content_len"\r\n"x op y"\r\n
            // 1.1 你怎么保证你读到的消息是 【一个】完整的消息
            string req_text, req_str;

            // 1.2 我们保证,我们req_text里面一定是一个完整的请求: "content_len"\r\n"x op y"\r\n

            // 检查是否是一个完整的请求
            if (!recvPackage(sock, inbuffer, &req_text))
                return;
            cout << "带报头的请求: \n"
                 << req_text << endl;
            if (!deLength(req_text, &req_str))
                return;
            cout << "去掉报头的正文: \n"
                 << req_str << endl;

            // 2. 反序列化
            // 2.1 得到一个结构化的请求对象
            Request req;
            if (!req.deserialize(req_str))
                return;

            // 3. 计算机处理,req.x,req.op,req.y --- 业务逻辑
            // 3.1 得到一个结构化的响应
            Response resp;
            func(req, resp); // req的处理结果,全部放入到了resp,

            // 4. 对响应Response,进行序列化
            // 4.1 得到了一个字符串
            string resp_str;
            resp.serialize(&resp_str);

            cout << "计算完成, 序列化响应: " << resp_str << endl;

            // 5. 然后我们再发送响应
            // 5.1 构建成为一个完整的报文
            string send_string = enLength(resp_str);
            cout << "构建完成完整的响应\n" << send_string << endl;

            send(sock, send_string.c_str(), send_string.size(), 0); // 其实这里的发送是有问题的,后面再说
        }
    }

    class CalServer
    {
    public:
        CalServer(const uint16_t &port = gport) : _listensock(-1), _port(port)
        {
        }
        void initServer()
        {

            // 1. 创建socket文件套接字对象
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERR);
            }
            logMessage(NORMAL, "create socket success: %d", _listensock);


            

            // 2.bind绑定自己的网络信息
            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(BIND_ERR);
            }
            logMessage(NORMAL, "bind socket success");




            // 3. 设置socket 为监听状态
            if (listen(_listensock, gbacklog) < 0) // TODO
            {
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERR);
            }
            logMessage(NORMAL, "listen socket success");
        }

        void start(func_t func)
        {

            for (;;)
            {
                // 4. server 获取新链接
                // sock 和客户端进行通信的fd
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(_listensock, (struct sockaddr *)&peer, &len);

                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next");
                    cout << "sock: " << sock << endl;

                    continue;
                }
                logMessage(NORMAL, "accept a new link success,get new sock: %d", sock);

                // version 2
                pid_t id = fork();

                if (id == 0) // child
                {
                    close(_listensock); // 子进程中不需要监听因此关闭监听的文件描述符
                    handlerEntery(sock, func);
                    close(sock);
                    exit(0);
                }

                // father
                pid_t ret = waitpid(id, nullptr, 0);

                if (ret > 0)
                {
                    logMessage(NORMAL, "wait child success"); //?
                }
            }
        }

        ~CalServer()
        {
        }

    private:
        int _listensock; // 不是用来进行数据通信的,它是用来监听链接到来,获取新链接的!
        uint16_t _port;
    };
}

calClient.cc

#include "calClient.hpp"
#include 

using namespace std;

static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}

// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);

    unique_ptr tcli(new CalClient(serverip, serverport));

    tcli->initClient();
    tcli->start();

    return 0;
}

calClient.hpp

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "Protocol.hpp"

using namespace std;

#define NUM 1024

class CalClient
{
public:
    CalClient(const string &serverip, const uint16_t &serverport)
        : _sock(-1), _serverip(serverip), _serverport(serverport)
    {
    }
    void initClient()
    {
        // 1. 创建socket
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_sock < 0)
        {
            cerr << "socket create error" << endl;
        }
        // 2. tcp的客户端要不要bind? 要的 要不要显示的bind?
        // 不要! 这里尤其是client port要让OS自定随机自定!
        // 3. 要不要listen?不要!只有服务端需要!
        // 4. 要不要accept?不要!只有服务端需要!
        // 5. 要什么呢??要发起链接!
    }

    void start()
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        server.sin_addr.s_addr = inet_addr(_serverip.c_str());

        int con = connect(_sock, (struct sockaddr *)&server, sizeof(server));
        if (con != 0)
        {
            cerr << "socket connect error" << endl;
        }
        else
        {
            string line;
            string inbuffer;
            while (true)
            {
                cout << "mycal>>> ";

                // 接收我们输入的操作
                getline(cin, line); // 1+1

                // 将我们输入的式子转化为Request类型的对象
                Request req = ParseLine(line); // 1+1
                string content;
                // 序列化: 将我们输入的式子转化为形如"x op y"形式的字符串
                req.serialize(&content); // content输出型参数
                
                // 加上正文长度信息和行分隔符
                // "x op y" ---> "content_len"\r\n"x op y"\r\n
                string send_string = enLength(content);

                // 将处理好的字符串发给服务端
                send(_sock, send_string.c_str(), send_string.size(), 0); 


                // 检查格式是否正确
                string package, text;
                // 检查是否有一个完整的报文
                if (!recvPackage(_sock, inbuffer, &package))
                    continue;
                else
                // 检查格式是否正确
                if (!deLength(package, &text))
                    continue;

                //"exitcode result"
                Response resp;
                resp.deserialize(text);

                cout << "exitcode: " << resp.exitcode << endl;
                cout << "result: " << resp.result << endl;
            }
        }
    }

    // 将我们输入的信息解析成Request对象的
    Request ParseLine(const string &line)
    {
        //"1+1" "123*456" "12/0"
        int status = 0; // 0:操作符之前 1: 碰到了操作符 2: 操作符之后
        int i = 0;
        int cnt = line.size();
        string left, right;
        char op;
        while (i < cnt)
        {
            switch (status)
            {
            case 0:
            {
                if (!isdigit(line[i])) // 不等于数字说明找到操作符了
                {
                    op = line[i];
                    status = 1; // 找到操作符就把status置为1
                }
                else
                    left.push_back(line[i++]); // 给左操作符赋值
            }
            break;
            case 1:
                i++;
                status = 2;
                break;
            case 2:
                right.push_back(line[i++]); // 给右操作符赋值
                break;
            }
        }
        cout << "左操作数 右操作数 操作符" << endl;
        cout << stoi(left) << " " << stoi(right) << " " << op << endl; // 打印一下左操作数 右操作数 操作符
        return Request(stoi(left), stoi(right), op);
    }
    ~CalClient()
    {
        if (_sock >= 0)
        {
            close(_sock);
        }
    }

private:
    int _sock;
    string _serverip;
    uint16_t _serverport;
};

Protocol.hpp

#pragma once

#include 
#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)

enum
{
    OK = 0,
    DIV_ZERO,
    MOD_ZERO,
    OP_ERROR
};

// 退出码+结果 ---->  长度+"\r\n"+退出码和结果+"\r\n"
//"exitcode result" -> "content_len"\r\n"exitcode result"\r\n
const string enLength(const string &text)
{
    string send_string = to_string(text.size());
    send_string += LINE_SEP;
    send_string += text;
    send_string += LINE_SEP;

    return send_string;
}

// 该函数是通过 ##"content_len"\r\n"exitcode result"\r\n## 来提取出exitcode result
// "content_len"\r\n"exitcode result"\r\n
bool deLength(const string &package, string *text) //*text 是输出型参数
{
    auto pos = package.find(LINE_SEP);

    if (pos == string::npos)
        return false;

    string text_len_string = package.substr(0, pos);
    int text_len = stoi(text_len_string);

    *text = package.substr(pos + LINE_SEP_LEN, text_len);

    return true;
}

// 没有人规定我们网络通信的时候,只能有一种协议!!
// 我们怎么让系统知道我们用的是那一种协议
// "content_len"\r\n"协议编号"\r\n"x op y"\r\n

// 操作信息
class Request
{
public:
    // 普通的构造函数
    Request() : x(0), y(0), op(0)
    {
    }
    // 带参的构造函数
    Request(int x_, int y_, char op_) : x(x_), y(y_), op(op_)
    {
    }
    // 1. 自己写
    // 序列化 -- 说白了就是将我们的1 + 1 全部转换成字符然后构成一个字符串
    bool serialize(string *out) // *out是一个输出型参数
    {
#ifdef MYSELF
        *out = "";
        // 结构化 -> "x op y";
        string x_string = to_string(x);
        string y_string = to_string(y);

        *out = x_string;  // "x"
        *out += SEP;      // "x "
        *out += op;       // "x +"
        *out += SEP;      // "x + "
        *out += y_string; // "x + y"
#else
        // 2. 用现成的  -- json
        Json::Value root;

        root["first"] = x;
        root["second"] = y;
        root["oper"] = op;

        Json::FastWriter writer;

        *out = writer.write(root);
#endif
        return true;
    }
    // "x op y\r\n"
    bool deserialize(const string &in)
    {
#ifdef MYSELF
        // 从前往后找空格分隔符
        auto left = in.find(SEP);
        // 从后往前找空格分隔符
        auto right = in.rfind(SEP);

        // 只要有其中一个没找到就返回false
        if (left == string::npos || right == string::npos)
            return false;
        // left == right 说明从前往后找和从后往前找都是一个空格分隔符 因此返回false
        if (left == right)
            return false;
        // 这个计算的其实是我们运算符的大小
        // 如果不为1说明我们的运算符是有错误的因此应该返回false
        if (right - (left + SEP_LEN) != 1)
            return false;

        // "x + y"
        // 走到这一步说明我们传入的是一个格式正确的字符串
        // 分割出来左操作数
        string x_string = in.substr(0, left);
        // 分割出来右操作数
        string y_string = in.substr(right + SEP_LEN);

        // 如果左右操作数为空 则返回false
        if (x_string.empty())
            return false;
        if (y_string.empty())
            return false;

        // 字符转整型取出左右操作数
        x = stoi(x_string);
        y = stoi(y_string);
        // left+SEP_LEN就是操作符的下标
        op = in[left + SEP_LEN];
#else
        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root);

        x = root["first"].asInt();
        y = root["second"].asInt();
        op = root["oper"].asInt();
#endif
        return true;
    }

public:
    //"x op y"
    int x;
    int y;
    char op;
};

// 返回信息
class Response
{
public:
    // 普通的构造函数
    Response() : exitcode(0), result(0)
    {
    }
    // 带参的构造函数
    Response(int exitcode_, int result_) : exitcode(exitcode_), result(result_)
    {
    }

    // 序列化操作
    bool serialize(string *out)
    {
#ifdef MYSELF

        *out = "";
        string ec_string = to_string(exitcode);
        string res_string = to_string(result);

        *out = ec_string;
        *out += SEP;
        *out += res_string;
#else
        Json::Value root;
        root["exitcode"] = exitcode;
        root["result"] = result;

        Json::FastWriter writer;
        *out = writer.write(root);
#endif
        return true;
    }
    // 反序列化操作
    bool deserialize(const string &in)
    {
#ifdef MYSELF

        // "exitcode result"
        // 找出分隔符的位置
        auto mid = in.find(SEP);
        // 如果找不到返回false

        if (mid == string::npos)
            return false;

        // 将exitcode 和 result分割出来
        string ec_string = in.substr(0, mid);
        string res_string = in.substr(mid + SEP_LEN);

        // 如果exitcode 和 result为空则返false
        if (ec_string.empty() || res_string.empty())
            return false;

        // 字符串转整型
        exitcode = stoi(ec_string);
        result = stoi(res_string);
#else
        Json::Value root;
        Json::Reader reader;
        reader.parse(in, root);

        exitcode = root["exitcode"].asInt();
        result = root["result"].asInt();
#endif

        return true;
    }
    int exitcode; // 0: 计算成功  !0: 表示计算失败,具体是多少,定好标准
    int result;   // 计算结果
};

// "content_len"\r\n"x op y"\r\n
bool recvPackage(int sock, string &inbuffer, string *text)
{
    char buffer[1024];
    // 死循环接收
    while (true)
    {
        ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            inbuffer += buffer;
            // 分析处理
            auto pos = inbuffer.find(LINE_SEP);
            // 如果没找到行分隔符直接continue只结束本次循环
            if (pos == string::npos)
                continue;

            // 将第一部分(第一个行分隔符前的内容)提取出来
            string text_len_string = inbuffer.substr(0, pos);
            // 转为整型
            int text_len = stoi(text_len_string);
            // text_len_string + "\r\n" + text + "\r\n" <= inbuffer.size(); 至少有一个完整的报文!!
            // 计算一个报文的完整长度是多少
            // 正文长度 + 两个行分隔符的长度 + 表示正文长度字符串的长度
            // "content_len"\r\n"x op y"\r\n
            int total_len = text_len_string.size() + 2 * LINE_SEP_LEN + text_len;
            cout << "处理前#inbuffer: " << endl;
            cout << "content_len\r\nexitcode result\r\n";
            cout << inbuffer;

            if (inbuffer.size() < total_len) // 如果该信息比一个报文的总长度都要小 那么肯定是不符合我们的要求的
            {
                cout << "你输入的消息, 没有严格遵守我们的协议, 正在等待后续的内容, continue" << endl;
                continue;
            }

            // 至少有一个完整的报文
            // 因此取出一个完整的报文,放到*text里面
            *text = inbuffer.substr(0, total_len);
            // 然后将这个报文从inbuffer里面给除去
            inbuffer.erase(0, total_len);

            cout << "处理后#inbuffer: ";
            cout << inbuffer << endl;
            // 取出完毕继续执行
            break;
        }
        else
            return false;
    }
    return true;
}

makefile

cc=g++
LD=-DMYSELF
.PHONY:all
all:calclient calserver

calclient:calClient.cc
	$(cc) -o $@ $^ -std=c++11 -ljsoncpp ${LD}

calserver:calServer.cc
	$(cc) -o $@ $^ -std=c++11 -ljsoncpp ${LD}

.PHONY:clean
clean:
	rm -f calclient calserver
	

成果展示 

序列化与反序列化_第1张图片

 

你可能感兴趣的:(Linux网络编程,网络)