Grpc學習總結--四種模式(C++實現)

目錄

Protobuf定義(包括四種模式的接口定義和所用到的簡單傳輸結構)

Service

傳輸結構

單向模式

服務端函數實現

客戶端函數實現

運行結果

服務端流

服務端實現

客戶都安實現

運行結果

客戶端流

服務端實現

客戶端實現

運行結果

雙向流

服務端實現

客戶端實現

運行結果

踩過的一些坑


GRPC的四種模式,分別實現了單工,雙工通信,客戶端和服務端實現跨語言交互,試用於多種複雜通信場景。此文主要對C++實現的四種模式進行學習總結,

Protobuf定義(包括四種模式的接口定義和所用到的簡單傳輸結構)

  • Service

 

  • 傳輸結構

 

單向模式

客戶端發送一個請求,阻塞等待一個回覆,是最簡單的一種方式,但是單向的,只能客戶端觸發請求。

  • 服務端函數實現

Status ZZXG_Server::RequestMsg(ServerContext* context, const RequestInfo* request, ReplyInfo* reply)
{
	switch (request->askmsg())
	{
	case TransMsg::AskType::name:
		reply->set_remsg("糉子小哥");
		break;
	case TransMsg::AskType::age:
		reply->set_remsg(std::to_string(25));
		break;
	case TransMsg::AskType::nativeplace:
		reply->set_remsg("四川");
		break;
	default:
		reply->set_remsg("未知請求");
		break;
	}
	return Status::OK;
}

收到請求後,給返回變量賦值,return Status::OK即爲賦值完畢。

  • 客戶端函數實現

void ZZXG_Client::RequestMsgTest()
{
	// 定義請求對象,並賦值.
	RequestInfo request;
	request.set_askmsg(TransMsg::name);
	// 返回對象定義.
	ReplyInfo reply;
	ClientContext context;
	// 調用請求函數.
	Status status = stub_->RequestMsg(&context, request, &reply);
	// 返回狀態.
 	if (status.ok()) 
	{
		std::cout << "接收到服務端回覆: " << reply.remsg() << std::endl;
	}
	else 
	{
		std::cout << "單向 rpc failed." << std::endl;
	}
}

Status status = stub_->RequestMsg(&context, request, &reply);會等待服務端對reply賦值返回status爲止,若返回狀態不是ok,則通信出錯,或者數據傳輸出錯,具體根據返回的status參數決定。

  • 運行結果

服務端流

服務端流,顧名思義,是一對多的情況,客戶都安一個請求,對應服務端流式回覆。當需要服務端主動推動數據時可以應用此模式。

  • 服務端實現

Status ZZXG_Server::RequestMsgServerStream(ServerContext* context, const RequestInfo* request, ServerWriter<ReplyInfo>* writer)
{
	//此處只有初始化的5組測試數據,實際根據需要推送的數據進行回覆,可以一直阻塞推送
	int i = 0;
	std::string str = "";
	for (ReplyInfo reply : replyInfoList)
	{	
		switch (request->askmsg())
		{
		case TransMsg::AskType::name:
			str = "name";
			reply.set_remsg("糉子小哥"+ std::to_string(i));
			break;
		case TransMsg::AskType::age:
			str = "age";
			reply.set_remsg(std::to_string(25));
			break;
		case TransMsg::AskType::nativeplace:
			str = "native place";
			reply.set_remsg("四川");
			break;
		default:
			str = "未知請求";
			reply.set_remsg("未知請求");
			break;
		}

		writer->Write(reply);
		i++;
	}
	std::cout << "接受到客戶端的請求:" << str << "\n" << std::endl;
	return Status::OK;
}

接收到客戶端請求開始,服務端組織參數回覆,直到return Status::OK;可以在此期間wirter調用write函數進行回覆寫入,進行多次調用,達到服務端主動推送的效果。

  • 客戶都安實現

void ZZXG_Client::RequestMsgServerStreamTest()
{
	RequestInfo requestInfo;
	requestInfo.set_askmsg(TransMsg::name);
	ClientContext context;
	ReplyInfo replyInfo;
	std::unique_ptr<ClientReader<ReplyInfo> > reader(stub_->RequestMsgServerStream(&context, requestInfo));
	while (reader->Read(&replyInfo)) 
	{
		std::cout << "收到服務端的回覆: "<< replyInfo.remsg() << " --\n "<< std::endl;
	}
	Status status = reader->Finish();
	if (!status.ok()) 
	{
		std::cout << "服務端流 rpc failed." << std::endl;
	}
}

客戶端發出請求後,循環進入等待接收,通過reader調用read函數進行讀取,直到服務端返回ok狀態結束接收。

  • 運行結果

uploading.4e448015.gif轉存失敗重新上傳取消

客戶端流

客戶端流和服務端流相似,只不過時多對一的情況,客戶端流式請求,對應服務端一個回覆,服務端直到接收的請求結束才返回status::ok。應用於客戶端主動流式給服務端推送數據的場景。

  • 服務端實現

Status ZZXG_Server::RequestMsgClientStream(ServerContext* context, ServerReader<RequestInfo>* reader, ReplyInfo* reply)
{
	RequestInfo request;
	std::string strInfo;
	std::string str;
	while (reader->Read(&request)) 
	{
		switch (request.askmsg())
		{
		case TransMsg::AskType::name:
			str += "-name-";
			strInfo += "糉子小哥";
			break;
		case TransMsg::AskType::age:
			str += "-age-";
			strInfo += std::to_string(25);
			break;
		case TransMsg::AskType::nativeplace:
			str += "-native place-";
			strInfo += "四川";
			break;
		}
	}
	std::cout << "接受到客戶端的請求:" << str << "\n" << std::endl;
	reply->set_remsg(strInfo);
	return Status::OK;
}
  • 客戶端實現

void ZZXG_Client::RequestMsgClientStreamTest()
{
	ReplyInfo reply;
	ClientContext context;
	std::unique_ptr<ClientWriter<RequestInfo> > writer(stub_->RequestMsgClientStream(&context, &reply));
	for (RequestInfo request:_requestList) 
	{
		writer->Write(request);
	}
	writer->WritesDone();
	Status status = writer->Finish();
	if (status.ok()) {
		std::cout << "接收到服務端回覆: " << reply.remsg() << " --\n"<< std::endl;
	}
	else {
		std::cout << "客戶端流 rpc failed." << std::endl;
	}
}

當客戶端writer->WritesDone();服務端將知道數據傳輸結束,將不在讀取數據,並返回狀態。

  • 運行結果

uploading.4e448015.gif轉存失敗重新上傳取消

 

 

雙向流

雙向流時結合客戶端流和服務端流的結果,服務端和客戶均可主動推送數據,實現雙工通信的效果。

  • 服務端實現

Status ZZXG_Server::RequestMsg2WayStream(ServerContext* context, ServerReaderWriter<ReplyInfo, RequestInfo>* stream)
{
	RequestInfo request;
	while (stream->Read(&request)) 
	{
		std::cout << "收到請求,類型爲" << request.askmsg() <<"\n"<<std::endl;
		int i = 0;
		for (ReplyInfo reply : replyInfoList)
		{
			switch (request.askmsg())
			{
			case TransMsg::AskType::name:
				reply.set_remsg("糉子小哥" + std::to_string(i));
				break;
			case TransMsg::AskType::age:
				reply.set_remsg(std::to_string(25));
				break;
			case TransMsg::AskType::nativeplace:
				reply.set_remsg("四川");
				break;
			default:
				reply.set_remsg("未知請求");
				break;
			}
			stream->Write(reply);
			i++;
		}
	}
  • 客戶端實現

//發送數據到服務端
void ZZXG_Client::SendRequestToServer(std::shared_ptr<ClientReaderWriter<RequestInfo, ReplyInfo> > writer)
{
	for (RequestInfo request : _requestList)
	{
		writer->Write(request);
	}
	writer->WritesDone();
}

void ZZXG_Client::RequestMsg2WayStreamTest()
{
	ClientContext context;

	std::shared_ptr<ClientReaderWriter<RequestInfo, ReplyInfo> > writerRead(stub_->RequestMsg2WayStream(&context));
	writerThread = new std::thread(&ZZXG_Client::SendRequestToServer, this, writerRead);

	ReplyInfo reply;
	while (writerRead->Read(&reply)) {
		std::cout << "接收到回覆:" << reply.remsg()<<"--\n" << std::endl;
	}
	writerThread->join();
	Status status = writerRead->Finish();
	if (!status.ok())
	{
		std::cout << "雙向流 rpc failed." << std::endl;
	}
}

由於阻塞等待接收,所以開了一個線程專門用於數據的發送,這樣互相不影響發收。

  • 運行結果

踩過的一些坑

  1. string類型不能傳輸中文字符,需要用bytes類型。
  2. proto2中message包含有繼承,proto3捨棄了,所以當傳輸的結構有繼承關係時,proto3比較麻煩。
  3.  C++下,proto文件中定義的結構set_XXX時,變量會被改成小寫。
  4. Proto文件編譯.cc文件中不包含預編譯。

注:本文編譯器使用的時VS2015,grpc庫時32位,如果有其他版本需要可以下載grpc源碼進行編譯

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章