initial commit

This commit is contained in:
2025-08-05 15:53:44 +08:00
commit 09dc02ae52
553 changed files with 137665 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
#ifndef HV_PROTO_RPC_HANDLER_CALC_H_
#define HV_PROTO_RPC_HANDLER_CALC_H_
#include "../router.h"
#include "../generated/calc.pb.h"
void calc_add(const protorpc::Request& req, protorpc::Response* res) {
// params
if (req.params_size() != 2) {
return bad_request(req, res);
}
protorpc::CalcParam param1, param2;
if (!param1.ParseFromString(req.params(0)) ||
!param2.ParseFromString(req.params(1))) {
return bad_request(req, res);
}
// result
protorpc::CalcResult result;
result.set_num(param1.num() + param2.num());
res->set_result(result.SerializeAsString());
}
void calc_sub(const protorpc::Request& req, protorpc::Response* res) {
// params
if (req.params_size() != 2) {
return bad_request(req, res);
}
protorpc::CalcParam param1, param2;
if (!param1.ParseFromString(req.params(0)) ||
!param2.ParseFromString(req.params(1))) {
return bad_request(req, res);
}
// result
protorpc::CalcResult result;
result.set_num(param1.num() - param2.num());
res->set_result(result.SerializeAsString());
}
void calc_mul(const protorpc::Request& req, protorpc::Response* res) {
// params
if (req.params_size() != 2) {
return bad_request(req, res);
}
protorpc::CalcParam param1, param2;
if (!param1.ParseFromString(req.params(0)) ||
!param2.ParseFromString(req.params(1))) {
return bad_request(req, res);
}
// result
protorpc::CalcResult result;
result.set_num(param1.num() * param2.num());
res->set_result(result.SerializeAsString());
}
void calc_div(const protorpc::Request& req, protorpc::Response* res) {
// params
if (req.params_size() != 2) {
return bad_request(req, res);
}
protorpc::CalcParam param1, param2;
if (!param1.ParseFromString(req.params(0)) ||
!param2.ParseFromString(req.params(1))) {
return bad_request(req, res);
}
if (param2.num() == 0) {
return bad_request(req, res);
}
// result
protorpc::CalcResult result;
result.set_num(param1.num() / param2.num());
res->set_result(result.SerializeAsString());
}
#endif // HV_PROTO_RPC_HANDLER_CALC_H_

View File

@@ -0,0 +1,19 @@
#ifndef HV_PROTO_RPC_HANDLER_H_
#define HV_PROTO_RPC_HANDLER_H_
#include "../router.h"
void error_response(protorpc::Response* res, int code, const std::string& message) {
res->mutable_error()->set_code(code);
res->mutable_error()->set_message(message);
}
void not_found(const protorpc::Request& req, protorpc::Response* res) {
error_response(res, 404, "Not Found");
}
void bad_request(const protorpc::Request& req, protorpc::Response* res) {
error_response(res, 400, "Bad Request");
}
#endif // HV_PROTO_RPC_HANDLER_H_

View File

@@ -0,0 +1,25 @@
#ifndef HV_PROTO_RPC_HANDLER_LOGIN_H_
#define HV_PROTO_RPC_HANDLER_LOGIN_H_
#include "../router.h"
#include "../generated/login.pb.h"
void login(const protorpc::Request& req, protorpc::Response* res) {
// params
if (req.params_size() == 0) {
return bad_request(req, res);
}
protorpc::LoginParam param;
if (!param.ParseFromString(req.params(0))) {
return bad_request(req, res);
}
// result
protorpc::LoginResult result;
result.set_user_id(123456);
result.set_token(param.username() + ":" + param.password());
res->set_result(result.SerializeAsString());
}
#endif // HV_PROTO_RPC_HANDLER_LOGIN_H_

View File

@@ -0,0 +1,20 @@
syntax = "proto3";
package protorpc;
message Error {
int32 code = 1;
string message = 2;
}
message Request {
uint64 id = 1;
string method = 2;
repeated bytes params = 3;
}
message Response {
uint64 id = 1;
bytes result = 2;
Error error = 3;
}

View File

@@ -0,0 +1,11 @@
syntax = "proto3";
package protorpc;
message CalcParam {
int64 num = 1;
}
message CalcResult {
int64 num = 1;
}

View File

@@ -0,0 +1,13 @@
syntax = "proto3";
package protorpc;
message LoginParam {
string username = 1;
string password = 2;
}
message LoginResult {
uint64 user_id = 1;
string token = 2;
}

View File

@@ -0,0 +1,18 @@
#!/bin/bash
cd `dirname $0`
PROTOC=`which protoc`
if [ $? -ne 0 ]; then
echo "Not found command protoc!"
echo "Please install libprotobuf first!"
exit 1
fi
CPP_OUT_DIR=../generated
if [ ! -d "${CPP_OUT_DIR}" ]; then
mkdir -p ${CPP_OUT_DIR}
fi
set -x
${PROTOC} --cpp_out=${CPP_OUT_DIR} *.proto

View File

@@ -0,0 +1,65 @@
#include "protorpc.h"
#include <string.h> // import memcpy
int protorpc_pack(const protorpc_message* msg, void* buf, int len) {
if (!msg || !buf || !len) return -1;
const protorpc_head* head = &(msg->head);
unsigned int packlen = protorpc_package_length(head);
// Check is buffer enough
if (len < packlen) {
return -2;
}
unsigned char* p = (unsigned char*)buf;
*p++ = head->protocol[0];
*p++ = head->protocol[1];
*p++ = head->protocol[2];
*p++ = head->protocol[3];
*p++ = head->version;
*p++ = head->flags;
*p++ = head->reserved[0];
*p++ = head->reserved[1];
// hton length
unsigned int length = head->length;
*p++ = (length >> 24) & 0xFF;
*p++ = (length >> 16) & 0xFF;
*p++ = (length >> 8) & 0xFF;
*p++ = length & 0xFF;
// memcpy body
if (msg->body && head->length) {
memcpy(p, msg->body, head->length);
}
return packlen;
}
int protorpc_unpack(protorpc_message* msg, const void* buf, int len) {
if (!msg || !buf || !len) return -1;
if (len < PROTORPC_HEAD_LENGTH) return -2;
protorpc_head* head = &(msg->head);
const unsigned char* p = (const unsigned char*)buf;
head->protocol[0] = *p++;
head->protocol[1] = *p++;
head->protocol[2] = *p++;
head->protocol[3] = *p++;
head->version = *p++;
head->flags = *p++;
head->reserved[0] = *p++;
head->reserved[1] = *p++;
// ntoh length
head->length = ((unsigned int)*p++) << 24;
head->length |= ((unsigned int)*p++) << 16;
head->length |= ((unsigned int)*p++) << 8;
head->length |= *p++;
// Check is buffer enough
unsigned int packlen = protorpc_package_length(head);
if (len < packlen) {
return -3;
}
// NOTE: just shadow copy
if (len > PROTORPC_HEAD_LENGTH) {
msg->body = (const char*)buf + PROTORPC_HEAD_LENGTH;
}
return packlen;
}

View File

@@ -0,0 +1,68 @@
#ifndef HV_PROTO_RPC_H_
#define HV_PROTO_RPC_H_
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PROTORPC_NAME "HRPC"
#define PROTORPC_VERSION 1
// protocol:4bytes + version:1byte + flags:1byte + reserved:2bytes + length:4bytes = 12bytes
#define PROTORPC_HEAD_LENGTH 12
#define PROTORPC_HEAD_LENGTH_FIELD_OFFSET 8
#define PROTORPC_HEAD_LENGTH_FIELD_BYTES 4
typedef struct {
unsigned char protocol[4];
unsigned char version;
unsigned char flags;
unsigned char reserved[2];
unsigned int length;
} protorpc_head;
typedef const char* protorpc_body;
typedef struct {
protorpc_head head;
protorpc_body body;
} protorpc_message;
static inline unsigned int protorpc_package_length(const protorpc_head* head) {
return PROTORPC_HEAD_LENGTH + head->length;
}
static inline void protorpc_head_init(protorpc_head* head) {
// protocol = HRPC
memcpy(head->protocol, PROTORPC_NAME, 4);
head->version = PROTORPC_VERSION;
head->reserved[0] = head->reserved[1] = 0;
head->length = 0;
}
static inline void protorpc_message_init(protorpc_message* msg) {
protorpc_head_init(&msg->head);
msg->body = NULL;
}
static inline int protorpc_head_check(protorpc_head* head) {
if (memcmp(head->protocol, PROTORPC_NAME, 4) != 0) {
return -1;
}
if (head->version != PROTORPC_VERSION) {
return -2;
}
return 0;
}
// @retval >0 package_length, <0 error
int protorpc_pack(const protorpc_message* msg, void* buf, int len);
// @retval >0 package_length, <0 error
int protorpc_unpack(protorpc_message* msg, const void* buf, int len);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // HV_PROTO_RPC_H_

View File

@@ -0,0 +1,280 @@
/*
* proto rpc client
*
* @build make protorpc
* @server bin/protorpc_server 1234
* @client bin/protorpc_client 127.0.0.1 1234 add 1 2
*
*/
#include "TcpClient.h"
#include <mutex>
#include <condition_variable>
using namespace hv;
#include "protorpc.h"
#include "generated/base.pb.h"
#include "generated/calc.pb.h"
#include "generated/login.pb.h"
// valgrind --leak-check=full --show-leak-kinds=all
class ProtobufRAII {
public:
ProtobufRAII() {
}
~ProtobufRAII() {
google::protobuf::ShutdownProtobufLibrary();
}
};
static ProtobufRAII s_protobuf;
namespace protorpc {
typedef std::shared_ptr<protorpc::Request> RequestPtr;
typedef std::shared_ptr<protorpc::Response> ResponsePtr;
enum ProtoRpcResult {
kRpcSuccess = 0,
kRpcTimeout = -1,
kRpcError = -2,
kRpcNoResult = -3,
kRpcParseError = -4,
};
class ProtoRpcContext {
public:
protorpc::RequestPtr req;
protorpc::ResponsePtr res;
private:
std::mutex _mutex;
std::condition_variable _cond;
public:
void wait(int timeout_ms) {
std::unique_lock<std::mutex> locker(_mutex);
_cond.wait_for(locker, std::chrono::milliseconds(timeout_ms));
}
void notify() {
_cond.notify_one();
}
};
typedef std::shared_ptr<ProtoRpcContext> ContextPtr;
class ProtoRpcClient : public TcpClient {
public:
ProtoRpcClient() : TcpClient()
{
connect_state = kInitialized;
setConnectTimeout(5000);
reconn_setting_t reconn;
reconn_setting_init(&reconn);
reconn.min_delay = 1000;
reconn.max_delay = 10000;
reconn.delay_policy = 2;
setReconnect(&reconn);
// init protorpc_unpack_setting
unpack_setting_t protorpc_unpack_setting;
memset(&protorpc_unpack_setting, 0, sizeof(unpack_setting_t));
protorpc_unpack_setting.mode = UNPACK_BY_LENGTH_FIELD;
protorpc_unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
protorpc_unpack_setting.body_offset = PROTORPC_HEAD_LENGTH;
protorpc_unpack_setting.length_field_offset = PROTORPC_HEAD_LENGTH_FIELD_OFFSET;
protorpc_unpack_setting.length_field_bytes = PROTORPC_HEAD_LENGTH_FIELD_BYTES;
protorpc_unpack_setting.length_field_coding = ENCODE_BY_BIG_ENDIAN;
setUnpack(&protorpc_unpack_setting);
onConnection = [this](const SocketChannelPtr& channel) {
std::string peeraddr = channel->peeraddr();
if (channel->isConnected()) {
connect_state = kConnected;
printf("connected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
} else {
connect_state = kDisconnectd;
printf("disconnected to %s! connfd=%d\n", peeraddr.c_str(), channel->fd());
}
};
onMessage = [this](const SocketChannelPtr& channel, Buffer* buf) {
// protorpc_unpack
protorpc_message msg;
memset(&msg, 0, sizeof(msg));
int packlen = protorpc_unpack(&msg, buf->data(), buf->size());
if (packlen < 0) {
printf("protorpc_unpack failed!\n");
return;
}
assert(packlen == buf->size());
if (protorpc_head_check(&msg.head) != 0) {
printf("protorpc_head_check failed!\n");
return;
}
// Response::ParseFromArray
auto res = std::make_shared<protorpc::Response>();
if (!res->ParseFromArray(msg.body, msg.head.length)) {
return;
}
// id => res
calls_mutex.lock();
auto iter = calls.find(res->id());
if (iter == calls.end()) {
calls_mutex.unlock();
return;
}
auto ctx = iter->second;
calls_mutex.unlock();
ctx->res = res;
ctx->notify();
};
}
int connect(int port, const char* host = "127.0.0.1") {
createsocket(port, host);
connect_state = kConnecting;
start();
return 0;
}
protorpc::ResponsePtr call(protorpc::RequestPtr& req, int timeout_ms = 10000) {
if (connect_state != kConnected) {
return NULL;
}
static std::atomic<uint64_t> s_id = ATOMIC_VAR_INIT(0);
req->set_id(++s_id);
req->id();
auto ctx = std::make_shared<protorpc::ProtoRpcContext>();
ctx->req = req;
calls_mutex.lock();
calls[req->id()] = ctx;
calls_mutex.unlock();
// Request::SerializeToArray + protorpc_pack
protorpc_message msg;
protorpc_message_init(&msg);
msg.head.length = req->ByteSize();
int packlen = protorpc_package_length(&msg.head);
unsigned char* writebuf = NULL;
HV_STACK_ALLOC(writebuf, packlen);
packlen = protorpc_pack(&msg, writebuf, packlen);
if (packlen > 0) {
printf("%s\n", req->DebugString().c_str());
req->SerializeToArray(writebuf + PROTORPC_HEAD_LENGTH, msg.head.length);
channel->write(writebuf, packlen);
}
HV_STACK_FREE(writebuf);
// wait until response come or timeout
ctx->wait(timeout_ms);
auto res = ctx->res;
calls_mutex.lock();
calls.erase(req->id());
calls_mutex.unlock();
if (res == NULL) {
printf("RPC timeout!\n");
} else if (res->has_error()) {
printf("RPC error:\n%s\n", res->error().DebugString().c_str());
}
return res;
}
int calc(const char* method, int num1, int num2, int& out) {
auto req = std::make_shared<protorpc::Request>();
// method
req->set_method(method);
// params
protorpc::CalcParam param1, param2;
param1.set_num(num1);
param2.set_num(num2);
req->add_params()->assign(param1.SerializeAsString());
req->add_params()->assign(param2.SerializeAsString());
auto res = call(req);
if (res == NULL) return kRpcTimeout;
if (res->has_error()) return kRpcError;
if (res->result().empty()) return kRpcNoResult;
protorpc::CalcResult result;
if (!result.ParseFromString(res->result())) return kRpcParseError;
out = result.num();
return kRpcSuccess;
}
int login(const protorpc::LoginParam& param, protorpc::LoginResult* result) {
auto req = std::make_shared<protorpc::Request>();
// method
req->set_method("login");
// params
req->add_params()->assign(param.SerializeAsString());
auto res = call(req);
if (res == NULL) return kRpcTimeout;
if (res->has_error()) return kRpcError;
if (res->result().empty()) return kRpcNoResult;
if (!result->ParseFromString(res->result())) return kRpcParseError;
return kRpcSuccess;
}
enum {
kInitialized,
kConnecting,
kConnected,
kDisconnectd,
} connect_state;
std::map<uint64_t, protorpc::ContextPtr> calls;
std::mutex calls_mutex;
};
}
int main(int argc, char** argv) {
if (argc < 6) {
printf("Usage: %s host port method param1 param2\n", argv[0]);
printf("method = [add, sub, mul, div]\n");
printf("Examples:\n");
printf(" %s 127.0.0.1 1234 add 1 2\n", argv[0]);
printf(" %s 127.0.0.1 1234 div 1 0\n", argv[0]);
return -10;
}
const char* host = argv[1];
int port = atoi(argv[2]);
const char* method = argv[3];
const char* param1 = argv[4];
const char* param2 = argv[5];
protorpc::ProtoRpcClient cli;
cli.connect(port, host);
while (cli.connect_state == protorpc::ProtoRpcClient::kConnecting) hv_msleep(1);
if (cli.connect_state == protorpc::ProtoRpcClient::kDisconnectd) {
return -20;
}
// test login
{
protorpc::LoginParam param;
param.set_username("admin");
param.set_password("123456");
protorpc::LoginResult result;
if (cli.login(param, &result) == protorpc::kRpcSuccess) {
printf("login success!\n");
printf("%s\n", result.DebugString().c_str());
} else {
printf("login failed!\n");
}
}
// test calc
{
int num1 = atoi(param1);
int num2 = atoi(param2);
int result = 0;
if (cli.calc(method, num1, num2, result) == protorpc::kRpcSuccess) {
printf("calc success!\n");
printf("%d %s %d = %d\n", num1, method, num2, result);
} else {
printf("calc failed!\n");
}
}
return 0;
}

View File

@@ -0,0 +1,141 @@
/*
* proto rpc server
*
* @build make protorpc
* @server bin/protorpc_server 1234
* @client bin/protorpc_client 127.0.0.1 1234 add 1 2
*
*/
#include "TcpServer.h"
using namespace hv;
#include "protorpc.h"
#include "router.h"
#include "handler/handler.h"
#include "handler/calc.h"
#include "handler/login.h"
// valgrind --leak-check=full --show-leak-kinds=all
class ProtobufRAII {
public:
ProtobufRAII() {
}
~ProtobufRAII() {
google::protobuf::ShutdownProtobufLibrary();
}
};
static ProtobufRAII s_protobuf;
protorpc_router router[] = {
{"add", calc_add},
{"sub", calc_sub},
{"mul", calc_mul},
{"div", calc_div},
{"login", login},
};
#define PROTORPC_ROUTER_NUM (sizeof(router)/sizeof(router[0]))
class ProtoRpcServer : public TcpServer {
public:
ProtoRpcServer() : TcpServer()
{
onConnection = [](const SocketChannelPtr& channel) {
std::string peeraddr = channel->peeraddr();
if (channel->isConnected()) {
printf("%s connected! connfd=%d\n", peeraddr.c_str(), channel->fd());
} else {
printf("%s disconnected! connfd=%d\n", peeraddr.c_str(), channel->fd());
}
};
onMessage = handleMessage;
// init protorpc_unpack_setting
unpack_setting_t protorpc_unpack_setting;
memset(&protorpc_unpack_setting, 0, sizeof(unpack_setting_t));
protorpc_unpack_setting.mode = UNPACK_BY_LENGTH_FIELD;
protorpc_unpack_setting.package_max_length = DEFAULT_PACKAGE_MAX_LENGTH;
protorpc_unpack_setting.body_offset = PROTORPC_HEAD_LENGTH;
protorpc_unpack_setting.length_field_offset = PROTORPC_HEAD_LENGTH_FIELD_OFFSET;
protorpc_unpack_setting.length_field_bytes = PROTORPC_HEAD_LENGTH_FIELD_BYTES;
protorpc_unpack_setting.length_field_coding = ENCODE_BY_BIG_ENDIAN;
setUnpack(&protorpc_unpack_setting);
}
int listen(int port) { return createsocket(port); }
private:
static void handleMessage(const SocketChannelPtr& channel, Buffer* buf) {
// unpack -> Request::ParseFromArray -> router -> Response::SerializeToArray -> pack -> Channel::write
// protorpc_unpack
protorpc_message msg;
memset(&msg, 0, sizeof(msg));
int packlen = protorpc_unpack(&msg, buf->data(), buf->size());
if (packlen < 0) {
printf("protorpc_unpack failed!\n");
return;
}
assert(packlen == buf->size());
if (protorpc_head_check(&msg.head) != 0) {
printf("protorpc_head_check failed!\n");
return;
}
// Request::ParseFromArray
protorpc::Request req;
protorpc::Response res;
if (req.ParseFromArray(msg.body, msg.head.length)) {
printf("> %s\n", req.DebugString().c_str());
res.set_id(req.id());
// router
const char* method = req.method().c_str();
bool found = false;
for (int i = 0; i < PROTORPC_ROUTER_NUM; ++i) {
if (strcmp(method, router[i].method) == 0) {
found = true;
router[i].handler(req, &res);
break;
}
}
if (!found) {
not_found(req, &res);
}
} else {
bad_request(req, &res);
}
// Response::SerializeToArray + protorpc_pack
protorpc_message_init(&msg);
msg.head.length = res.ByteSize();
packlen = protorpc_package_length(&msg.head);
unsigned char* writebuf = NULL;
HV_STACK_ALLOC(writebuf, packlen);
packlen = protorpc_pack(&msg, writebuf, packlen);
if (packlen > 0) {
printf("< %s\n", res.DebugString().c_str());
res.SerializeToArray(writebuf + PROTORPC_HEAD_LENGTH, msg.head.length);
channel->write(writebuf, packlen);
}
HV_STACK_FREE(writebuf);
}
};
int main(int argc, char** argv) {
if (argc < 2) {
printf("Usage: %s port\n", argv[0]);
return -10;
}
int port = atoi(argv[1]);
ProtoRpcServer srv;
int listenfd = srv.listen(port);
if (listenfd < 0) {
return -20;
}
printf("protorpc_server listen on port %d, listenfd=%d ...\n", port, listenfd);
srv.setThreadNum(4);
srv.start();
while (1) hv_sleep(1);
return 0;
}

24
third_party/libhv/examples/protorpc/router.h vendored Executable file
View File

@@ -0,0 +1,24 @@
#ifndef HV_PROTO_RPC_ROUTER_H_
#define HV_PROTO_RPC_ROUTER_H_
#include "generated/base.pb.h"
typedef void (*protorpc_handler)(const protorpc::Request& req, protorpc::Response* res);
typedef struct {
const char* method;
protorpc_handler handler;
} protorpc_router;
void error_response(protorpc::Response* res, int code, const std::string& message);
void not_found(const protorpc::Request& req, protorpc::Response* res);
void bad_request(const protorpc::Request& req, protorpc::Response* res);
void calc_add(const protorpc::Request& req, protorpc::Response* res);
void calc_sub(const protorpc::Request& req, protorpc::Response* res);
void calc_mul(const protorpc::Request& req, protorpc::Response* res);
void calc_div(const protorpc::Request& req, protorpc::Response* res);
void login(const protorpc::Request& req, protorpc::Response* res);
#endif // HV_PROTO_RPC_ROUTER_H_