【项目】JsonRpc框架——开发实现1(细节功能、字段定义、抽象层、具象层)

文章目录

  • 一、一些常用的细节功能接口
    • [1. 日志功能](#1. 日志功能)
    • [2. Json序列化/反序列化方式](#2. Json序列化/反序列化方式)
    • [3. UUID的生成](#3. UUID的生成)
  • 二、字段信息定义
    • [1. 请求/响应报文的字段宏定义​](#1. 请求/响应报文的字段宏定义)
    • [2. 消息类型定义​](#2. 消息类型定义)
    • [3. 响应码类型定义​](#3. 响应码类型定义)
    • [4. RPC请求类型定义​](#4. RPC请求类型定义)
    • [5. 主题操作类型定义​](#5. 主题操作类型定义)
    • [6. 服务操作类型定义​](#6. 服务操作类型定义)
  • 三、抽象层的实现
  • 四、具象层的实现
    • [1. 不同的消息类型](#1. 不同的消息类型)
    • [2. 网络通信模块 + Protocol模块](#2. 网络通信模块 + Protocol模块)

项目代码:https://github.com/luoyu524/Json_Rpc

项目文档:https://blog.csdn.net/2402_86681376/category_13166697.html

一、一些常用的细节功能接口

1. 日志功能

意义:快速定位程序运行逻辑出错的位置。

项目在运行中可能会出现各种问题,关键的是要能找到问题,并解决问题。

解决问题的方式:

  • gdb调试:逐步调试过于繁琐,缓慢。主要用于程序崩溃后的定位。
  • 系统运行日志分析:在任何程序运行有可能逻辑错误的位置进行输出提示,快速定位逻辑问题的位置。
cpp 复制代码
// Logger.hpp

// 单例模式、策略模式
// 实现线程安全的日志对象

#pragma once
#include <cstddef>
#include <cstdio>
#include <ctime>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include<pthread.h>

class Mutex
{
public:
    Mutex()
    {
        pthread_mutex_init(&_lock, nullptr);
    }
    ~Mutex()
    {
        pthread_mutex_destroy(&_lock);
    }
    void Lock()
    {
        pthread_mutex_lock(&_lock);
    }
    void Unlock()
    {
        pthread_mutex_unlock(&_lock);
    }
    pthread_mutex_t* Ptr()
    {
        return &_lock;
    }
private:
    pthread_mutex_t _lock;
};

// RAII风格用法
class LockGuard
{
public:
    LockGuard(Mutex& lock)
        :_lockref(lock)
    {
        _lockref.Lock();
    }
    ~LockGuard()
    {
        _lockref.Unlock();
    }
private:
    Mutex& _lockref;
};

/*
后续使用我们自己封装的锁时,可以写成:
{
    LockGuard lock(mutex);
    // ...
    // 临界区
}
{}划定作用域, 利用LockGuard自动调用构造析构完成加锁和解锁
*/


// 日志等级
enum class LogLevel
{
    INFO,
    WARNING,
    ERROR,
    FATAL,
};
std::string LogLevel2String(LogLevel level)
{
    switch (level)
    {
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
    }
}

// 刷新策略的基类
class LogStrategy
{
public:
    virtual ~LogStrategy()
    {
    }

    // 写日志的具体方法,每种策略中必须重写该函数
    virtual void SynLog(const std::string& message) = 0;
};

// 显示器打印日志策略
class ConsoleStrategy : public LogStrategy
{
public:
    void SynLog(const std::string& message) override
    {
        LockGuard lg(_mutex);
        std::cout << message << std::endl;
    }

private:
    Mutex _mutex;
};

// 文件写入日志策略
const std::string default_logpath = "./log";
const std::string default_logfilename = "log.txt";
class FileStrategy : public LogStrategy
{
public:
    FileStrategy(const std::string& logpath = default_logpath, const std::string& logfilename = default_logfilename)
        : _logpath(logpath), _logfilename(logfilename)
    {
        // 如果传入的目录和文件不存在则创建

        LockGuard lg(_mutex);

        // C++17引入的std::filesystem库用法
        if (std::filesystem::exists(_logpath))
            return;
        try
        {
            std::filesystem::create_directories(_logpath);
        }
        catch (const std::filesystem::filesystem_error& e)
        {
            std::cerr << e.what() << std::endl;
        }
    }

    void SynLog(const std::string& message) override
    {
        LockGuard lg(_mutex);

        if (_logpath != "" && _logpath.back() != '/')
        {
            _logpath += '/';
        }
        std::string targetlog = _logpath + _logfilename;
        std::ofstream out(targetlog, std::ios::app);
        if (!out.is_open())
        {
            std::cerr << "open " << targetlog << " fail!" << std::endl;
            return;
        }
        out << message << '\n';
        out.close();
    }

private:
    std::string _logpath;     // 日志文件所在目录
    std::string _logfilename; // 日志文件名
    Mutex _mutex;
};

// 按等级写入不同文件策略
class FileLevelStrategy : public LogStrategy
{
public:
    FileLevelStrategy(const std::string& logpath = default_logpath) : _logpath(logpath)
    {
        // 如果传入的目录不存在则创建

        LockGuard lg(_mutex);

        // C++17引入的std::filesystem库用法
        if (std::filesystem::exists(_logpath))
            return;
        try
        {
            std::filesystem::create_directories(_logpath);
        }
        catch (const std::filesystem::filesystem_error& e)
        {
            std::cerr << e.what() << std::endl;
        }
    }

    void SynLog(const std::string& message) override
    {
        LockGuard lg(_mutex);

        if (_logpath != "" && _logpath.back() != '/')
        {
            _logpath += '/';
        }

        size_t pos1 = message.find('[', 1);
        size_t pos2 = message.find(']', pos1);
        if(pos1 != std::string::npos && pos2 != std::string::npos)
        {
            std::string level = message.substr(pos1+1, pos2-pos1-1);
            _logfilename = "log." + level + ".txt";
        }
       
        std::string targetlog = _logpath + _logfilename;
        std::ofstream out(targetlog, std::ios::app);
        if (!out.is_open())
        {
            std::cerr << "open " << targetlog << " fail!" << std::endl;
            return;
        }
        out << message << '\n';
        out.close();
    }

private:
    std::string _logpath;
    std::string _logfilename;
    Mutex _mutex;
};

// 获取当前时间方法
std::string GetTime()
{
    struct timeval cur_time;
    gettimeofday(&cur_time, nullptr);
    struct tm struct_time;
    localtime_r(&(cur_time.tv_sec), &struct_time);
    char timestr[128];
    snprintf(timestr, sizeof timestr, "%04d-%02d-%02d %02d:%02d:%02d", struct_time.tm_year + 1900,
             struct_time.tm_mon + 1, struct_time.tm_mday, struct_time.tm_hour, struct_time.tm_min, struct_time.tm_sec);
    return timestr;
}

// 日志类需要完成: 1.生成日志信息 2.根据不同的策略进行刷新
class Logger
{
private:
    std::unique_ptr<LogStrategy> _strategy; // 刷新策略

public:
    // 内部类,描述一条完整的日志信息:
    // [时间][日志等级][进程id][文件名][代码行号] - 内容信息
    class LogMessage
    {
    public:
        LogMessage(LogLevel level, int line, std::string& filename, Logger& logger)
            : _cur_time(GetTime()), _level(level), _pid(getpid()), _filename(filename), _line(line), _logger(logger)
        {
            std::stringstream ss;
            ss << '[' << _cur_time << ']' << '[' << LogLevel2String(_level) << ']' << '[' << _pid << ']' << '['
               << _filename << ']' << '[' << _line << ']' << " - ";
            _loginfo = ss.str();
        }

        // 读取用户输入的内容信息
        template <class T> LogMessage& operator<<(const T& info)
        {
            std::stringstream ss;
            ss << info;
            _loginfo += ss.str();
            return *this;
        }

        // RAII自动刷新
        ~LogMessage()
        {
            _logger._strategy->SynLog(_loginfo);
        }

    private:
        std::string _cur_time;
        LogLevel _level;
        pid_t _pid;
        std::string _filename;
        int _line;
        std::string _loginfo;

        Logger& _logger;
    };

public:
    Logger()
    {
        _strategy = std::make_unique<ConsoleStrategy>();
    }

    void UseConsoleStrategy()
    {
        _strategy = std::make_unique<ConsoleStrategy>();
    }

    void UseFileStrategy()
    {
        _strategy = std::make_unique<FileStrategy>();
    }

    void UseFileLevelStrategy()
    {
        _strategy = std::make_unique<FileLevelStrategy>();
    }

    LogMessage operator()(LogLevel level, std::string filename, int line)
    {
        return LogMessage(level, line, filename, *this);
    }
};

Logger logger;

#define USE_CONSOLE_LOG_STRATEGY() logger.UseConsoleStrategy();
#define USE_FILE_LOG_STRATEGY() logger.UseFileStrategy();
#define USE_FILE_LEVEL_LOG_STRATEGY() logger.UseFileLevelStrategy();

#define LOG(level) logger(level, __FILE__, __LINE__)

// 至此,我们就可以用 "LOG(level) << 日志信息" 的方式进行日志写入刷新了

2. Json序列化/反序列化方式

在项目中需要频繁地序列化和反序列化,因此提前把这两个方法工具化

cpp 复制代码
// details.hpp
namespace RPC
{
    class JSON
    {
    public:
        // 实现数据的序列化
        static bool serialize(const Json::Value& val, std::string* body)
        {
            std::stringstream ss;
            // 先实例化一个工厂类对象
            Json::StreamWriterBuilder swb;
            // 通过工厂类对象来生产派生类对象
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            int ret = sw->write(val, &ss);
            if (ret != 0)
            {
                LOG(LogLevel::FATAL) << "json serialize failed!";
                return false;
            }
            *body = ss.str();
            return true;
        }

        // 实现json字符串的反序列化
        static bool unserialize(const std::string& body, Json::Value* val)
        {
            // 实例化工厂类对象
            Json::CharReaderBuilder crb;
            // 生产CharReader对象
            std::string errs;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), val, &errs);
            if (ret == false)
            {
                LOG(LogLevel::FATAL) << "json unserialize failed: " << errs;
                return false;
            }
            return true;
        }
    };
}

3. UUID的生成

UUID(Universally Unique Identifier), 也叫通用唯一识别码,通常由32位16进制数字字符组成。​

UUID的标准型式包含32个16进制数字字符,以"-"分为五段,形式为8-4-4-4-12的32个字符,​如:550e8400-e29b-41d4-a716-446655440000。​

在这里,uuid的生成我们采用生成8个随机数字,加上8字节序号,共16字节数组生成32位16进制字符

的组合形式来确保全局唯一的同时能够根据序号来分辨数据。

C++的Ramdon提供了生成随机数的方法

cpp 复制代码
// details.hpp
namespace RPC
{
	class UUID
    {
    public:
        static std::string uuid()
        {
            std::stringstream ss;
 
            // 构造一个机器随机数对象
            std::random_device rd;
            // 以机器随机数为种子构造随机数对象
            std::mt19937 generator(rd());
            // 构造限定数据范围的对象
            std::uniform_int_distribution<int> distribution(0, 255);
            // 生成8个随机数,按照特定格式组织成为16进制数字字符的字符串
            for(int i = 0; i < 8; i++)
            {
                if(i == 4 || i == 6)
                    ss << '-';
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
            }
            ss << '-'; 
            // 定义一个8字节序号,逐字节组织为16进制数字字符的字符串
            static std::atomic<size_t> seq(1);
            size_t cur = seq.fetch_add(1);
            for(int i = 7; i >= 0; i--)
            {
                if(i == 5)
                    ss << '-';
                ss << std::setw(2) << std::setfill('0') << std::hex << ((cur >> (i*8) & 0xFF));
            }
            return ss.str();
        }
        
    };

}

二、字段信息定义

1. 请求/响应报文的字段宏定义​

  • 消息ID
  • 消息类型
  • 消息正文
    • Rpc请求
      • 方法名称
      • 方法参数
    • 发布订阅相关请求
      • 主题名称
      • 操作类型
      • 主题消息
    • 服务操作相关请求
      • 方法名称
      • 操作类型
      • 主机信息:IP地址、PORT端口
    • 响应码
    • Rpc响应
      • 调用结果
cpp 复制代码
namespace RPC
{
    #define KEY_METHOD      "method"
    #define KEY_PARAMS      "parameters"
    #define KEY_TOPIC_KEY   "topic_key"
    #define KEY_TOPIC_MSG   "topic_msg"
    #define KEY_OPTYPE      "opertion_type"
    #define KEY_HOST        "host"
    #define KEY_HOST_IP     "host_ip"
    #define KEY_HOST_PORT   "host_port"
    #define KEY_RCODE       "return_code"
    #define KEY_RESULT      "result"
}

2. 消息类型定义​

  • Rpc请求 & 响应
  • 主题操作请求 & 响应
  • 消息发布请求 & 响应
  • 服务操作请求 & 响应
cpp 复制代码
namespace RPC
{
    enum class MsgType
    {
        REQ_RPC,
        RSP_RPC,
        REQ_TOPIC,
        RSP_TOPIC,
        REQ_SERVICE,
        RSP_SERVICE
    };
}

3. 响应码类型定义​

  • 成功处理
  • 解析失败
  • 消息中字段缺失或错误导致无效消息
  • 连接断开
  • 无效的Rpc调用参数
  • Rpc服务不存在
  • 无效的Topic操作类型
  • 主题不存在
  • 无效的服务操作类型
cpp 复制代码
namespace RPC
{
    enum class RspCode
    {
        RCODE_OK,
        RCODE_PARSE_FAILED,
        RCODE_ERROR_MSGTYPE,
        RCODE_INVALID_MSG,
        RCODE_DISCONNECTED,
        RCODE_INVALID_PARAMS,
        RCODE_NOT_FOUND_SERVICE,
        RCODE_NOT_FOUND_TOPIC,
        RCODE_INVALID_OPTYPE,
        RCODE_INTERNAL_ERROR
    };

    static std::string errReason(RCode code)
    {
        static std::unordered_map<RspCode, std::string> err_map = 
        {
            {RCode::RCODE_OK,                 "成功处理"},
            {RCode::RCODE_PARSE_FAILED,       "消息解析失败"},
            {RCode::RCODE_ERROR_MSGTYPE,      "消息类型错误"},
            {RCode::RCODE_INVALID_MSG,        "消息无效"},
            {RCode::RCODE_DISCONNECTED,       "连接已断开"},
            {RCode::RCODE_INVALID_PARAMS,     "RPC参数无效"},
            {RCode::RCODE_NOT_FOUND_SERVICE,  "没有找到对应的服务"},
            {RCode::RCODE_NOT_FOUND_TOPIC,    "没有找到对应的主题"},
            {RCode::RCODE_INVALID_OPTYPE,     "操作类型无效"},
            {RCode::RCODE_INTERNAL_ERROR,     "内部错误"}
        };

        if(err_map.count(code) == 0)
            return "未知错误";
        
        return err_map[code];
    }
}

4. RPC请求类型定义​

  • 同步请求:等待收到响应后返回。但是我们在异步请求时,获取结果时,需要同步获取,因此同步请求方式这里不需要单独存在。
  • 异步请求:返回异步对象,在需要的时候通过异步对象获取响应结果,还未收到结果会阻塞。
  • 回调请求:设置回调函数,通过回调函数对响应进行处理。
cpp 复制代码
namespace RPC
{
    enum class ReqType
    {
        REQ_ASYNC,
        REQ_CALLBACK
    };
}

5. 主题操作类型定义​

  • 主题创建
  • 主题删除
  • 主题订阅
  • 主题取消订阅
  • 主题消息发布
cpp 复制代码
namespace RPC
{
    enum class TopicOpType
    {
        TOPIC_CREATE,
        TOPIC_REMOVE,
        TOPIC_SUBSCRIBE,
        TOPIC_CANCEL,
        TOPIC_PUBLISH
    };
}

6. 服务操作类型定义​

  • 服务注册
  • 服务发现
  • 服务上线
  • 服务下线
cpp 复制代码
namespace RPC
{
    enum class ServiceOpType
    {
        SERVICE_REGISTRY,
        SERVICE_DISCOVERY,
        SERVICE_ONLINE,
        SERVICE_OFFLINE,
        SERVICE_UNKNOW
    };
}

三、抽象层的实现

根据这张框架图,明确我们应该在抽象层实现的类型与成员。

cpp 复制代码
// Base.hpp
// 抽象层
#pragma once
#include "fields.hpp"
#include <functional>
#include <memory>
#include <string>

namespace RPC
{
    class BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<BaseBuffer>;

        virtual size_t readableSize() = 0;
        virtual int32_t peekInt32() = 0;   // 尝试取出前四字节数据
        virtual void retrieveInt32() = 0;  // 删除前四字节数据 

        virtual int32_t readInt32() = 0;   // 取出并删除前四字节数据
        virtual std::string retrieveAsString(size_t len) = 0;
    };

    class BaseMessage
    {
    public:
        using ptr = std::shared_ptr<BaseMessage>;

        virtual ~BaseMessage();

        virtual void setId(const std::string& id)
        {
            _rid = id;
        }
        virtual void setMsgtype(MsgType mtype)
        {
            _mtype = mtype;
        }
        virtual std::string rid()
        {
            return _rid;
        }
        virtual MsgType mtype()
        {
            return _mtype;
        }

        virtual std::string serialize() = 0;
        virtual bool unserialize(const std::string& msg) = 0;
        virtual bool check() = 0;

    private:
        MsgType _mtype;
        std::string _rid;
    };

    class BaseConnection
    {
    public:
        using ptr = std::shared_ptr<BaseConnection>;
        virtual void send(const BaseMessage::ptr& msg) = 0;
        virtual void shutdown() = 0;
        virtual bool connected() = 0;
    };

    class BaseProtocol
    {
    public:
        using ptr = std::shared_ptr<BaseProtocol>;
        virtual bool canProcessed(const BaseBuffer::ptr& buf) = 0;
        virtual bool onMessage(const BaseBuffer::ptr& buf, BaseMessage::ptr& msg) = 0;
        virtual std::string serialize(const BaseMessage::ptr& msg) = 0;
    };

    using ConnectionCallBack = std::function<void(const BaseConnection::ptr&)>;
    using CloseCallBack = std::function<void(const BaseConnection::ptr&)>;
    using MessageCallBack = std::function<void(const BaseConnection::ptr&, BaseMessage::ptr&)>;
    class BaseServer
    {
    public:
        using ptr = std::shared_ptr<BaseServer>;
        virtual void setConnectionCallBack(const ConnectionCallBack& cb)
        {
            _cb_connection = cb;
        }
        virtual void setCloseCallBack(const CloseCallBack& cb)
        {
            _cb_close = cb;
        }
        virtual void setMessageCallBack(const MessageCallBack& cb)
        {
            _cb_message = cb;
        }

    protected:
        ConnectionCallBack _cb_connection;
        CloseCallBack _cb_close;
        MessageCallBack _cb_message;
    };

    class BaseClient
    {
    public:
        using ptr = std::shared_ptr<BaseClient>;
        virtual void setConnectionCallBack(const ConnectionCallBack& cb)
        {
            _cb_connection = cb;
        }
        virtual void setCloseCallBack(const CloseCallBack& cb)
        {
            _cb_close = cb;
        }
        virtual void setMessageCallBack(const MessageCallBack& cb)
        {
            _cb_message = cb;
        }

        virtual void connect() = 0;
        virtual void shutdown() = 0;
        virtual bool send(const BaseMessage::ptr& msg) = 0;
        virtual BaseConnection::ptr connection() = 0;
        virtual bool connected() = 0;
        
    protected:
        ConnectionCallBack _cb_connection;
        CloseCallBack _cb_close;
        MessageCallBack _cb_message;
    };

}

四、具象层的实现

每一种类,我们最好都要配套相应的工厂类进行生产对象,便于管理和扩展。

1. 不同的消息类型

根据这张框架设计图,设计出类的继承体系。

cpp 复制代码
// message.hpp
#pragma once
#include "details.hpp"
#include "Base.hpp"
#include "fields.hpp"
#include <utility>
#include <vector>

namespace RPC
{
    typedef std::pair<std::string, int> Address; // ip + port

    class JsonMessage : public BaseMessage
    {
    public:
        using ptr = std::shared_ptr<JsonMessage>;

        virtual std::string serialize() override
        {   
            std::string body;
            if(JSON::serialize(_body, &body) == false)
            {
                return std::string();
            }
            return body;
        }

        virtual bool unserialize(const std::string& msg) override
        {
            return JSON::unserialize(msg, &_body);
        }

    protected:
        Json::Value _body;
    };



    class JsonRequest : public JsonMessage
    {
    public:
        using ptr = std::shared_ptr<JsonRequest>;
    };

    class RpcRequest : public JsonRequest
    {
    public:
        using ptr = std::shared_ptr<RpcRequest>;

        virtual bool check() override
        {
            // Rpc请求中,应该包含请求方法名称(字符串)、参数信息(对象)
            if(_body[KEY_METHOD].isNull() || _body[KEY_METHOD].isString() == false)
            {
                LOG(LogLevel::ERROR) << "Rpc请求中没有方法名称或方法名称类型错误!";
                return false;
            }
            if(_body[KEY_PARAMS].isNull() || _body[KEY_PARAMS].isObject() == false)
            {
                LOG(LogLevel::ERROR) << "Rpc请求中没有参数信息或参数信息类型错误!";
                return false;
            }
            return true;
        }

        std::string method()
        {
            return _body[KEY_METHOD].asString();
        }

        void setMethod(const std::string& method)
        {
            _body[KEY_METHOD] = method;
        }

        Json::Value params()
        {
            return _body[KEY_PARAMS];
        }

        void setParams(const Json::Value& params)
        {
            _body[KEY_PARAMS] = params;
        }

    };

    class TopicRequest : public JsonRequest
    {
    public:
        using ptr = std::shared_ptr<TopicRequest>;
        virtual bool check() override
        {
            // Topic请求中,应该包含主题名称(字符串)、操作类型
            if(_body[KEY_TOPIC_KEY].isNull() || _body[KEY_TOPIC_KEY].isString() == false)
            {
                LOG(LogLevel::ERROR) << "主题请求中没有主题名称或主题名称类型错误!";
                return false;
            }
            if(_body[KEY_OPTYPE].isNull() || _body[KEY_OPTYPE].isInt() == false)
            {
                LOG(LogLevel::ERROR) << "主题请求中没有操作类型信息或操作类型的类型错误!";
                return false;
            }

            // 如果Topic请求的操作类型是"发布主题",则还应该包含消息信息字段
            if(_body[KEY_OPTYPE].asInt() == (int)TopicOpType::TOPIC_PUBLISH &&
               (_body[KEY_TOPIC_MSG].isNull() || _body[KEY_TOPIC_MSG].isString() == false))
            {
                LOG(LogLevel::ERROR) << "主题发布请求中没有消息信息或消息信息的类型错误!";
                return false;
            }

            return true;
        }

        std::string topicKey()
        {
            return _body[KEY_TOPIC_KEY].asString();
        }

        void setTopicKey(const std::string& key)
        {
            _body[KEY_TOPIC_KEY] = key;
        }

        TopicOpType optype()
        {
            return (TopicOpType)_body[KEY_OPTYPE].asInt();
        }

        void setOptype(TopicOpType optype)
        {
            _body[KEY_OPTYPE] = (int)optype;
        }

        std::string topicMsg() 
        {
            return _body[KEY_TOPIC_MSG].asString();
        }
            
        void setTopicMsg(const std::string &msg)
        {
            _body[KEY_TOPIC_MSG] = msg;
        }
    };

    class ServiceRequest: public JsonRequest
    {
    public:
        using ptr = std::shared_ptr<ServiceRequest>;
        virtual bool check() override
        {
            // Service请求中,应该包含服务名称(字符串)、操作类型
            if(_body[KEY_METHOD].isNull() || _body[KEY_METHOD].isString() == false)
            {
                LOG(LogLevel::ERROR) << "服务请求中没有服务名称或服务名称类型错误!";
                return false;
            }
            if(_body[KEY_OPTYPE].isNull() || _body[KEY_OPTYPE].isInt() == false)
            {
                LOG(LogLevel::ERROR) << "服务请求中没有操作类型信息或操作类型的类型错误!";
                return false;
            }

            // 如果操作类型不是"发现请求",而是其他的"服务注册、服务上线、服务下线",则请求中还应该有主机地址信息字段
            if(_body[KEY_OPTYPE].asInt() != (int)(ServiceOpType::SERVICE_DISCOVERY) &&
               (_body[KEY_HOST].isNull() || _body[KEY_HOST].isObject() == false ||
                _body[KEY_HOST][KEY_HOST_IP].isNull() || _body[KEY_HOST][KEY_HOST_IP].isString() == false ||
                _body[KEY_HOST][KEY_HOST_PORT].isNull() || _body[KEY_HOST][KEY_HOST_PORT].isInt() == false))
            {
                LOG(LogLevel::ERROR) << "服务请求中主机地址信息错误!";
                return false;
            }

            return true;
        }

        std::string method()
        {
            return _body[KEY_METHOD].asString();
        }

        void setMethod(const std::string& method)
        {
            _body[KEY_METHOD] = method;
        }

        ServiceOpType optype()
        {
            return (ServiceOpType)_body[KEY_OPTYPE].asInt();
        }

        void setOptype(ServiceOpType optype)
        {   
            _body[KEY_OPTYPE] = (int)optype;
        }

        Address host()
        {
            Address addr;
            addr.first = _body[KEY_HOST][KEY_HOST_IP].asString();
            addr.second = _body[KEY_HOST][KEY_HOST_PORT].asInt();
            return addr;
        }

        void setHost(const Address& host)
        {
            Json::Value val;
            val[KEY_HOST_IP] = host.first;
            val[KEY_HOST_PORT] = host.second;
            _body[KEY_HOST] = val;
        }
    };



    class JsonResponse : public JsonMessage
    {
    public:
        using ptr = std::shared_ptr<JsonResponse>;
        virtual bool check() override
        {
            // 大部分响应只有RspCode
            // 只要判断响应字段是否存在,类型是否正确即可
            if(_body[KEY_RCODE].isNull())
            {
                LOG(LogLevel::ERROR) << "响应中没有RCode!";
                return false;
            }
            if(_body[KEY_RCODE].isInt() == false)
            {
                LOG(LogLevel::ERROR) << "响应中RCode类型错误!";
                return false;
            }
            return true;
        }

        virtual RCode rcode()
        {
            return (RCode)_body[KEY_RCODE].asInt();
        }

        virtual void setRCode(RCode rcode)
        {
            _body[KEY_RCODE] = (int)rcode;
        }
    };

    class RpcResponse : public JsonResponse
    {
    public:
        using ptr = std::shared_ptr<RpcResponse>;
        virtual bool check() override
        {
            if(_body[KEY_RCODE].isNull() || _body[KEY_RCODE].isInt() == false)
            {
                LOG(LogLevel::ERROR) << "RPC响应中RCode不存在或RCode类型错误!";
                return false;
            }

            // 服务的返回结果可能是各种类型,这里不判断类型
            if(_body[KEY_RESULT].isNull())
            {
                LOG(LogLevel::ERROR) << "RPC响应中没有RPC调用结果!";
                return false;
            }
            return true;
        }

        Json::Value result()
        {
            return _body[KEY_RESULT];
        }

        void setResult(const Json::Value& result)
        {
            _body[KEY_RESULT] = result;
        }
    };

    class TopicResponse : public JsonResponse
    {
    public:
        using ptr = std::shared_ptr<TopicResponse>;
        // Topic响应中只会有RCode, 父类中已经实现好check了
    };

    class ServiceResponse : public JsonResponse
    {
    public:
        using ptr = std::shared_ptr<ServiceResponse>;
        virtual bool check() override
        {
            if(_body[KEY_RCODE].isNull() || _body[KEY_RCODE].isInt() == false)
            {
                LOG(LogLevel::ERROR) << "服务响应中RCode不存在或RCode类型错误!";
                return false;
            }
            if (_body[KEY_OPTYPE].isNull() || _body[KEY_OPTYPE].isInt() == false) 
            {
                LOG(LogLevel::ERROR) << "服务响应中没有操作类型或操作类型的类型错误!";
                return false;
            }

            // 如果操作类型是"服务发现",则响应中还应该有method和host数组字段
            if(_body[KEY_OPTYPE].asInt() == (int)(ServiceOpType::SERVICE_DISCOVERY) &&
              (_body[KEY_METHOD].isNull() || _body[KEY_METHOD].isString() == false ||
               _body[KEY_HOST].isNull() || _body[KEY_HOST].isArray() == false))
            {
                LOG(LogLevel::ERROR) << "服务发现响应中信息字段错误!";
                return false;
            }
            return true;
        }

        ServiceOpType optype()
        {
            return (ServiceOpType)_body[KEY_OPTYPE].asInt();
        }

        void setOptype(ServiceOpType optype)
        {
            _body[KEY_OPTYPE] = (int)optype;
        }

        std::string method() 
        {
            return _body[KEY_METHOD].asString();
        }
            
        void setMethod(const std::string &method) 
        {
            _body[KEY_METHOD] = method;
        }

        void setHost(std::vector<Address> addrs)
        {
            for(auto& addr : addrs)
            {
                Json::Value val;
                val[KEY_HOST_IP] = addr.first;
                val[KEY_HOST_PORT] = addr.second;
                _body[KEY_HOST].append(val);
            }
        }

        std::vector<Address> hosts()
        {
            std::vector<Address> addrs;
            int sz = _body[KEY_HOST].size();
            for(int i = 0; i < sz; i++)
            {
                Address addr;
                addr.first = _body[KEY_HOST][i][KEY_HOST_IP].asString();
                addr.second = _body[KEY_HOST][i][KEY_HOST_PORT].asInt();
                addrs.push_back(addr);
            }
            return addrs;
        }

    };


    // 工厂模式,实现一个消息对象的简单生产工厂
    class MsgFactory
    {   
    public:
        // 两种生产方法

        static BaseMessage::ptr create(MsgType mtype)
        {
            switch(mtype) 
            {
                case MsgType::REQ_RPC: return std::make_shared<RpcRequest>();
                case MsgType::RSP_RPC: return std::make_shared<RpcResponse>();
                case MsgType::REQ_TOPIC: return std::make_shared<TopicRequest>();
                case MsgType::RSP_TOPIC: return std::make_shared<TopicResponse>();
                case MsgType::REQ_SERVICE: return std::make_shared<ServiceRequest>();
                case MsgType::RSP_SERVICE: return std::make_shared<ServiceResponse>();
            }
            return BaseMessage::ptr();
        }

        template<typename T, typename ...Args>
        static std::shared_ptr<T> create(Args&& ...args)
        {
            return std::make_shared<T>(std::forward(args)...);
        }
    };
}

2. 网络通信模块 + Protocol模块

这部分应该包含有:网络通信缓冲区的管理、网络通信协议的规定、通信连接的管理、服务端通信的管理、客户端通信的管理。

cpp 复制代码
// net.hpp
#pragma once
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpClient.h>
#include "fields.hpp"
#include "Logger.hpp"
#include "Base.hpp"
#include <netinet/in.h>
#include "message.hpp"
#include <mutex>
#include <unordered_map>

namespace RPC
{
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;

        MuduoBuffer(muduo::net::Buffer* buf):
            _buf(buf)
        {}

        virtual size_t readableSize() override
        {
            return _buf->readableBytes();
        }

        virtual int32_t peekInt32() override
        {
            return _buf->peekInt32();
        }

        virtual void retrieveInt32() override
        {
            _buf->retrieveInt32();
        }   

        virtual int32_t readInt32() override
        {
            return _buf->readInt32();
        }

        virtual std::string retrieveAsString(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }

    private:
        muduo::net::Buffer* _buf;
    };

    class BufferFactory
    {
    public:
        template<typename ...Args>
        static BaseBuffer::ptr create(Args&& ...args)
        {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
        }
    };



    class LVProtocol : public BaseProtocol
    {
    public:
        using ptr = std::shared_ptr<LVProtocol>;

        // 协议规定报文格式:
        // |--len--|--mtype--|--idlen--|--id--|--body--|
        // len字段固定四字节,len值为len字段之后所有内容的长度
        // mtype字段固定四字节
        // idlen字段固定四字节,idlen值为id字段长度
    private:
        const size_t lenFieldsLength = 4;
        const size_t mtypeFieldsLength = 4;
        const size_t idlenFieldsLength = 4;

    public:
        // 判断缓冲区中的数据量是否足够一条消息
        virtual bool canProcessed(const BaseBuffer::ptr& buf) override
        {
            if(buf->readableSize() < lenFieldsLength)
            {
                return false;
            }
            int32_t total_len = buf->peekInt32();
            if(buf->readableSize() < total_len + lenFieldsLength)
            {
                return false;
            }
            return true;
        }

        // 当调用onMessage函数时,默认认为缓冲区中的数据足够一条完整的消息
        // 从缓冲区中取出一条消息,反序列化,记录到参数的msg中
        virtual bool onMessage(const BaseBuffer::ptr& buf, BaseMessage::ptr& msg) override
        {
            int32_t total_len = buf->readInt32();
            MsgType mtype = (MsgType)buf->readInt32();
            int32_t idlen = buf->readInt32();

            int32_t body_len = total_len - mtypeFieldsLength - idlenFieldsLength - idlen;
            std::string id = buf->retrieveAsString(idlen);
            std::string body = buf->retrieveAsString(body_len);

            msg = MsgFactory::create(mtype);
            if(msg.get() == nullptr)
            {
                LOG(LogLevel::ERROR) << "消息类型错误,构建消息对象失败!";
                return false;
            }
            bool ret = msg->unserialize(body);
            if(ret == false)
            {
                return false;
            }
            msg->setId(id);
            msg->setMsgtype(mtype);
            return true;
        }
        
        virtual std::string serialize(const BaseMessage::ptr& msg) override
        {
            // |--len--|--mtype--|--idlen--|--id--|--body--|
            std::string body = msg->serialize();
            std::string id = msg->rid();
            auto mtype = htonl((int32_t)msg->mtype());
            int32_t idlen = htonl(id.size());

            int32_t h_total_len = mtypeFieldsLength + idlenFieldsLength + id.size() + body.size();
            int32_t n_total_len = htonl(h_total_len);

            std::string result;
            result.reserve(h_total_len + lenFieldsLength);
            result.append((char*)&n_total_len, lenFieldsLength);
            result.append((char*)&mtype, mtypeFieldsLength);
            result.append((char*)&idlen, idlenFieldsLength);
            result.append(id);
            result.append(body);

            return result;
        }
    };

    class ProtocolFactory
    {
    public:
        template<typename ...Args>
        static BaseProtocol::ptr create(Args&& ...args)
        {
            return std::make_shared<LVProtocol>(std::forward<Args>(args)...);
        }
    };



    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;
        MuduoConnection(const muduo::net::TcpConnectionPtr& conn, const BaseProtocol::ptr& protocol)
            : _protocol(protocol)
            , _conn(conn)
        {}

        virtual void send(const BaseMessage::ptr& msg) override
        {
            std::string body = _protocol->serialize(msg);
            _conn->send(body);
        }

        virtual void shutdown() override
        {
            _conn->shutdown();
        }

        virtual bool connected() override
        {
            return _conn->connected();
        }

    private:
        BaseProtocol::ptr _protocol;
        muduo::net::TcpConnectionPtr _conn;
    };

    class ConnectionFactory
    {
    public:
        template<typename ...Args>
        static BaseConnection::ptr create(Args&& ...args)
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };



    class MuduoServer : public BaseServer
    {
    public:
        using ptr = std::shared_ptr<MuduoServer>;

        MuduoServer(int port)
            : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "MuduoServer", muduo::net::TcpServer::kReusePort)
            , _protocol(ProtocolFactory::create())
            // server的0.0.0.0表示监听本机所有网卡,muduo::net::TcpServer::kReusePort表示开启端口复用
        {}

        virtual void start()
        {
            _server.setConnectionCallback(std::bind(&MuduoServer::onConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            
            _server.start();  // 先开始监听
            _baseloop.loop(); // 开始循环事件监控
        }

    private:
        // 这个函数会在网络连接改变时,服务端调用。
        // 如果是新来一个连接,就把记录到自己的_conns表中,同时调用新建连接后的业务函数
        // 如果是有一个连接断了,就把他从自己的_conns表中删除,同时调用断开连接后的业务函数
        void onConnection(const muduo::net::TcpConnectionPtr& conn)
        {
            if(conn->connected())
            {
                LOG(LogLevel::INFO) << "连接建立!";
                auto muduo_conn = ConnectionFactory::create(conn, _protocol);
                {
                    // 加锁保护
                    std::unique_lock<std::mutex> lock(_mutex);
                    _conns.insert(std::make_pair(conn, muduo_conn));
                }
                if(_cb_connection)
                    _cb_connection(muduo_conn);
            }
            else 
            {
                LOG(LogLevel::INFO) << "连接断开!";
                BaseConnection::ptr muduo_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if(it == _conns.end())
                    {
                        return;
                    }
                    muduo_conn = it->second;
                    _conns.erase(conn);
                }
                if(_cb_close)
                    _cb_close(muduo_conn);
            }
        }

        // 这个函数会在收到数据时,服务端调用。
        // 如果缓冲区中有一条完整消息,取出,然后调用处理消息的业务函数
        void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp)
        {
            LOG(LogLevel::INFO) << "有消息到来,开始处理!";
            auto base_buf = BufferFactory::create(buf);
            while(1)
            {
                if(_protocol->canProcessed(base_buf) == false) // 当前缓冲区数据不够一条消息 
                {
                    if(base_buf->readableSize() > maxDataSize)
                    {
                        // 设置一个maxDataSize, 防止有恶意用户发送过大数据
                        LOG(LogLevel::WARNING) << "缓冲区中数据过大";
                        conn->shutdown();
                        return;
                    }
                    LOG(LogLevel::INFO) << "缓冲区中数据量不足";
                    break;
                }

                // 从缓冲区中取出一条消息
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg);
                if(ret == false)
                {
                    conn->shutdown();
                    LOG(LogLevel::ERROR) << "缓冲区中数据错误!";
                    return;
                }

                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end()) 
                    {
                        conn->shutdown();
                        return;
                    }
                    base_conn = it->second;
                }
                if(_cb_message)
                    _cb_message(base_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        muduo::net::EventLoop _baseloop;
        muduo::net::TcpServer _server;
        BaseProtocol::ptr _protocol;
        std::mutex _mutex;
        // Muduo库的Connection类与我们自己的Connection类的映射
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;
    };

    class ServerFactory
    {
    public:
        template<typename ...Args>
        static BaseServer::ptr create(Args&& ...args)
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };



    class MuduoClient : public BaseClient
    {
    public:
        using ptr = std::shared_ptr<MuduoClient>;

        MuduoClient(const std::string& sip, int sport)
            : _protocol(ProtocolFactory::create())
            , _baseloop(_loopthread.startLoop())
            , _downlatch(1)
            , _client(_baseloop, muduo::net::InetAddress(sip, sport), "MuduoClient")
        {}

        virtual void connect() override
        {
            _client.setConnectionCallback(std::bind(&MuduoClient::onConnection, this, std::placeholders::_1));
            _client.setMessageCallback(std::bind(&MuduoClient::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        
            _client.connect();
            _downlatch.wait();
            LOG(LogLevel::INFO) << "连接服务器成功!";
        }

        virtual void shutdown() override
        {
            _client.disconnect();
        }

        virtual bool send(const BaseMessage::ptr& msg) override
        {
            if (connected() == false) 
            {
                LOG(LogLevel::INFO) << "连接已断开!";
                return false;
            }
            _conn->send(msg);
            return true;
        }

        virtual BaseConnection::ptr connection() override
        {
            return _conn;
        }

        virtual bool connected() override
        {
            return _conn && _conn->connected();
        }

    private:
        // 这个函数会在网络连接改变时,客户端调用。
        // 当连接建立后,把连接记录到自己的成员_conn中
        // 当连接断开后,释放当前连接对象指针
        void onConnection(const muduo::net::TcpConnectionPtr& conn)
        {
            if (conn->connected()) 
            {
                LOG(LogLevel::INFO) << "连接建立!";
                _downlatch.countDown();  //计数--,为0时唤醒阻塞
                _conn = ConnectionFactory::create(conn, _protocol);
            }
            else 
            {
                LOG(LogLevel::INFO) << "连接断开!";
                _conn.reset(); // 释放当前连接对象指针
            }
        }

        // 这个函数会在收到数据时,客户端调用。
        // 如果缓冲区中有一条完整消息,取出,然后调用处理消息的业务函数
        void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp)
        {
            LOG(LogLevel::INFO) << "有消息到来,开始处理!";
            auto base_buf = BufferFactory::create(buf);
            while(1)
            {
                if(_protocol->canProcessed(base_buf) == false) // 当前缓冲区数据不够一条消息 
                {
                    if(base_buf->readableSize() > maxDataSize)
                    {
                        // 设置一个maxDataSize, 防止有恶意用户发送过大数据
                        LOG(LogLevel::WARNING) << "缓冲区中数据过大";
                        conn->shutdown();
                        return;
                    }
                    LOG(LogLevel::INFO) << "缓冲区中数据量不足";
                    break;
                }

                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg);
                if(ret == false)
                {
                    conn->shutdown();
                    LOG(LogLevel::ERROR) << "缓冲区中数据错误!";
                    return;
                }

                if(_cb_message)
                    _cb_message(_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        BaseProtocol::ptr _protocol;
        BaseConnection::ptr _conn;
        muduo::CountDownLatch _downlatch;
        muduo::net::EventLoopThread _loopthread;
        muduo::net::EventLoop* _baseloop;
        muduo::net::TcpClient _client;
    };

    class ClientFactory
    {
    public:
        template<typename ...Args>
        static BaseClient::ptr create(Args&& ...args)
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };

}

相关推荐
Harvy_没救了1 小时前
【github爆款】Headroom:AI代理的智能上下文压缩层 —— 深度解析与洞察
人工智能·github
圣殿骑士-Khtangc1 小时前
GPT-5.5 全面上线:大模型进入“价值验证“之年,幻觉率断崖式下降52.5%
人工智能
shixuzhimeng1 小时前
FTP服务器项目
linux·网络·ftp
weixin_468466851 小时前
深度学习图像数据增强新手实战指南
图像处理·人工智能·深度学习·ai·数据增强·机器视觉
Chris-zz1 小时前
Linux:线程概念与控制
linux·运维
Swift社区1 小时前
鸿蒙 App 集成 AI 助手:架构设计 + 实战代码
人工智能·华为·harmonyos
复利人生 复利日知录 赋能循环1 小时前
丘孔20260606复利的认知提升
人工智能
handler011 小时前
【算法】并查集(普通/扩展/带权)模板与例题
数据结构·c++·笔记·算法·c·图论·查并集
力学与人工智能1 小时前
AIAAJ | 西工大常宝辉、李楠等:基于径向基函数神经网络的激波串数据驱动控制方法研究
人工智能·深度学习·神经网络·数据驱动·径向基函数·激波·控制方法