手写一个 RPC 远程调用(C++)
版权声明
个人作品,禁止转载
参考资料
- Build Remote Procedure Calls (RPC) - from scratch in C(https://www.udemy.com/course/linuxrpc/)
- Linux manual page(https://man7.org/linux/man-pages/)
- Cpp reference(https://en.cppreference.com/w/)
代码下载
https://github.com/541380000/write_rpc_from_scratch.git
或者从网盘下载
链接:https://pan.baidu.com/s/1BjTK3aH_T80umXsF9Yzqmg?pwd=5mof
提取码:5mof
推荐进阶工具
- protobuf(对象序列化工具,按字节序列化)
- msgpack(对象序列化工具,类似 json,但更加精简)
- Boost 的序列化库
Overview
- 这是一个学习型项目,旨在学习 RPC 如何运行
- 这是一个 C++项目,只是用 C++标准库,不使用第三方库
- 各个步骤纯手写,代码无法用于商业项目
- 本项目不考虑机器大小端
预备知识
- 基础的 c/c++知识
- 基础的 socket 编程
- 基础的 debug 能力
RPC 的步骤
- RPC 客户端,序列化 RPC 头和 RPC 参数
- RPC 客户端,将 RPC 头和参数打包成数据包,通过网络(等其他通信手段)发送
- RPC 服务器,反序列化 RPC 头和 RPC 参数
- RPC 服务器,执行远程调用
- RPC 服务器,将 RPC 头和返回值打包成数据包,发送给客户端
- RPC 客户端,反序列化 RPC 头和返回值,得到 RPC 结果
序列化和反序列化
基础数据类型:SerBuffer
结构体定义
SerBuffer是数据序列化和反序列化的缓冲区
```
using byte=char;
struct SerBuffer{
vector<byte> buffer;
};
```
方法
创建新的 SerBuffer,预留若干字节的空间
unique_ptr<SerBuffer> creatSerBuffer(const uint32_t buffer_size=DEFAULT_INIT_SERIALIZE_BUFFER_SIZE){
auto ptr = make_unique<SerBuffer>();
ptr->buffer.reserve(buffer_size);
return move(ptr);
}
将 nBytes 的数据复制到 buffer 中
void serializeData(const unique_ptr<SerBuffer>& buffer, const byte* data, const uint32_t nBytes){
copy(data, data+nBytes, back_inserter(buffer->buffer));
}
跳过 n 字节,nBytes 为负则从缓冲区删除数据,为正则追加 nBytes 个 0
void serializeBufferSkip(const unique_ptr<SerBuffer>& buffer, int32_t nBytes){
if (nBytes < 0)
while(nBytes < 0) buffer->buffer.pop_back(), nBytes ++;
else if (nBytes > 0)
while(nBytes > 0) buffer->buffer.emplace_back('0'), nBytes --;
}
清空缓冲区
void resetSerBuffer(const unique_ptr<SerBuffer>& buffer){
buffer->buffer.clear();
}
填充一个空指针,空指针用 0xFFFFFFFF 表示
void fillNull(const unique_ptr<SerBuffer>& buffer){
uint32_t sentinel = 0xFFFFFFFF;
serializeData(buffer, (byte*)&sentinel, 4);
}
将 RPC 头序列化到 buffer 中(RPC 头定义见下文)
void serializeRPCHeader(const unique_ptr<SerBuffer>& buffer, const RPCHeader* const header){
serializeData(buffer, (char*)header, sizeof(header));
}