C++實現簡單的RPC框架

簡介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

相關文章
相關標籤/搜索