简介
RPC是远程过程调用(Remote Procedure Call)的缩写形式 ,RPC的目的是为了简化网络通信,让用户可以专注于业务处理,不用关心网络层的处理,真正实现在客户端A中调用函数F就可以调用服务端B中的函数F的目的。
RPC 模型引入存根进程( stub) 的概念, 对于服务端的服务类A,在客户端通过A::stub类进行调用,中间的网络交互流程有RPC框架进行实现。
实现
我们可以借助protobuf来实现序列化和stub的生成,借助HpSocket来完成网络的交互,为了简单,我们采用HTTP协议来进行消息的交互。
protobuf生成stub

RPC交互流程

实现流程
第一步、定义protobuf结构
1syntax = "proto3"; 2 3//支持rpc服务代码生成 4option cc_generic_services = true; 5 6//C#命名空间 7option csharp_namespace = "Google.Protobuf.Auth"; 8 9package Auth; 10 11 12//Rpc协议 13message RpcProtocol 14{ 15uint32 serviceId = 1; 16uint32 methodId = 2; 17bytes data = 3; 18int32 falg = 4; 19} 20 21 22message UserInfo 23{ 24 string phoneNum = 1; 25 string password = 2; 26} 27 28message ResponseMsg 29{ 30 int32 code = 1; 31 bytes message = 2; 32} 33 34//鉴权服务 35service Authentication { 36 //注册申请 37 rpc UserApplyReg(UserInfo) returns (ResponseMsg); 38 //用户注册 39 rpc UserRegister(UserInfo) returns (ResponseMsg); 40 //用户登陆 41 rpc UserLogin(UserInfo) returns (ResponseMsg); 42}
第二步、服务端实现
1#include <stdio.h> 2#include "WebService.h" 3#include "Authentication.h" 4//Rcp 服务端演示 5int main() 6{ 7 //启动web接口线程 8 WebService *pSrvProc = new WebService(); 9 pSrvProc->RegisterService(new CAuthentication); 10 11 CHttpServerPtr pHttpSrv(pSrvProc); 12 13 if (pHttpSrv->Start("0.0.0.0", 9090)) 14 { 15 printf("创建web server 成功"); 16 } 17 else 18 { 19 printf("创建web server 失败"); 20 } 21 22 while (true) 23 { 24 Sleep(10000); 25 } 26}
第三步、客户端实现
1#include <iostream> 2#include "RpcChannel.h" 3#include "RpcController.h" 4#include "proto\Authentication.pb.h" 5//Rcp 客户端演示 6int main() 7{ 8 std::string strSrvAddr = "http://127.0.0.1:9090"; 9 CRpcChannel channel(strSrvAddr); 10 11 CRpcController pController; 12 Auth::Authentication::Stub stub(&channel); 13 Auth::UserInfo req; 14 Auth::ResponseMsg res; 15 16 req.set_phonenum("10086"); 17 stub.UserApplyReg(&pController, &req, &res, NULL); 18 std::cout << res.message() << std::endl; 19 20 req.set_password("********"); 21 stub.UserRegister(&pController, &req, &res, NULL); 22 std::cout << res.message() << std::endl; 23 24 stub.UserLogin(&pController, &req, &res, NULL); 25 std::cout << res.message() << std::endl; 26}
核心代码
一、CRpcChannel 的实现,用于封装客户端与服务端交互流程
1#include "RpcChannel.h" 2#include "proto\Authentication.pb.h" 3#include <google\protobuf\message_lite.h> 4#include <google\protobuf\message.h> 5 6#define PROTOBUF_PROTOCOL_FLAG 0xfafbfcfd 7 8CRpcChannel::CRpcChannel(const std::string& srvAddr):m_strSrvAddr(srvAddr) 9{ 10} 11 12CRpcChannel::~CRpcChannel() 13{ 14} 15 16void CRpcChannel::CallMethod(const proto::MethodDescriptor* method, 17 proto::RpcController* controller, 18 const proto::Message* request, 19 proto::Message* response, 20 proto::Closure* done) 21{ 22 Auth::RpcProtocol message; 23 message.set_serviceid(method->service()->index()); 24 message.set_methodid(method->index()); 25 message.set_falg(PROTOBUF_PROTOCOL_FLAG); 26 message.set_data(request->SerializeAsString()); 27 28 CHttpSyncClientPtr httpReq(nullptr); 29 std::string strBody = message.SerializeAsString(); 30 int nSize = strBody.size(); 31 std::string strBodySize = std::to_string(nSize); 32 33 THeader header[] = { { "Content-Type", "text/plain;charset=utf-8" },{ "Content-Length", strBodySize.c_str() } }; 34 35 int iHeaderCount = sizeof(header) / sizeof(THeader); 36 37 if (!httpReq->OpenUrl("GET", m_strSrvAddr.c_str(), header, iHeaderCount, (const BYTE*)strBody.c_str(), strBody.size())) 38 { 39 printf("发送结果失败"); 40 return; 41 } 42 43 BYTE * respBody = nullptr; 44 int len = 0; 45 if (httpReq->GetResponseBody((LPCBYTE*)&respBody, &len) == FALSE) 46 return; 47 48 response->ParseFromString((char*)respBody); 49}
二、服务器处理
1EnHttpParseResult WebService::OnMessageComplete(IHttpServer * pSender, CONNID dwConnID) 2{ 3 char* pszBuf = nullptr; 4 pSender->GetConnectionExtra(dwConnID, (VOID**)&pszBuf); 5 6 char szBuf[64] = { 0 }; 7 int nSize = sizeof(szBuf); 8 USHORT nPort; 9 pSender->GetRemoteAddress(dwConnID, szBuf, nSize, nPort); 10 11 Auth::RpcProtocol protoMsg; 12 if (!protoMsg.ParseFromString(pszBuf)) 13 { 14 std::cout << "无效消息" << std::endl; 15 return HPR_ERROR; 16 } 17 18 if (m_mapService.find(protoMsg.serviceid()) != m_mapService.end()) 19 { 20 auto pRpcMonitor = m_mapService[protoMsg.serviceid()]; 21 const protoBuf::ServiceDescriptor *service_descriptor = pRpcMonitor->GetDescriptor(); 22 23 24 const protoBuf::MethodDescriptor *method_descriptor = service_descriptor->method(protoMsg.methodid()); 25 const protoBuf::Message& request_proto = pRpcMonitor->GetRequestPrototype(method_descriptor); 26 const protoBuf::Message& response_proto = pRpcMonitor->GetResponsePrototype(method_descriptor); 27 28 protoBuf::Message *reqMsg = request_proto.New(); 29 reqMsg->ParseFromString(protoMsg.data()); 30 protoBuf::Message *resMsg = response_proto.New(); 31 CRpcController pController(szBuf); 32 pRpcMonitor->CallMethod(method_descriptor, &pController, reqMsg, resMsg, NULL); 33 34 std::string strBody = resMsg->SerializeAsString(); 35 int nSize = strBody.size(); 36 std::string strBodySize = std::to_string(nSize); 37 THeader header[] = { { "Content-Type", "text/plain;charset=utf-8" },{ "Content-Length", strBodySize.c_str() } }; 38 39 int iHeaderCount = sizeof(header) / sizeof(THeader); 40 pSender->SendResponse(dwConnID, 41 HSC_OK, 42 "Http Server OK", 43 header, iHeaderCount, 44 (const BYTE*)strBody.c_str(), 45 strBody.size()); 46 } 47 return HPR_OK; 48}
三、服务端业务实现
1#include "Authentication.h" 2 3void CAuthentication::UserApplyReg(::google::protobuf::RpcController * controller, const::Auth::UserInfo * request, ::Auth::ResponseMsg * response, ::google::protobuf::Closure * done) 4{ 5 std::cout << "用户:" << request->phonenum() << "申请" << std::endl; 6 response->set_code(0); 7 response->set_message("允许申请"); 8} 9 10void CAuthentication::UserRegister(::google::protobuf::RpcController * controller, const::Auth::UserInfo * request, ::Auth::ResponseMsg * response, ::google::protobuf::Closure * done) 11{ 12 std::cout << "用户:" << request->phonenum() << ", 密码 " << request->password() << "正在注册" << std::endl; 13 response->set_code(0); 14 response->set_message("注册成功"); 15} 16 17void CAuthentication::UserLogin(::google::protobuf::RpcController * controller, const::Auth::UserInfo * request, ::Auth::ResponseMsg * response, ::google::protobuf::Closure * done) 18{ 19 std::cout << "用户:" << request->phonenum() << ", 密码 " << request->password() << "正在登陆" << std::endl; 20 response->set_code(0); 21 response->set_message("登陆成功"); 22}
至此一个简单的RPC交互流程就完成了