文章目录
项目链接:https://gitee.com/gan2729583308/json-rpc-framework
一、什么是JsonRpc?
Rpc的意思就是Remote Procedure Call,指的是一种远程过程调用,类似于我们使用AI的过程就是一种远程过程调用。

这里我们使用AI,但是问题的计算过程并不在我们的计算机当中,我们只是提出了问题就得到了结果,这就是一种Rpc,我们只是发送一个指令给远程的服务器,消耗远程服务器的资源来计算内容,然后将结果返回到我们自己的计算机就叫Rpc,Json指的是两台计算机之间通信需要一种协议才能通信,那么我们这里就采用Json的形式
二、JsonRpc第一步具体需要实现什么功能?

为了便于初步的理解,大概需要实现的内容就是如上图所示,但是我们在后面需要引入微服务功能所以不会这么简单,但是我们首先需要简历初步的理解,否则直接理解会感觉很抽象。
三、第一层抽象接口层
3-1 抽象层消息类
既然Rpc是要发送消息来解决任务,那我们就需要定义发送消息的格式,也就是将消息给封装起来
class BaseMessage{
public:
using ptr=std::shared_ptr<BaseMessage>;
virtual ~BaseMessage(){}
virtual void setId(const std::string &id)
{
_rid=id;
}
virtual std::string rid()
{
return _rid;
}
virtual void setMType(MType mtype)
{
_mtype=mtype;
}
virtual MType mtype()
{
return _mtype;
}
virtual std::string serialize()=0;
virtual bool unserialize(std::string& msg)=0;
virtual bool check()=0;
private:
MType _mtype;
std::string _rid;
};
-
rid:我们需要保证不管什么类型的消息都需要有一个唯一标识符,不然会导致消息的处理错乱,毕竟是在多态主机中进行流通的消息,有时我们需要让一个消息有唯一的属性才方便管理 -
mtype:我们后续的主机消息交换会涉及到多种数据类型,每种数据类型有不同的处理方法,所以需要在每个消息中加上这个消息属于那种消息类型的参数,以确保后续处理对应类型的数据有相关的方法 -
serialize和unserialize:序列化和反序列化方法,将消息以string字符串形式呈现,或者以Json格式呈现 -
check:则是检查消息中必要的参数是否存在
3-2 抽象层连接类
连接类和muduo::net::TcpConnectionPtr联系程度很高,因为所有的消息都需要序列化后通过muduo::net::TcpConnectionPtr这根线后才能抵达对方,
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;
};
send:直接将BaseMessage通过muduo::net::TcpConnectionPtr来发送出去,当然BaseMessage需要先序列化成string才能发送出去
connected():用于判断这个BaseConnection是否已经建立了连接
connected():用于断开连接
3-3 抽象层缓冲区类
每个客户端或者服务端都需要一个接收消息数据的缓冲区,发送过来的数据需要先存入缓冲区中再取出来使用
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;
};
这五个接口全都是muduo::net::Buffer的接口,只是我们在这里封装了一层
readableSize:查看缓冲区中还有多少字节可读peekInt32:返回前4个字节的整型值但不读取retrieveInt32:丢弃前面四个字节的数据,配合peekInt32使用读取readInt32:读取前面4个字节的数据并移动读取指针retrieveAsString:读取前len个字节的数据,并移动指针,以string的形式返回
3-4 抽象层协议类
协议类是为了解决粘包半包问题的,在TCP通信中可能会存在数据包的丢失,而TCP是通过字节流的方式来读取数据的,如果有数据丢失会导致半包问题,如果不设置协议,那么所有的数据包黏在一起我们不知道怎么分开
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;
};
canProcessed:检验这个缓冲区,第一个数据包的完整性onMessage:执行完canProcessed,将第一个报文拆解出来转化成BaseMessageserialize:将BaseMessage使用协议的规则序列化
3-5 抽象层服务类
协议,缓冲区,消息,连接都有了,那么最底层就差通信双方了,就是客户端和服务端
- 服务端
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;
}
virtual void start()=0;
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&)=0;
virtual BaseConnection::ptr connection()=0;
virtual bool connected()=0;
protected:
ConnectionCallback _cb_connection;
CloseCallback _cb_close;
MessageCallback _cb_message;
};
四、第二层网络实现层
上述的抽象层属于地基,而网络层就是在这个地基上负责施工的工人
我们先来看工人施工用的工具有哪些
4-1 网络层工具
4-1-1 日志工具
在程序运行时,我们需要打印一些信息,所以我们需要一个日志的工具,而且为了方便调试,这个日志工具带有时间,并且需要分级
#define LOG(level,format,...){\
if(level>=LDEFAULT){\
time_t t=time(NULL);\
struct tm* lt=localtime(&t);\
char time_tmp[32]={0};\
strftime(time_tmp,31,"%m-%d %T",lt);\
fprintf(stdout,"[%s][%s:%d]" format "\n",time_tmp,__FILE__,__LINE__,##__VA_ARGS__);\
}\
}
#define DLOG(format,...) LOG(LDBG,format,##__VA_ARGS__);
#define ILOG(format,...) LOG(LINF,format,##__VA_ARGS__);
#define ELOG(format,...) LOG(LERR,format,##__VA_ARGS__);
4-1-2 Json序列化和反序列化工具
定义一个结构,序列化输入一个结构体对象,返回一个字符串,反序列化输入一个字符串返回一个结构体对象
class JSON{
public:
//Json::Value----->String
static bool serialize(const Json::Value& val,std::string& body)
{
std::stringstream ss;
Json::StreamWriterBuilder swb;
swb["emitUTF8"]=true;
std::unique_ptr<Json::StreamWriter>sw(swb.newStreamWriter());
int ret=sw->write(val,&ss);
if(ret!=0)
{
ELOG("JSON UNSERIALIZE FAILED");
return false;
}
body=ss.str();
return true;
}
//String----->Json::value
static bool unserialize(const std::string &body,Json::Value&val)
{
Json::CharReaderBuilder crb;
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)
{
ELOG("JSON UNSERIALIZE FAILED:%s",errs.c_str());
return false;
}
return true;
}
};
4-1-3 UUID工具
因为每个报文都要有唯一的ID,所以需要一个工具为我们生成一个32位的ID,比如:ad2af789-d53c-154f-0000-000000000001
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);
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<<"-";
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();
}
};
4-2 网络层缓冲区类
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();
}
//返回前4个字节的整型值但不读取
virtual int32_t peekInt32() override{
return _buf->peekInt32();
}
//丢弃前面四个字节的数据,配合peekInt32使用读取
virtual void retrieveInt32()override{
return _buf->retrieveInt32();
}
//读取前面4个字节的数据并移动读取指针
virtual int32_t readInt32()override{
return _buf->readInt32();
}
//读取前len个字节的数据,并移动指针,以string的形式返回
virtual std::string retrieveAsString(size_t len)override{
return _buf->retrieveAsString(len);
}
private:
muduo::net::Buffer *_buf;
};
这里的缓冲区和我们上面所述的差不多,就是简单封装muduo::net::Buffer的接口
4-3 网络层连接类
class MuduoConnection:public BaseConnection
{
public:
using ptr=std::shared_ptr<MuduoConnection>;
//TcpConnectionPtr是用来控制一个端口和另一个端口的连接,传入一个协议一个连接即可,每个客户端只要一个协议即可
MuduoConnection(const muduo::net::TcpConnectionPtr &conn
,const BaseProtocol::ptr& protocol):_protocol(protocol),_conn(conn)
{}
virtual void send(const BaseMessage::ptr& msg) override
{
//将BaseMessage的对象转化成string
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;//muduo库中的连接
};
对于抽象类的功能具体实现,因为数据发送走的直接对象是连接,所以我们需要用BaseProtocol协议先序列化一下然后发送
4-4 网络层服务端类
网络层的服务,用于接收muduo::net::TcpConnectionPtr和muduo::net::Buffer,设置回调函数
class MuduoServer:public BaseServer{
public:
using ptr=std::shared_ptr<MuduoServer>;
//初始化_server,设置端口
MuduoServer(int port)
:_server(&_baseloop,muduo::net::InetAddress("0.0.0.0",port)
,"MuduoServer"
,muduo::net::TcpServer::kReusePort)//kReusePort表示端口复用,可以多个进程绑定同一个端口
,_protocol(Protocolfactory::create())
{
_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));//bind函数将函数和参数绑定起来,使得函数可以接受参数
}
virtual void start()override{
_server.start();//启动服务
_baseloop.loop();//开始监听套接字
}
private:
void onConnection(const muduo::net::TcpConnectionPtr& conn)
{
ILOG("连接有新的情况,开始处理,可能是建立或者断开");
if(conn->connected())
{
//如果连接建立成功将连接存放到_conns中
// std::cout<<"连接建立"<<std::endl;
ILOG("MuduoServer中连接建立");
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
{
//如果连接断开,检查连接是否存在conns中存在则删除,并进行回调
// std::cout<<"连接断开"<<std::endl;
ILOG("连接断开");
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(it->first);
}
if(_cb_close)
_cb_close(muduo_conn);
}
}
void onMessage(const muduo::net::TcpConnectionPtr&conn,muduo::net::Buffer*buf,muduo::Timestamp)
{
ILOG("连接有数据的到来,开始处理");
auto base_buf=BufferFactory::create(buf);
while(1)
{
ILOG("检查缓冲区报文是否完全")
if(_protocol->canProcessed(base_buf)==false)
{
if(base_buf->readableSize()>maxDataSize)
{
conn->shutdown();
ELOG("缓冲区中数据过大");
return;
}
break;
}
BaseMessage::ptr msg;
ILOG("将缓冲区中的一个报文转化为BaseMessage的对象")
bool ret=_protocol->onMessage(base_buf,msg);
if(ret==false)
{
conn->shutdown();
ELOG("缓冲区中数据错误");
return;
}
ILOG("根据连接找到对应的BaseConnection对象")
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;
}
//将BaseMessage的对象转化成string
if(_cb_message)
{
ILOG("MuduoServer中使用绑定的函数处理")
_cb_message(base_conn,msg);
}
}
}
private:
const size_t maxDataSize=(1<<16);
BaseProtocol::ptr _protocol;
//用于监听设置在服务端中的套接字包括连接和消息监听的
muduo::net::EventLoop _baseloop;
//用于建立服务的,内部封装listen和bind和accept,并且设置callback函数
muduo::net::TcpServer _server;
//锁住对_conns资源的访问操作
std::mutex _mutex;
//BaseConnection里面的TcpConnectionPtr和BaseMessage::ptr的映射关系
std::unordered_map<muduo::net::TcpConnectionPtr,BaseConnection::ptr>_conns;
};
这里的_baseloop就是muduo::net::TcpServer中用于监听套接字的,当对应的套接字返回const muduo::net::TcpConnectionPtr&conn,muduo::net::Buffer*buf,muduo::Timestamp会顺着bind的绑定到onConnection和onMessage
这里的回调函数很关键:
-
_cb_connection:用于建立连接后,我们该如何继续接下来的操作 -
_cb_close:用于断开连接后,我们该如何继续接下来的操作 -
_cb_message:用于收到消息后,我们该如何继续接下来的操作
4-5 网络层客户端类
客户端类和服务端类会有所不同,客户端有两个线程,主线程负责生产数据,IO 子线程负责搬运数据,而服务端唯一使命就是处理网络请求,所以直接使用一个线程就可以了
class MuduoClient:public BaseClient
{
public:
using ptr=std::shared_ptr<MuduoClient>;
//初始化_client,设置ip和端口
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{
// DLOG("设置回调函数,连接服务器");
// _client.setConnectionCallback(std::bind())
// }
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();//等待连接
DLOG("服务端连接成功");
}
virtual void shutdown()override{
return _client.disconnect();//断开连接
}
//发送Message给服务端
virtual bool send(const BaseMessage::ptr&msg)override{
if(connected()==false)
{
ELOG("连接已断开");
return false;
}
_conn->send(msg);
return true;
}
//返回和服务端的BaseConnection对象
virtual BaseConnection::ptr connection()override{
return _conn;
}
//检查与服务端的连接
virtual bool connected() override
{
return(_conn&&_conn->connected());
}
private:
//连接回调函数如果建立连接则让_client停止阻塞
void onConnection(const muduo::net::TcpConnectionPtr& conn)
{
if(conn->connected())
{
std::cout<<"连接建立"<<std::endl;
_downlatch.countDown();
_conn=Connectionfactory::create(conn,_protocol);
}
else
{
std::cout<<"连接断开"<<std::endl;
_conn.reset();
}
}
//将数据写入到缓冲区当中
void onMessage(const muduo::net::TcpConnectionPtr&conn,
muduo::net::Buffer* buf,muduo::Timestamp)
{
DLOG("链接有数据的到来,开始处理!");
MuduoBuffer::ptr base_buf=BufferFactory::create(buf);
while(1)
{
if(_protocol->canProcessed(base_buf)==false)
{
if(buf->readableBytes()>_maxBufferSize)
{
conn->shutdown();
ELOG("可读字节超过限制");
return;
}
break;
}
//将缓冲区的数据写入BaseMessage
BaseMessage::ptr msg;
bool ret=_protocol->onMessage(base_buf,msg);
if(ret==false)
{
conn->shutdown();
ELOG("缓冲区中数据错误!");
return;
}
//处理回调
if(_cb_message)
{
_cb_message(_conn,msg);
}
}
}
private:
static const int _maxBufferSize=(1<<16);
BaseProtocol::ptr _protocol; //协议,用于将缓冲区中的数据转化为BaseMessage
BaseConnection::ptr _conn; //和服务端的连接
muduo::CountDownLatch _downlatch; //用于等待连接建立
muduo::net::EventLoopThread _loopthread; //用于创建新的线程监听套接字
muduo::net::EventLoop *_baseloop; //用于监听套接字的线程
muduo::net::TcpClient _client; //用于建立连接的客户端
};
这里有很多知识,我们分开来讲述
_loopthread有什么用?
客户端和服务端不一样,客户端需要根据用户的需求生产任务,而服务端不需要,服务端只要设置好任务的处理条件就可以了,处理完之后的结果直接发送给客户端就可以了,所以服务端只需要一个线程一直不断处理任务和发送数据,而客户端需要有两个工作台,一个工作台给客户端生产任务,一个工作台负责自动化接收数据和发送数据
_downlatch是用来做什么的
就像是条件变量一样,当我们使用_downlatch.wait()时,如果_downlatch的值为1时,这个_downlatch.wait()会阻塞进程,防止进程执行得太快,在onConnection的TcpConnectionPtr还没传过来的时候,我们不能往后执行程序send,所以这个是一个保险,确认当客户端和服务端连接完成后才执行数据传输的任务
connect() -> _downlatch.wait() -> onConnection() -> countDown() -> send()
保证连接完成后执行任务
conn->send()并不是在主线程中执行
它这里只是将任务和资源传给了子线程来执行,当主线程执行到了这一块的时候发现conn并不是主线程的,所以会将这份任务传给子线程执行,这里非常复杂,我们来慢慢讲,会比较抽象
_loopthread.startLoop()
这里子线程返回了一个muduo::net::EventLoop给主线程,当主线程使用这个来构造muduo::net::TcpClient后,返回的TcpConnection会带上子线程的ID,所以使用TcpConnection后会识别出不是主线程的,所以会让子线程来执行任务
五、第三层协议与序列化层
5-1 协议类
到这一层其实我们应该明白了,网络层其实就是对于抽象层的一个具体功能实现
class LVProtocol:public BaseProtocol
{
public:
using ptr=std::shared_ptr<LVProtocol>;
virtual bool canProcessed(const BaseBuffer::ptr &buf)override
{
//检查缓冲区中是否有4个字节,如果4个字节都没有那连报文都不知道多长
if(buf->readableSize()<lenFieldsLength)
{
return false;
}
//将前4个字节表示的报文长度返回给total_len,表示这个报文主体的大小
int32_t total_len=buf->peekInt32();
//不仅报文的主体,还需要前面表示大小的前4个字节
if(buf->readableSize()<(total_len+lenFieldsLength))
{
return false;
}
return true;
}
virtual bool onMessage(const BaseBuffer::ptr& buf,BaseMessage::ptr& msg)override
{
int total_len=buf->readInt32();//获取报文总长度
MType mtype=(MType)buf->readInt32();//获取消息类型
int32_t idlen=buf->readInt32();//获取消息id的长度
int32_t body_len=total_len-idlen-idlenFieldsLength-mtypeFieldsLength;//获取消息主体的长度
std::string id=buf->retrieveAsString(idlen);//获取消息id
std::string body=buf->retrieveAsString(body_len);//获取消息主体
msg=MessageFactory::create(mtype);//创建一个消息对象
if(!msg)
{
ELOG("消息类型错误,构造消息对象失败!");
return false;
}
bool ret=msg->unserialize(body);//将body用于构建消息对象
if(!ret)
{
ELOG("消息正文反序列化失败!");
return false;
}
msg->setId(id);//设置消息id
msg->setMType(mtype);//设置消息类型,虽然这个类已经是消息类型,但还是要明确一下的
return true;
}
virtual std::string serialize(const BaseMessage::ptr& msg)override{
std::string body=msg->serialize();//主体消息的大小
std::string id=msg->rid();//取出id
int32_t mtype=htonl((int32_t)msg->mtype());//将mtype转化为网络字节序
int32_t alllen=idlenFieldsLength+mtypeFieldsLength+id.size()+body.size();//计算全部的大小
int32_t nall=htonl(alllen);//将全部的大小转化为网络字节序
int32_t idlen=htonl(id.size());//将id长度的大小计算并转化为网络字节序
std::string result;
result.reserve(alllen+lenFieldsLength);
result.append((char*)&nall,lenFieldsLength);//第一个加全部的大小
result.append((char*)&mtype,mtypeFieldsLength);//第二个家mtype的大小
result.append((char*)&idlen,idlenFieldsLength);//第三个加id的大小
result.append(id);//string无需转化为网络字节序,因为字符只占一个字节
result.append(body);
return result;
}
private:
//消息头部,存储了整个报文的总长度值
const size_t lenFieldsLength=4;
//消息类型放在第二位
const size_t mtypeFieldsLength=4;
//消息ID的长度
const size_t idlenFieldsLength=4;
//只有消息类型和消息ID是不在body中的所以要放到外面来
};
lenFieldsLength存储了除lenFieldsLength以外一个报文的总长度
5-2 消息类
#pragma once
#include"detail.hpp"
#include"fields.hpp"
#include"abstract.hpp"
namespace gchrpc
{
typedef std::pair<std::string,int> Address;
//用于存储Json格式的Message,也可以将保留的Json格式的对象序列化返回
class JsonMessage:public BaseMessage{
public:
using ptr=std::shared_ptr<JsonMessage>;
//Success
virtual std::string serialize() override{
std::string body;
bool ret=JSON::serialize(_body,body);
if(ret==false)
{
return std::string();
}
return body;
}
//Success
virtual bool unserialize(std::string& msg)override
{
return JSON::unserialize(msg,_body);
}
protected:
Json::Value _body;
};
//Json格式的请求,只有Json::Value _body和MType _mtype和std::string _rid;
class JsonRequest:public JsonMessage{
public:
using ptr=std::shared_ptr<JsonRequest>;
};
//返回的消息,返回的消息和请求消息的区别是返回的消息有返回状态码,而请求消息只有请求码
class JsonResponse:public JsonMessage{
public:
using ptr=std::shared_ptr<JsonResponse>;
//Success
virtual bool check()override{
if(_body[KEY_RCODE].isNull()==true)
{
ELOG("响应中无状态码");
return false;
}
if(_body[KEY_RCODE].isIntegral()==false)
{
ELOG("相应状态码类型错误");
}
return true;
}
//Success
virtual Rcode rcode()
{
return (Rcode)_body[KEY_RCODE].asInt();
}
//Success
virtual void setRCode(Rcode rcode){
_body[KEY_RCODE]=(int)rcode;
}
};
//Rpc设置请求Message的参数,而请求系统的方法
class RpcRequest:public JsonRequest{
public:
using ptr=std::shared_ptr<RpcRequest>;
//Success
RpcRequest(){setMType(MType::REQ_RPC);}
//Success
virtual bool check()override{
if(_body[KEY_METHOD].isNull()==true||
_body[KEY_METHOD].isString()==false)
{
ELOG("RPC请求中没有方法名称或方法名称类型错误");
return false;
}
if(_body[KEY_PARAMS].isNull()==true||
_body[KEY_PARAMS].isObject()==false)
{
ELOG("RPC请求中没用参数或参数类型错误");
return false;
}
return true;
}
//Success
std::string method()
{
return _body[KEY_METHOD].asString();
}
//Success
void setMethod(const std::string &method_name)
{
_body[KEY_METHOD]=method_name;
}
//Success
Json::Value params()
{
return _body[KEY_PARAMS];
}
//Success
void setParams(const Json::Value& params)
{
_body[KEY_PARAMS]=params;
}
};
class TopicRequest:public JsonRequest{
public:
using ptr=std::shared_ptr<TopicRequest>;
//Success
TopicRequest(){setMType(MType::REQ_TOPIC);}
//Success
virtual bool check()override{
if(_body[KEY_TOPIC_KEY].isNull()==true||
_body[KEY_TOPIC_KEY].isString()==false)
{
ELOG("主题请求中没用主题名称或主题名称类型错误!");
return false;
}
if(_body[KEY_OPTYPE].isNull()==true||
_body[KEY_OPTYPE].isIntegral()==false)
{
ELOG("主题请求中没有操作类型或操作类型名称类型错误");
return false;
}
if(_body[KEY_OPTYPE].asInt()==(int)TopicOptype::TOPIC_PUBLISH &&
(_body[KEY_TOPIC_MSG].isNull()==true ||
_body[KEY_TOPIC_MSG].isString()==false))
{
ELOG("主题消息发布请求中没有消息内容字段或消息内容类型错误");
return false;
}
return true;
}
//Success
std::string topicKey()
{
return _body[KEY_TOPIC_KEY].asString();
}
//Success
void setTopicKey(const std::string& key)
{
_body[KEY_TOPIC_KEY]=key;
}
//Success
TopicOptype optype()
{
return (TopicOptype)_body[KEY_OPTYPE].asInt();
}
//Success
void setOptype(TopicOptype optype)
{
_body[KEY_OPTYPE]=(int)optype;
}
//Success
std::string topicMsg()
{
return _body[KEY_TOPIC_MSG].asString();
}
//Success
void setTopicMsg(const std::string& msg)
{
_body[KEY_TOPIC_MSG]=msg;
}
};
class ServiceRequest:public JsonRequest{
public:
//Success
ServiceRequest(){setMType(MType::REQ_SERVICE);}
using ptr=std::shared_ptr<ServiceRequest>;
//Success
virtual bool check() override{
if(_body[KEY_METHOD].isNull()==true||
_body[KEY_METHOD].isString()==false)
{
ELOG("服务请求中没有方法名称或方法名称类型错误!");
return false;
}
if(_body[KEY_OPTYPE].isNull()==true||
_body[KEY_OPTYPE].isIntegral()==false)
{
ELOG("服务请求中没有操作类型或操作类型的类型错误!");
return false;
}
if(_body[KEY_OPTYPE].asInt()!=(int)(ServiceOptype::SERVICE_DISCOVERY)&&
(_body[KEY_HOST].isNull()==true||
_body[KEY_HOST].isObject()==false||
_body[KEY_HOST][KEY_HOST_IP].isNull()==true||
_body[KEY_HOST][KEY_HOST_IP].isString()==false||
_body[KEY_HOST][KEY_HOST_PORT].isNull()==true||
_body[KEY_HOST][KEY_HOST_PORT].isIntegral()==false))
{
ELOG("服务请求中主题地址错误!");
return false;
}
return true;
}
//Success
std::string method()
{
return _body[KEY_METHOD].asString();
}
//Success
void setMethod(const std::string& name)
{
_body[KEY_METHOD]=name;
}
//Success
ServiceOptype optype()
{
return (ServiceOptype)_body[KEY_OPTYPE].asInt();
}
//Success
void setOptype(ServiceOptype optype)
{
_body[KEY_OPTYPE]=(int)(optype);
}
//Success
Address host()
{
Address addr;
addr.first=_body[KEY_HOST][KEY_HOST_IP].asString();
addr.second=_body[KEY_HOST][KEY_HOST_PORT].asInt();
return addr;
}
//Success
void setHost(const Address& addr)
{
_body[KEY_HOST][KEY_HOST_IP]=addr.first;
_body[KEY_HOST][KEY_HOST_PORT]=addr.second;
}
};
class RpcResponse:public JsonResponse
{
public:
//Success
RpcResponse(){setMType(MType::RSP_RPC);}
using ptr=std::shared_ptr<RpcResponse>;
//Success
virtual bool check()override{
if(_body[KEY_RCODE].isNull()==true||
_body[KEY_RCODE].isInt()==false)
{
ELOG("响应回复状态码为空,或状态码类型错误");
return false;
}
if(_body[KEY_RESULT].isNull()==true)
{
ELOG("响应结果为空,或响应结果类型错误");
return false;
}
return true;
}
//Success
Json::Value result()
{
return _body[KEY_RESULT];
}
//Success
void setResult(const Json::Value& result)
{
_body[KEY_RESULT]=result;
}
};
class TopicResponse:public JsonResponse
{
public:
using ptr=std::shared_ptr<TopicResponse>;
};
class ServiceResponse:public JsonResponse
{
public:
//Success
ServiceResponse(){setMType(MType::RSP_SERVICE);}
using ptr=std::shared_ptr<ServiceResponse>;
//Success
virtual bool check()override{
if(_body[KEY_RCODE].isNull()==true||
_body[KEY_RCODE].isIntegral()==false)
{
ELOG("响应中没有状态码或者状态码类型错误");
return false;
}
if(_body[KEY_OPTYPE].isNull()==true||
_body[KEY_OPTYPE].isIntegral()==false)
{
ELOG("响应中没有操作类型或者操作类型的类型错误");
return false;
}
if((_body[KEY_OPTYPE]==(int)ServiceOptype::SERVICE_DISCOVERY)&&
(_body[KEY_METHOD].isNull()==true||
_body[KEY_METHOD].isString()==false||
_body[KEY_HOST].isNull()==true||
_body[KEY_HOST].isArray()==false))
{
ELOG("服务发现响应中信息字段错误");
return false;
}
return true;
}
//Success
ServiceOptype optype()
{
return (ServiceOptype)_body[KEY_OPTYPE].asInt();
}
//Success
void setoptype(const ServiceOptype& optype)
{
_body[KEY_OPTYPE]=(int)optype;
}
//Success
std::string method()
{
return _body[KEY_METHOD].asString();
}
//Success
void setMethod(const std::string& method)
{
_body[KEY_METHOD]=method;
}
//Success
void setHost(std::vector<Address>addrs)
{
for(auto& addr:addrs)
{
Json::Value newValue;
newValue[KEY_HOST_IP]=addr.first;
newValue[KEY_HOST_PORT]=addr.second;
_body[KEY_HOST].append(newValue);
}
}
//Success
std::vector<Address> hosts()
{
// std::vector<Address> address(0);
int sz=_body[KEY_HOST].size();
std::vector<Address> address(sz);
for(int i=0;i<sz;i++)
{
address[i].first=_body[KEY_HOST][i][KEY_HOST_IP].asString();
address[i].second=_body[KEY_HOST][i][KEY_HOST_PORT].asInt();
}
return address;
}
};
class MessageFactory{
public:
//Success返回基类
static BaseMessage::ptr create(MType mtype)
{
switch (mtype)
{
case MType::REQ_RPC:
return std::make_shared<RpcRequest>();
case MType::RSP_RPC:
return std::make_shared<RpcResponse>();
case MType::REQ_TOPIC:
return std::make_shared<TopicRequest>();
case MType::RSP_TOPIC:
return std::make_shared<TopicResponse>();
case MType::REQ_SERVICE:
return std::make_shared<ServiceRequest>();
case MType::RSP_SERVICE:
return std::make_shared<ServiceResponse>();
}
return BaseMessage::ptr();
}
//Success返回派生类
template<typename T,typename ...Args>
static std::shared_ptr<T> create(Args&& ...args)
{
return std::make_shared<T>(std::forward<Args>(args)...);
}
};
}
下面是各个消息的继承关系,这里的回复要比请求多一个返回码KEY_CODE,每次服务器处理完后返回客户端返回结果

这张图能直观表示各种消息_body结构体的区别,最重要的还是这个工厂类的讲解
template<typename T,typename ...Args>
static std::shared_ptr<T> create(Args&& ...args)
{
return std::make_shared<T>(std::forward<Args>(args)...);
}
这个模板类的作用是:不管调用者传了几个参数,也不管传的是左值还是右值,我这个中间商(Factory)都要一模一样地传给 T 的构造函数,绝不产生多余的拷贝
其中的两个核心技术点:可变参数模板 和 完美转发
typename ...Args和Args&& ...args
Args 是一个参数包,它表示任意数量、任意类型的参数
有的类构造函数不需要参数:new T(),有的需要一个 int:new T(10),有的需要 string 和 bool:new T("hello", true),工厂方法 create 为了能通吃所有情况,必须使用 ...Args 来打包所有参数
Args&&:这里的 && 不完全等于右值引用,当 && 用在推导类型(模板参数 T&& 或 Args&&)时,它叫万能引用,如果你传进来的是左值(比如 int a = 1; create(a)),Args&& 会退化为 int&,如果你传进来的是右值(比如 create(10)),Args&& 会变成 int&&,它像一个“变色龙”,能准确捕捉到你传进来的参数到底是临时的(右值)还是持久的(左值)
std::forward<Args>(args)...
这部分解决了**“性质保留”**的问题,是这段代码的灵魂,参数有了名字就变成左值了
请看这个悖论:
template<typename T>
void wrapper(T&& arg) {
// 假设调用 wrapper(10),arg 是右值引用。
// 但是!在 wrapper 函数内部,arg 这个变量本身是有名字的。
// 在 C++ 中,只要有名字的变量,它就是左值!
new Object(arg); // 糟糕!这里把 arg 当作左值传过去了!
}
如果你传了一个临时对象(右值)给工厂,工厂转手传给构造函数时,却变成左值了。构造函数就会调用拷贝构造,而不是高效的移动构造。性能损失巨大
std::forward就像一个时光倒流机或者类型还原器,如果 Args 推导出来当初是个右值,那我就把 args 强制转回右值;如果当初是左值,我就不动它,
... (包展开)
std::forward<Args>(args)... 最后的那个 ... 是包展开
它的意思是:对 Args 包里的每一个参数,都执行一遍 std::forward
例如,如果你调用 create(a, 10):编译器展开为std::make_shared<T>(std::forward<TypeA>(a), std::forward<TypeB>(10))
1860

被折叠的 条评论
为什么被折叠?



