本代码来自GitHub - button-chen/buttonrpc: Tiny and simple c++ rpc library,非常简单的rpc代码,总共就两个cpp文件,代码量只有3,4百行。这里简单分析一下。
StreamBuffer
class StreamBuffer : public vector<char>{
public:
StreamBuffer(){ m_curpos = 0; }
StreamBuffer(const char* in, size_t len){
m_curpos = 0;
insert(begin(), in, in+len);
}
~StreamBuffer(){ };
void reset(){ m_curpos = 0; }
const char* data(){ return &(*this)[0]; }
const char* current(){ return&(*this)[m_curpos]; }
void offset(int k){ m_curpos += k; }
bool is_eof(){ return (m_curpos >= size()); }
void input( char* in, size_t len){ insert(end(), in, in+len); }
int findc(char c){
iterator itr = find(begin()+m_curpos, end(), c);
if (itr != end()) {
return itr - (begin()+m_curpos);
}
return -1;
}
private:
// 当前字节流位置
unsigned int m_curpos;
};
看着很复杂,就是一个string buffer,起始也是没有明白为什么要继承,直接包含一个不就好了。
Serializer
class Serializer{
public:
Serializer() { m_byteorder = LittleEndian; };
~Serializer(){ };
Serializer(StreamBuffer dev, int byteorder=LittleEndian){
m_byteorder = byteorder;
m_iodevice = dev;
}
public:
enum ByteOrder {
BigEndian,
LittleEndian
};
public:
// 这就是一些简单的设置buffer的方法
void reset(){
m_iodevice.reset();
}
int size(){
return m_iodevice.size();
}
void skip_raw_date(int k){
m_iodevice.offset(k);
}
const char* data(){
return m_iodevice.data();
}
void byte_orser(char* in, int len){
if (m_byteorder == BigEndian){
reverse(in, in+len);
}
}
void write_raw_data(char* in, int len){
m_iodevice.input(in, len);
m_iodevice.offset(len);
}
const char* current(){
return m_iodevice.current();
}
void clear(){
m_iodevice.clear();
reset();
}
// 关键的方法在这
template<typename T>
void output_type(T& t);
template<typename T>
void input_type(T t);
// 直接给一个长度, 返回当前位置以后x个字节数据
void get_length_mem(char* p, int len){
memcpy(p, m_iodevice.current(), len);
m_iodevice.offset(len);
}
public:
// 两个流操作符的重载是为了序列化数据
template<typename T>
Serializer &operator >> (T& i){
output_type(i);
return *this;
}
template<typename T>
Serializer &operator << (T i){
input_type(i);
return *this;
}
private:
int m_byteorder; // 记录是大端存储还是小端存储
StreamBuffer m_iodevice; // 序列化的字节流
};
// output_type模板定义,反序列化
// 根据类型大小,将buffer大小的数据拷贝到数据上
// 只能针对基本类型,以及纯struct的数据结构
template<typename T>
inline void Serializer::output_type(T& t){
int len = sizeof(T);
char* d = new char[len];
if (!m_iodevice.is_eof()){
memcpy(d, m_iodevice.current(), len);
m_iodevice.offset(len);
byte_orser(d, len);
t = *reinterpret_cast<T*>(&d[0]);
}
delete [] d;
}
// output_type的偏特化
// 针对string类,string类不能拷贝其大小,而是需要拷贝数据
// 在对string序列化的时候,第一个uint16,2字节,存放的是string的长度
// 接下来才是数据
template<>
inline void Serializer::output_type(std::string& in){
int marklen = sizeof(uint16_t);
char* d = new char[marklen];
memcpy(d, m_iodevice.current(), marklen);
byte_orser(d, marklen);
int len = *reinterpret_cast<uint16_t*>(&d[0]);
m_iodevice.offset(marklen);
delete [] d;
if (len == 0) return;
in.insert(in.begin(), m_iodevice.current(), m_iodevice.current() + len);
m_iodevice.offset(len);
}
// 将数据序列化
template<typename T>
inline void Serializer::input_type(T t){
int len = sizeof(T);
char* d = new char[len];
const char* p = reinterpret_cast<const char*>(&t);
memcpy(d, p, len);
byte_orser(d, len);
m_iodevice.input(d, len);
delete [] d;
}
template<>
inline void Serializer::input_type(std::string in){
// 先存入字符串长度
uint16_t len = in.size();
char* p = reinterpret_cast< char*>(&len);
byte_orser(p, sizeof(uint16_t));
m_iodevice.input(p, sizeof(uint16_t));
// 存入字符串
if (len == 0) return;
char* d = new char[len];
memcpy(d, in.c_str(), len);
m_iodevice.input(d, len);
delete [] d;
}
template<>
inline void Serializer::input_type(const char* in){
input_type<std::string>(std::string(in));
}
接下来才是这个简单的rpc的定义和方法,对源码做了点修改,读起来更加方便一些。
一些简单的数据定义
// 如果不是void类型,就保留
template<typename T>
struct type_xx{ typedef T type; };
// 如果是void类型,就转换成uint8类型
template<>
struct type_xx<void>{ typedef uint8_t type; };
// 枚举类
enum rpc_statue{
RPC_ERR_SUCCESS = 0,
RPC_ERR_FUNCTIION_NOT_BIND,
RPC_ERR_RECV_TIMEOUT
};
template<typename T>
class value_t{
public:
typedef typename type_xx<T>::type type;
typedef std::string msg_type;
typedef uint16_t code_type;
value_t() : code_(0) { msg_.clear(); }
bool valid() { return (code_ == 0 ? true : false); }
int statue() { return code_; }
std::string statue_msg() { return msg_; }
type val() {return val_; }
void set_val(const type& val) { val_ = val; }
void set_msg(msg_type msg) { msg_ = msg; }
void set_code(code_type code) { code_ = code; }
Serializer& operator>>(Serializer& in, value_t<T>& d){
in >> d.code_ >> d.msg_;
if(d.code_ == 0){
in >> d.val_;
}
return in;
}
Serializer& operator<<(Serializer& out, value_t<T> d){
out << d.code_ << d.msg_ << d.val_;
return out;
}
private:
code_type code_;
msg_type msg_;
type val_;
};
// rpc服务端应答客户端的传递消息的结构体
/*
code_:用来记录本次消息的状态,是成功,或者客户端请求的方法不存在,或者客户端超时
msg_:消息的内容,用string保存
val_:保存消息的类型,可以说是调用方法的返回值
*/
template<typename T>
class value_t{
public:
typedef typename type_xx<T>::type type;
typedef std::string msg_type;
typedef uint16_t code_type;
value_t() : code_(0) { msg_.clear(); }
bool valid() { return (code_ == 0 ? true : false); }
int statue() { return code_; }
std::string statue_msg() { return msg_; }
type val() {return val_; }
void set_val(const type& val) { val_ = val; }
void set_msg(msg_type msg) { msg_ = msg; }
void set_code(code_type code) { code_ = code; }
Serializer& operator>>(Serializer& in, value_t<T>& d){
in >> d.code_ >> d.msg_;
if(d.code_ == 0){
in >> d.val_;
}
return in;
}
Serializer& operator<<(Serializer& out, value_t<T> d){
out << d.code_ << d.msg_ << d.val_;
return out;
}
private:
code_type code_;
msg_type msg_;
type val_;
};
rpcclient代码
在客户端和服务端都使用了zeromq,但是这其实值提供了tcp连接和发送接收消息的功能,没用到什么深奥的方法,其实也可以手错一个简单的tcp客户服务器。
客户端的代码,主要是连接服务端,然后调用方法,获取调用方法的返回值。client方法很简单。
// client
class rpcclient{
public:
rpcclient(): statue_(RPC_ERR_SUCCESS), m_context(1) {}
rpcclient(std::string ip, int port) : ip_(ip), port_(port), statue_(RPC_ERR_SUCCESS), m_context(1) {}
~rpcclient() {
m_socket->close();
delete m_socket;
m_context.close();
}
// zeromq的调用流程
bool init();
void send(zmq::message_t& data);
void recv(zmq::message_t& data);
void set_tiemout(uint32_t ms);
// 一些简单的set get方法
bool set_ip(std::string ip) { ip_ = ip; }
std::string get_ip() {return ip_; }
bool set_port(int port) { port_ = port; }
int get_port() {return port_; }
// 主要是这call方法
// 虽然看起来很多,起始是针对调用函数需要参数的数量
// 其实感觉可以使用边参数的模板,没必要这样,后面再写
template<typename R>
value_t<R> call(std::string name);
template<typename R, typename P1>
value_t<R> call(std::string name, P1); // 一个参数
template<typename R, typename P1, typename P2>
value_t<R> call(std::string name, P1, P2);
template<typename R, typename P1, typename P2, typename P3>
value_t<R> call(std::string name, P1, P2, P3);
template<typename R, typename P1, typename P2, typename P3, typename P4>
value_t<R> call(std::string name, P1, P2, P3, P4);
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
value_t<R> call(std::string name, P1, P2, P3, P4, P5);
private:
// 这个call是真正的网络调用
template<typename R>
value_t<R> net_call(Serializer& ds);
std::string ip_; // 服务端地址
int port_; // 服务端ip
zmq::context_t* m_context;
zmq::socket_t* m_socket;
rpc_statue statue_;
};
// 初始化socket
bool rpcclient::init(){
m_sokcet = new zmq::socket_t(m_context, ZMQ_REQ);
ostringstream os;
os << "tcp://" << ip_ << ":" << port_;
m_socket->connect(os.str());
}
// 发送消息
void rpcclient::send( zmq::message_t& data ){
m_socket->send(data);
}
// 接收消息
void rpccl;ient::recv(zmq::message_t& data){
m_socket->recv(&data);
}
// 设置超时时间
inline void rpcclient::set_timeout(uint32_t ms){
m_socket->setsockopt(ZMQ_RCVTIMEO, ms);
}
// 将消息发送过去,等待对方回复
template<typename R>
inline value_t<R> rpcserver::net_call(Serializer& ds){
zmq::messge_t request(ds.size() + 1);
memcpy(request.data(), ds.data(), ds.size());
if(statue_ != RPC_ERR_RECV_TIMEOUT){
send(request);
}
zmq::message_t reply;
recv(reply);
value_t<R> val;
if(reply.size() == 0){
statue_ = RPC_ERR_RECV_TIMEOUT;
val.set_code();
val.set_msg("recv timeout");
return val;
}
statue_ = RPC_ERR_SUCCESS;
ds.clear();
ds.write_raw_data((char*)reply.data(), reply.size());
ds.reset();
ds >> val;
return val;
}
// 将调用函数名字和参数序列化
template<typename R>
value_t<R> rpcserver::call(std::string name){
Serializer ds;
ds << name;
return net_call<R>(ds);
}
template<typename R, typename P1>
value_t<R> rpcserver::call(std::string name, P1 p1){
Serializer ds;
ds << name << p1;
return net_call<R>(ds);
}
template<typename R, typename P1, typename P2>
value_t<R> rpcserver::call(std::string name, P1 p1, P2 p2){
Serializer ds;
ds << name << d1 << d2;
return net_call<R>(ds);
}
template<typename R, typename P1, typename P2, typename P3>
value_t<R> call(std::string name, P1 p1, P2 p2, P3 p3){
Serializer ds;
ds << name << d1 << d2 << d3;
return net_call<R>(ds);
}
template<typename R, typename P1, typename P2, typename P3, typename P4>
value_t<R> call(std::string name, P1 p1, P2 p2, P3 p3, P4 p4){
Serializer ds;
ds << name << d1 << d2 << d3 << d4;
return net_call<R>(ds);
}
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
value_t<R> call(std::string name, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5){
Serializer ds;
ds << name << d1 << d3 << d4 << d5;
return net_call<R>(ds);
}
rpcserver实现
重点在服务端,服务端如何解码客户端的消息,服务端如何调用合适函数,如何将结果返回个客户端。
// server
class rpcserver{
public:
rpcserver() : m_context(1),statue_(RPC_ERR_SUCCESS) {}
rpcserver(std::string ip, int port) : ip_(ip), port(port_), m_context(1),statue_(RPC_ERR_SUCCESS) {}
~rpcserver();
// 服务端启动流程
void init();
void send(zmq::message_t& data);
void recv(zmq::message_t& data);
void run();
// 服务端绑定函数,是对客户端提供的函数方法
template<typename F>
void bind(std::string name, F func);
template<typename F, typename S>
void bind(std::string name, F func, S* s);
private:
// 调用函数
Serializer* call_(std::string name, const char* data, int len);
// 代理模式,看着很复杂,其实就是应为提供的方法很简单,所以用了一种全部展开的方法
template<typename F>
void callproxy(F fun, Serialzer* pr, const char* data, int len);
template<typename R, typename P1>
void callproxy(F fun, S* s, Serialzer* pr, const char* data, int len);
// PROXY FUNCTION POINT
// 这些方法最终调用的都是下面PORXY FUNCTIONAL模板函数
template<typename R>
void callproxy_(R(*fun)(), Serialzer* pr, const char* data, int len){
callproxy_(std::function<R()>(func), pr, data, len);
}
template<typename R, typename P1>
void callproxy_(R(*func)(P1), Serialzer* pr, const char* pr, int len){
callproxy_(std::function<R(P1)>(func), pr, data, len);
}
template<typename R, typename P1, typename P2>
void callproxy_(R(*func)(P1, P2), Serializer* pr, const char* data, int len) {
callproxy_(std::function<R(P1, P2)>(func), pr, data, len);
}
template<typename R, typename P1, typename P2, typename P3>
void callproxy_(R(*func)(P1, P2, P3), Serializer* pr, const char* data, int len) {
callproxy_(std::function<R(P1, P2, P3)>(func), pr, data, len);
}
template<typename R, typename P1, typename P2, typename P3, typename P4>
void callproxy_(R(*func)(P1, P2, P3, P4), Serializer* pr, const char* data, int len) {
callproxy_(std::function<R(P1, P2, P3, P4)>(func), pr, data, len);
}
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
void callproxy_(R(*func)(P1, P2, P3, P4, P5), Serializer* pr, const char* data, int len) {
callproxy_(std::function<R(P1, P2, P3, P4, P5)>(func), pr, data, len);
}
// PROXY CLASS MEMBER
// 这些方法最终调用的都是下面PORXY FUNCTIONAL模板函数
template<typename R, typename C, typename S>
void callproxy_(R(C::* func)(), S* s, Serializer* pr, const char* data, int len) {
callproxy_(std::function<R()>(std::bind(func, s)), pr, data, len);
}
template<typename R, typename C, typename S, typename P1>
void callproxy_(R(C::* func)(P1), S* s, Serializer* pr, const char* data, int len) {
callproxy_(std::function<R(P1)>(std::bind(func, s, std::placeholders::_1)), pr, data, len);
}
template<typename R, typename C, typename S, typename P1, typename P2>
void callproxy_(R(C::* func)(P1, P2), S* s, Serializer* pr, const char* data, int len) {
callproxy_(std::function<R(P1, P2)>(std::bind(func, s, std::placeholders::_1, std::placeholders::_2)), pr, data, len);
}
template<typename R, typename C, typename S, typename P1, typename P2, typename P3>
void callproxy_(R(C::* func)(P1, P2, P3), S* s, Serializer* pr, const char* data, int len) {
callproxy_(std::function<R(P1, P2, P3)>(std::bind(func, s,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), pr, data, len);
}
template<typename R, typename C, typename S, typename P1, typename P2, typename P3, typename P4>
void callproxy_(R(C::* func)(P1, P2, P3, P4), S* s, Serializer* pr, const char* data, int len) {
callproxy_(std::function<R(P1, P2, P3, P4)>(std::bind(func, s,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)), pr, data, len);
}
template<typename R, typename C, typename S, typename P1, typename P2, typename P3, typename P4, typename P5>
void callproxy_(R(C::* func)(P1, P2, P3, P4, P5), S* s, Serializer* pr, const char* data, int len) {
callproxy_(std::function<R(P1, P2, P3, P4, P5)>(std::bind(func, s,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)), pr, data, len);
}
// PORXY FUNCTIONAL
template<typename R>
void callproxy_(std::function<R()>, Serializer* pr, const char* data, int len);
template<typename R, typename P1>
void callproxy_(std::function<R(P1)>, Serializer* pr, const char* data, int len);
template<typename R, typename P1, typename P2>
void callproxy_(std::function<R(P1, P2)>, Serializer* pr, const char* data, int len);
template<typename R, typename P1, typename P2, typename P3>
void callproxy_(std::function<R(P1, P2, P3)>, Serializer* pr, const char* data, int len);
template<typename R, typename P1, typename P2, typename P3, typename P4>
void callproxy_(std::function<R(P1, P2, P3, P4)>, Serializer* pr, const char* data, int len);
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
void callproxy_(std::function<R(P1, P2, P3, P4, P5)>, Serializer* pr, const char* data, int len);
private:
std::string ip_;
int port_;
std::map<std::string, std::function<void(Serializer*, const char*, int)>> m_handlers;
zmq::context_t m_context;
zmq::socket_t* m_socket;
rpc_statue statue_;
};
rpcserver::~rpcserver(){
m_socket->close();
delete m_socket;
m_context.close();
}
void rpcserver::init(){
m_socket = new zmq::socket_t(m_context, ZMQ_REP);
ostringstream os;
os << "tcp://*:" << port;
m_socket->bind (os.str());
}
void rpcserver::send( zmq::message_t& data ){
m_socket->send(data);
}
void rpcserver::recv( zmq::message_t& data ){
m_socket->recv(&data);
}
void rpcserver::run(){
// only server can call
while (1){
zmq::message_t data;
recv(data);
StreamBuffer iodev((char*)data.data(), data.size());
Serializer ds(iodev);
std::string funname;
ds >> funname;
Serializer* r = call_(funname, ds.current(), ds.size()- funname.size());
zmq::message_t retmsg (r->size());
memcpy (retmsg.data (), r->data(), r->size());
send(retmsg);
delete r;
}
}
Serializer* rpcserver::call_(std::string name, const char* data, int len){
Serializer* ds = new Serializer();
if (m_handlers.find(name) == m_handlers.end()) {
(*ds) << value_t<int>::code_type(RPC_ERR_FUNCTIION_NOT_BIND);
(*ds) << value_t<int>::msg_type("function not bind: " + name);
return ds;
}
auto fun = m_handlers[name];
fun(ds, data, len);
ds->reset();
return ds;
}
template<typename F>
void rpcserver::bind(std::string name, F func){
m_handlers[name] = std::bind(&rpcserver::callproxy<F>, this, func, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)
}
template<typename F, typename S>
inline void rpcserver::bind(std::string name, F func, S* s){
m_handlers[name] = std::bind(&rpcserver::callproxy<F, S>, this, func, s, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
}
// 调用impl
template<typename F>
void rpcserver::callproxy( F fun, Serializer* pr, const char* data, int len ){
callproxy_(fun, pr, data, len);
}
template<typename F, typename S>
inline void rpcserver::callproxy(F fun, S* s, Serializer* pr, const char* data, int len){
callproxy_(fun, s, pr, data, len);
}
// 为了解决函数返回值的问题,由于返回void的函数没有返回值,所以这里只能代理
// 让void函数返回(uint8)0
// 如果是void类型,就返回0
template<typename R, typename F>
typename std::enable_if<std::is_same<R, void>::value, typename type_xx<R>::type>::type call_helper(F f) {
f();
return 0;
}
// 如果不是void类型,就返回原函数
template<typename R, typename F>
typename std::enable_if<!std::is_same<R, void>::value, typename type_xx<R>::type >::type call_helper(F f) {
return f();
}
template<typename R>
void rpcserver::callproxy_(std::function<R()> func, Serializer* pr, const char* data, int len){
typename type_xx<R>::type r = call_helper<R>(std::bind(func));
value_t<R> val;
val.set_code(RPC_ERR_SUCCESS);
val.set_val(r);
(*pr) << val;
}
template<typename R, typename P1>
void rpcserver::callproxy_(std::function<R(P1)> func, Serializer* pr, const char* data, int len){
Serializer ds(StreamBuffer(data, len));
P1 p1;
ds >> p1;
typename type_xx<R>::type r = call_helper<R>(std::bind(func, p1));
value_t<R> val;
val.set_code(RPC_ERR_SUCCESS);
val.set_val(r);
(*pr) << val;
}
template<typename R, typename P1, typename P2>
void rpcserver::callproxy_(std::function<R(P1, P2)> func, Serializer* pr, const char* data, int len ){
Serializer ds(StreamBuffer(data, len));
P1 p1; P2 p2;
ds >> p1 >> p2;
typename type_xx<R>::type r = call_helper<R>(std::bind(func, p1, p2));
value_t<R> val;
val.set_code(RPC_ERR_SUCCESS);
val.set_val(r);
(*pr) << val;
}
template<typename R, typename P1, typename P2, typename P3>
void rpcserver::callproxy_(std::function<R(P1, P2, P3)> func, Serializer* pr, const char* data, int len){
Serializer ds(StreamBuffer(data, len));
P1 p1; P2 p2; P3 p3;
ds >> p1 >> p2 >> p3;
typename type_xx<R>::type r = call_helper<R>(std::bind(func, p1, p2, p3));
value_t<R> val;
val.set_code(RPC_ERR_SUCCESS);
val.set_val(r);
(*pr) << val;
}
template<typename R, typename P1, typename P2, typename P3, typename P4>
void rpcserver::callproxy_(std::function<R(P1, P2, P3, P4)> func, Serializer* pr, const char* data, int len){
Serializer ds(StreamBuffer(data, len));
P1 p1; P2 p2; P3 p3; P4 p4;
ds >> p1 >> p2 >> p3 >> p4;
typename type_xx<R>::type r = call_helper<R>(std::bind(func, p1, p2, p3, p4));
value_t<R> val;
val.set_code(RPC_ERR_SUCCESS);
val.set_val(r);
(*pr) << val;
}
template<typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
void rpcserver::callproxy_(std::function<R(P1, P2, P3, P4, P5)> func, Serializer* pr, const char* data, int len){
Serializer ds(StreamBuffer(data, len));
P1 p1; P2 p2; P3 p3; P4 p4; P5 p5;
ds >> p1 >> p2 >> p3 >> p4 >> p5;
typename type_xx<R>::type r = call_helper<R>(std::bind(func, p1, p2, p3, p4, p5));
value_t<R> val;
val.set_code(RPC_ERR_SUCCESS);
val.set_val(r);
(*pr) << val;
}