文章目录
- 一、一些常用的细节功能接口
-
- [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响应
- 调用结果
- 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)...);
}
};
}