簡介ios
RPC是遠程過程調用(Remote Procedure Call)的縮寫形式 ,RPC的目的是爲了簡化網絡通訊,讓用戶能夠專一於業務處理,不用關心網絡層的處理,真正實如今客戶端A中調用函數F就能夠調用服務端B中的函數F的目的。git
RPC 模型引入存根進程( stub) 的概念, 對於服務端的服務類A,在客戶端經過A::stub類進行調用,中間的網絡交互流程有RPC框架進行實現。web
實現服務器
咱們能夠藉助protobuf來實現序列化和stub的生成,藉助HpSocket來完成網絡的交互,爲了簡單,咱們採用HTTP協議來進行消息的交互。網絡
protobuf生成stub框架
RPC交互流程函數
實現流程ui
第一步、定義protobuf結構google
syntax = "proto3"; //支持rpc服務代碼生成 option cc_generic_services = true; //C#命名空間 option csharp_namespace = "Google.Protobuf.Auth"; package Auth; //Rpc協議 message RpcProtocol { uint32 serviceId = 1; uint32 methodId = 2; bytes data = 3; int32 falg = 4; } message UserInfo { string phoneNum = 1; string password = 2; } message ResponseMsg { int32 code = 1; bytes message = 2; } //鑑權服務 service Authentication { //註冊申請 rpc UserApplyReg(UserInfo) returns (ResponseMsg); //用戶註冊 rpc UserRegister(UserInfo) returns (ResponseMsg); //用戶登錄 rpc UserLogin(UserInfo) returns (ResponseMsg); }
第二步、服務端實現spa
#include <stdio.h> #include "WebService.h" #include "Authentication.h" //Rcp 服務端演示 int main() { //啓動web接口線程 WebService *pSrvProc = new WebService(); pSrvProc->RegisterService(new CAuthentication); CHttpServerPtr pHttpSrv(pSrvProc); if (pHttpSrv->Start("0.0.0.0", 9090)) { printf("建立web server 成功"); } else { printf("建立web server 失敗"); } while (true) { Sleep(10000); } }
第三步、客戶端實現
#include <iostream> #include "RpcChannel.h" #include "RpcController.h" #include "proto\Authentication.pb.h" //Rcp 客戶端演示 int main() { std::string strSrvAddr = "http://127.0.0.1:9090"; CRpcChannel channel(strSrvAddr); CRpcController pController; Auth::Authentication::Stub stub(&channel); Auth::UserInfo req; Auth::ResponseMsg res; req.set_phonenum("10086"); stub.UserApplyReg(&pController, &req, &res, NULL); std::cout << res.message() << std::endl; req.set_password("********"); stub.UserRegister(&pController, &req, &res, NULL); std::cout << res.message() << std::endl; stub.UserLogin(&pController, &req, &res, NULL); std::cout << res.message() << std::endl; }
核心代碼
1、CRpcChannel 的實現,用於封裝客戶端與服務端交互流程
#include "RpcChannel.h" #include "proto\Authentication.pb.h" #include <google\protobuf\message_lite.h> #include <google\protobuf\message.h> #define PROTOBUF_PROTOCOL_FLAG 0xfafbfcfd CRpcChannel::CRpcChannel(const std::string& srvAddr):m_strSrvAddr(srvAddr) { } CRpcChannel::~CRpcChannel() { } void CRpcChannel::CallMethod(const proto::MethodDescriptor* method, proto::RpcController* controller, const proto::Message* request, proto::Message* response, proto::Closure* done) { Auth::RpcProtocol message; message.set_serviceid(method->service()->index()); message.set_methodid(method->index()); message.set_falg(PROTOBUF_PROTOCOL_FLAG); message.set_data(request->SerializeAsString()); CHttpSyncClientPtr httpReq(nullptr); std::string strBody = message.SerializeAsString(); int nSize = strBody.size(); std::string strBodySize = std::to_string(nSize); THeader header[] = { { "Content-Type", "text/plain;charset=utf-8" },{ "Content-Length", strBodySize.c_str() } }; int iHeaderCount = sizeof(header) / sizeof(THeader); if (!httpReq->OpenUrl("GET", m_strSrvAddr.c_str(), header, iHeaderCount, (const BYTE*)strBody.c_str(), strBody.size())) { printf("發送結果失敗"); return; } BYTE * respBody = nullptr; int len = 0; if (httpReq->GetResponseBody((LPCBYTE*)&respBody, &len) == FALSE) return; response->ParseFromString((char*)respBody); }
2、服務器處理
EnHttpParseResult WebService::OnMessageComplete(IHttpServer * pSender, CONNID dwConnID) { char* pszBuf = nullptr; pSender->GetConnectionExtra(dwConnID, (VOID**)&pszBuf); char szBuf[64] = { 0 }; int nSize = sizeof(szBuf); USHORT nPort; pSender->GetRemoteAddress(dwConnID, szBuf, nSize, nPort); Auth::RpcProtocol protoMsg; if (!protoMsg.ParseFromString(pszBuf)) { std::cout << "無效消息" << std::endl; return HPR_ERROR; } if (m_mapService.find(protoMsg.serviceid()) != m_mapService.end()) { auto pRpcMonitor = m_mapService[protoMsg.serviceid()]; const protoBuf::ServiceDescriptor *service_descriptor = pRpcMonitor->GetDescriptor(); const protoBuf::MethodDescriptor *method_descriptor = service_descriptor->method(protoMsg.methodid()); const protoBuf::Message& request_proto = pRpcMonitor->GetRequestPrototype(method_descriptor); const protoBuf::Message& response_proto = pRpcMonitor->GetResponsePrototype(method_descriptor); protoBuf::Message *reqMsg = request_proto.New(); reqMsg->ParseFromString(protoMsg.data()); protoBuf::Message *resMsg = response_proto.New(); CRpcController pController(szBuf); pRpcMonitor->CallMethod(method_descriptor, &pController, reqMsg, resMsg, NULL); std::string strBody = resMsg->SerializeAsString(); int nSize = strBody.size(); std::string strBodySize = std::to_string(nSize); THeader header[] = { { "Content-Type", "text/plain;charset=utf-8" },{ "Content-Length", strBodySize.c_str() } }; int iHeaderCount = sizeof(header) / sizeof(THeader); pSender->SendResponse(dwConnID, HSC_OK, "Http Server OK", header, iHeaderCount, (const BYTE*)strBody.c_str(), strBody.size()); } return HPR_OK; }
3、服務端業務實現
#include "Authentication.h" void CAuthentication::UserApplyReg(::google::protobuf::RpcController * controller, const::Auth::UserInfo * request, ::Auth::ResponseMsg * response, ::google::protobuf::Closure * done) { std::cout << "用戶:" << request->phonenum() << "申請" << std::endl; response->set_code(0); response->set_message("容許申請"); } void CAuthentication::UserRegister(::google::protobuf::RpcController * controller, const::Auth::UserInfo * request, ::Auth::ResponseMsg * response, ::google::protobuf::Closure * done) { std::cout << "用戶:" << request->phonenum() << ", 密碼 " << request->password() << "正在註冊" << std::endl; response->set_code(0); response->set_message("註冊成功"); } void CAuthentication::UserLogin(::google::protobuf::RpcController * controller, const::Auth::UserInfo * request, ::Auth::ResponseMsg * response, ::google::protobuf::Closure * done) { std::cout << "用戶:" << request->phonenum() << ", 密碼 " << request->password() << "正在登錄" << std::endl; response->set_code(0); response->set_message("登錄成功"); }
至此一個簡單的RPC交互流程就完成了
源碼地址:https://gitee.com/lingluonianhua/EasyRpc.git