目前仅支持Linux
操作系统,之后考虑添加Windows
系统的支持。
首先定义了BaseClient
和BaseServer
两个类,实现进程间数据(std::string
)传输(文本
、二进制
均可),使用的是UDP
协议。
- 进程
A
启动BaseServer
作为服务端。 - 进程
B
通过BaseClient
向进程A
发送数据。 BaseServer
不能主动发送数据给BaseClient
,但是可以在接收到数据后,返回响应数据(Response)。- 如果需要完全双向通信,需要两个进程都同时启用
BaseServer
和BaseClient
。
在不超出机器物理内存限制的情况下,单次通信的数据大小不受限制。数据量较大时BaseClient
会自动拆分多个包发送,BaseServer
收到后会进行组包。
在此基础上,实现了基于JSON
格式的Client
和Server
,功能上类似于HTTP
协议的POST
请求。通过Router
路由功能,可以快速实现对不同请求路径的处理。
BaseServer
的QPS
可达100,000+
,即每秒钟处理10万
次请求并作出响应。
.
├── bin // 编译后的示例程序
├── example // 示例程序代码
│ ├── benchmark
│ ├── echo_server
│ ├── file_transfer
│ ├── simple_json_server
│ ├── uds_base_cli
│ └── uds_json_cli
├── lib
│ └── linux
│ ├── libjsoncpp.a // jsoncpp静态库
│ └── release
│ ├── libuds_base.a // src/uds/base编译生成的静态库
│ └── libuds_json.a // src/uds/json编译生成的静态库
├── README.md
├── src
│ └── uds
│ ├── base
│ │ ├── base_client.cpp
│ │ ├── base_client.h // 基础的客户端
│ │ ├── base_server.cpp
│ │ ├── base_server.h // 基础的服务端
│ │ ├── error_code.cpp
│ │ ├── error_code.h // 错误代码定义
│ │ └── impl // 实现类
│ │ └── ...
│ └── json // 在src/uds/base基础上封装的基于json(可携带二进制数据)的通信
│ ├── client.cpp
│ ├── client.h
│ ├── message.cpp
│ ├── message.h
│ ├── request.cpp
│ ├── request.h
│ ├── response.cpp
│ ├── response.h
│ ├── router.cpp
│ ├── router.h
│ ├── server.cpp
│ └── server.h
├── third_party // 第三方库
│ └── jsoncpp
│ └── json
└── xmake.lua
分为两部分:
BaseServer
: 服务端BaseClient
: 客户端
通信数据不限制
大小,超过64KB
会自动分包发送,服务端收到后会组包。
客户端发送数据给服务端,可以等待响应,也可以不等待响应。
/**
* @brief 仅发送数据,不等待服务器返回响应.
*
* @param data 待发送的数据
* @param ec 错误代码
* @return 当前请求的ID
* @note 通过 ec 判断是否成功
*/
int64_t Send(const std::string& data, std::error_code& ec);
/**
* @brief 发送数据,等待服务器返回响应.
*
* @param data 待发送的数据
* @param response 服务器响应数据
* @param timeout_ms 超时时间,单位:毫秒
* @param ec 错误代码
* @return 当前请求的ID
* @note 通过 ec 判断是否成功
*/
int64_t SendRequest(const std::string& data, std::string* response, uint32_t timeout_ms, std::error_code& ec);
服务端设置请求回调函数。
/**
* @brief 接收到完成数据后的回调函数.
*/
using RequestCallback = std::function<void(
BaseServer* base_server, /* 当前服务器指针 */
const sockaddr_un& client_addr, /* 来源客户端地址 */
int64_t request_id, /* 请求ID,由客户端保证每次发送的请求ID是唯一的 */
const std::string& data /* 请求数据 */
)>;
void set_request_callback(RequestCallback callback);
服务端处理请求后,调用SendResponse(...)
函数返回响应。
/**
* @brief 返回给客户端响应数据.
*
* @param client_addr 客户端地址
* @param request_id 客户端的请求ID
* @param data 响应内容
*/
bool SendResponse(const sockaddr_un& client_addr, int64_t request_id, const std::string& data);
参考example/echo_server
目录下的示例代码。
实现类似于HTTP
协议的POST
请求功能。
协议格式:
命名空间ic::uds
,二者都继承自Message
基类。
Client
发送Request
,接收Response
Server
接收Request
,返回Response
以协议格式
中的请求为例:
// 1. 创建请求
ic::uds::Request request("/image/Upload");
// 或 request.set_path("/image/Upload");
// 2. 设置请求参数(可选)
request["uid"] = 1001;
request["ext"] = "jpg";
request["overwrite"] = true;
// 3. 添加body数据体(可选)
request.AddBody("original_image", read_image("./image/original_image.jpg"));
request.AddBody("thumbnail", read_image("./image/thumbnail.jpg"));
// 4. 发送请求,并接收响应
auto response = client.SendRequest(request, 3000);
// 5. 查看响应内容
std::cout << "Response.status: " << (int)response.status() << std::endl;
std::cout << "Response.message: " << response.message() << std::endl;
std::cout << "Response.content: " << fw.write(response.data()) << std::endl << std::endl;
以协议格式
中的请求为例:
// 1. 创建服务器
auto server = std::make_shared<ic::uds::Server>();
// 2. 初始化服务器
std::error_code ec;
server->Init("/dev/shm/server.sock", 4, ec); // 线程池大小设为4
if (ec) {
printf("[Error] UDS.Server init failed. %s\n", ec.message().c_str());
return 1;
}
// 3. 定义路由
ic::uds::Router* router = server->router();
router->AddRoute("/image/Upload", "图片上传", [](ic::uds::Request& req, ic::uds::Response& res){
// 3.1 读取请求参数
int uid = req["uid"].asInt();
std::string ext = req["ext"].asString();
bool overwrite = req["overwrite"].asBool();
// 3.2 读取数据体
const std::string& original_image = req.GetBody("original_image"); /* 原始图片文件数据 */
const std::string& thumbnail = req.GetBody("thumbnail"); /* 缩略图文件数据 */
// 3.3 保存文件,计算md5,计算base64
// ...
// 3.4 返回响应
res["code"] = 0;
res["msg"] = "OK";
res["data"]["md5"] = md5;
res.body()["image_base64"] = image_base64;
});
// 4. 启动服务器
// 阻塞在这里,也可以通过std::thread放到其他线程启动
server->Start();