【Linux网络】打造初级网络计算器 - 从协议设计到服务实现-????️‍????一、protocol.hpp

时间:2025-04-27 06:59:30

该文件实现序列化与反序列使用到的类和相关函数(加报头解报头)!

1.1 Request类

1.1.1 基本结构

该类有三个成员变量,_x,_y,_oper(运算符号),序列化,反序列化,构造,析构及其他获取成员变量与打印的函数!

namespace protocol{
    class Request{
        public:
            Request(){}
            // 序列化 将结构化转成字符串
            bool Serialize(std::string& out);
            // 反序列化 将字符串转成结构化
            bool Deserialize(const std::string& in);
            void Print();
            int X();
            int Y();
            char Oper();
            ~Request(){}
        private:
            int _x;
            int _y;
            char _oper; // 计算符号
    };
}

1.1.2 构造、析构函数

为了方便后面的使用,此处实现两个构造函数,一个无参,一个带参函数,析构函数无需处理!

// 构造函数 - 无参
Request() {}
// 构造函数 - 有参
Request(int x, int y, char oper) : _x(x), _y(y), _oper(oper) {}
// 析构函数
~Request(){}

1.1.3 序列化函数

序列化即将结构化转成字符串,并将字符串以输出型参数输出

// 序列化 将结构化转成字符串
            bool Serialize(std::string* out){
                // 使用现成的库,xml,json,protobuf等
                // 这里使用json库
                Json::Value root;
                root["x"] = _x;
                root["y"] = _y;
                root["oper"] = _oper;
                Json::FastWriter writer;
                std::string s = writer.write(root);
                *out = s;
                return true;
            }

1.1.4 反序列化函数

反序列化即将字符串转成结构化,参数传入字符串!

// 反序列化 将字符串转成结构化
bool Deserialize(const std::string& in) {
    Json::Value root;
    Json::Reader reader;
    bool res = reader.parse(in, root);

    _x = root["x"].asInt();
    _y = root["y"].asInt();
    _oper = root["oper"].asInt();

    return true;
}

1.1.5 其他函数

void Print() {
    std::cout << _x << std::endl;
    std::cout << _y << std::endl;
    std::cout << _oper << std::endl;
}

int X() { return _x; }
int Y() { return _y; }
char Oper() { return _oper; }

void SetValue(int x, int y, char oper) {
    _x = x;
    _y = y;
    _oper = oper;
}

1.2、Response类

1.2.1 基本结构

这个类我们需要组织发送给客户端的响应,需要三个成员变量,_result(计算结果),_code(自定义错误码),_desc(错误码描述)

内部主要是 序列化(发送)反序列化(接受) 函数!

class Response {
public:
    Response() : _result(0), _code(0), _desc("success") {}
    bool Serialize(std::string* out);
    bool Deserialize(const std::string& in);
    ~Response() {}

public:
    int _result;
    int _code;         // 错误码 0 success, 1 fail, 2 fatal
    std::string _desc; // 错误码描述
};

1.2.2 构造析构函数

构造函数直接手动初始化(结果和错误码初始化为0,描述默认初始化为success)
析构函数无需处理!

Response() : _result(0), _code(0), _desc("success") {}
~Response() {}

1.2.3 序列化函数

这里和 request 类如出一辙,只需要构造相应的JSON结果,然后利用紧凑方法,构造出JSON风格的字符串就行了

bool Serialize(std::string* out) {
    Json::Value root;
    root["result"] = _result;
    root["code"] = _code;
    root["desc"] = _desc;
    Json::FastWriter writer;
    std::string s = writer.write(root);
    *out = s;
    return true;
}

1.2.4 反序列化函数

反序列化即将字符串转成结构化,参数传入字符串!

// 反序列化 - 将JSON字符串反序列化成成员变量
bool Deserialize(const std::string& in) {
    Json::Value root;
    Json::Reader reader;

    // parse 的作用是将 JSON 字符串解析成 Json::Value 对象
    bool res = reader.parse(in, root);

    _result = root["result"].asInt();
    _code = root["code"].asInt();
    _desc = root["desc"].asString();

    return true;
}

1.2.5 打印结果

将成员变量以字符串形式打印出来即可!

void PrintResult(){
    std::cout << "result: " << _result << ", code: " << _code 
    << ", desc: " << _desc << std::endl;
}

1.3 Factory类

  • 因为 Request类Response类 可能频繁创建,因此我们可以设计一个工厂类内部设计两个创建类的静态函数(没有this指针,外部直接调用函数即可)!
class Factory {
public:
    static std::shared_ptr<Request> BuildRequestDefault() {
        return std::make_shared<Request>();
    }
    static std::shared_ptr<Response> BuildResponseDefault() {
        return std::make_shared<Response>();
    }
};

1.4 报头

在实际的网络通信中,传的不仅仅是序列化后的字符串,还有报头信息,此处我们也设计一下报头信息

  1. "len"\r\n"{json}"\r\n – 完整的报文
  2. len 有效荷载长度
  3. \r\n(第一个):区分 len 和 json 串
  4. \r\n(第二个):暂时没用

1.4.1 添加报头

// 添加报头
std::string AddHeader(const std::string& jsonstr) {
    int len = jsonstr.size();
    std::string lenstr = std::to_string(len);
    return lenstr + sep + jsonstr + sep;
}

1.4.2 解析报头

注意:可能没有一个有效信息或者有多个有效信息!

// 解析报头
// 去掉前面的长度和分隔符与有效信息后面的分隔符
std::string ParseHeader(std::string& jsonstr) {
    // 分析
    auto pos = jsonstr.find(sep); // 在json风格字符串中找第一个分隔符
    if (pos == std::string::npos)
        return std::string(); // 找不到分隔符,返回空字符串

    // 获取 len
    std::string lenstr = jsonstr.substr(0, pos); // 取出长度字符串
    int len = std::stoi(lenstr);                 // 转成整数

    // 计算一个完整的报文应该是多长
    int totallen = lenstr.size() + len + 2 * sep.size();
    // 若传进来的字符串长度小于报文总长,说明没有一个完整的有效信息,返回空
    if (jsonstr.size() < totallen)
        return std::string();

    // 取出有效信息
    std::string validstr = jsonstr.substr(pos + sep.size(), len);
    // 去掉最后的分隔符
    jsonstr.erase(0, totallen);

    return validstr;
}