這里我們運用vcpkg去下載安裝gRPC,進入vcpkg目錄后,執(zhí)行命令:.\vcpkg.exe install grpc:x64-windows
grpc在vcpkg里面安裝完成后,我們就來使用grpc做一個簡單的例子。
gRPC顧名思義,就是google的RPC方案,基于protobuf數(shù)據(jù)傳輸,其中proto文件的定義約定了服務器端和客戶端的服務接口協(xié)議。
這里我們就用加法和乘法作為服務器端提供的服務,讓客戶端去調用(RPC),我們建立三個文件夾CPP_Server, CPP_Client, proto 來分別存儲服務器端代碼,客戶端代碼,以及proto文件。項目配置選用cmakelist.txt和cmake來管理。
1.? 服務器端和客戶端的proto定義(calculation.proto文件):
syntax = "proto3";
package data_handler;service CalculationInterface{
?? ?// Add operation
? ? rpc Add(AddRequest) returns (AddReply){}
?? ?// Multiply operation
? ? rpc Multiply(MultiplyRequest) returns (MultiplyReply){}
}message AddReply{
? ? int32 result = 1;
}message AddRequest{
? ? int32 param1 = 1;
? ? int32 param2 = 2;
}message MultiplyReply{
? ? int32 result = 1;
}message MultiplyRequest{
? ? int32 param1 = 1;
? ? int32 param2 = 2;
}
?2. 服務器端代碼
在服務器端,我們要在cmakelist里面進行proto文件的解析執(zhí)行成相應的.pb.cc,.pb.h,.grpc.pb.cc,.grpc.pb.h文件,同時對項目文件的配置。
那么我們必然要先找到grpc, protobuf庫和執(zhí)行文件。這時候就需要用到vcpkg這套包管理器,
而下面這句話就是讓vcpkg的包管理起作用的關鍵:
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")
注意這句話一定要在定義project名字之前,本例子是:project(CalculationInGrpcServer)
這樣子后面的find_package, find_program, target_link_libraries等都會去vckpg里面找到。
cmake_minimum_required(VERSION 3.20)
# Note: 8 target(s) were omitted.
message("--------" $ENV{VCPKG_ROOT})set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")
project(CalculationInGrpcServer)set(_GRPC_GRPCPP gRPC::grpc++)
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
set(_REFLECTION gRPC::grpc++_reflection)set(_PROTOBUF_LIBPROTOBUF_D libprotobufd)
find_package(gRPC CONFIG REQUIRED)
find_program(_PROTOBUF_PROTOC protoc REQUIRED)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin REQUIRED)# Generated proto sources
get_filename_component(proto "../proto/calculation.proto" ABSOLUTE)
get_filename_component(proto_name "../proto/calculation.proto" NAME_WE)
get_filename_component(proto_path "${proto}" PATH)set(proto_srcs "${proto_path}/${proto_name}.pb.cc")
set(proto_hdrs "${proto_path}/${proto_name}.pb.h")
set(grpc_srcs "${proto_path}/${proto_name}.grpc.pb.cc")
set(grpc_hdrs "${proto_path}/${proto_name}.grpc.pb.h")
message("------------------------------------------------")
message(${_PROTOBUF_PROTOC})
message(${_GRPC_CPP_PLUGIN_EXECUTABLE})
message(${proto_path})message("-------------------------------------------------")
add_custom_command(
? ? ? OUTPUT "${proto_srcs}" "${proto_hdrs}" "${grpc_srcs}" "${grpc_hdrs}"
? ? ? COMMAND ${_PROTOBUF_PROTOC}
? ? ? ARGS --grpc_out "${proto_path}"
? ? ? ? --cpp_out "${proto_path}"
? ? ? ? -I "${proto_path}"
? ? ? ? --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
? ? ? ? "${proto}"
? ? ? DEPENDS "${proto}")# Include generated *.pb.h files
include_directories(
? ? "${proto_path}"
? ? )file(GLOB PUBLIC_HEADER ${CMAKE_CURRENT_BINARY_DIR}/*.h
? ? ? ? ? ? ? ? ? ? ? ? ${PROJECT_SOURCE_DIR}/../proto/*.h)
add_executable(${PROJECT_NAME} CalculationServer.cc ${proto_srcs} ${grpc_srcs})
target_link_libraries(${PROJECT_NAME} PRIVATE gRPC::gpr gRPC::upb gRPC::grpc gRPC::grpc++)message("protobuf libs are:")
message(${_PROTOBUF_LIBPROTOBUF_D})
在我們的服務器端代碼里面著重用到的是::data_handler::CalculationInterface::Service,這個是proto解釋器幫我們對proto文件解析成cc文件后,里面的一個Service接口,我們代碼里面最主要是去實現(xiàn)這個接口,來看看吧:
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>
#include <calculation.grpc.pb.h>
#include <calculation.pb.h>
#include <thread>
using grpc::Server;
using grpc::ServerBuilder;
using ::grpc::ServerContext;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerWriter;
using grpc::Status;
class CalculationInGrpcServerImpl final
: public ::data_handler::CalculationInterface::Service {
public:
virtual ~CalculationInGrpcServerImpl(){};
// Add operation
::grpc::Status Add(::grpc::ServerContext* context,
const ::data_handler::AddRequest* request,
::data_handler::AddReply* response) override;
// Multiply operation
::grpc::Status Multiply(
::grpc::ServerContext* context,
const ::data_handler::MultiplyRequest* request,
::data_handler::MultiplyReply* response) override;
};
::grpc::Status CalculationInGrpcServerImpl::Add(
::grpc::ServerContext* context,
const ::data_handler::AddRequest* request,
::data_handler::AddReply* response) {
if (!context || !request || !response) {
return ::grpc::Status::CANCELLED;
}
int32_t a = request->param1();
int32_t b = request->param2();
int32_t result = a + b;
response->set_result(result);
std::cout << "Add operation: " << a << " + " << b << std::endl;
std::cout << "The result is: " << result << std::endl;
return ::grpc::Status::OK;
}
::grpc::Status CalculationInGrpcServerImpl::Multiply(
::grpc::ServerContext* context,
const ::data_handler::MultiplyRequest* request,
::data_handler::MultiplyReply* response) {
if (!context || !request || !response) {
return ::grpc::Status::CANCELLED;
}
int32_t a = request->param1();
int32_t b = request->param2();
int32_t result = a * b;
response->set_result(result);
std::cout << "Multiply operation: " << a << " * " << b << std::endl;
std::cout << "The result is: " << result << std::endl;
return ::grpc::Status::OK;
}
// define the gRPC server
std::unique_ptr<Server> server_ptr;
CalculationInGrpcServerImpl service;
void RunServer(const std::string& server_address) {
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
server_ptr = builder.BuildAndStart();
std::cout << "Server(" << server_address << ") is listening on ..." << std::endl;
std::cout << "Press 'q' to exit the server" << std::endl;
server_ptr->Wait();
}
int main() {
std::string server_address("0.0.0.0:50051");
std::thread server_thread(RunServer, server_address);
bool running = true;
while (running) {
char c = getchar();
if (c == '\n' || c == EOF) continue;
if (c == 'q') {
// reset running flag and shutdown server
running = false;
server_ptr->Shutdown();
}
}
server_thread.join();
return 0;
}
大家有可能看到了main函數(shù),本人偷懶,將其一起寫在一個文件里了,最好還是將main函數(shù)實現(xiàn)放到另外的文件。當然我們重點是將grpc的運用,大家可以借鑒一下里面server是怎樣綁定IP和port口,運行起來server的。
生成一下,看看是不是和預想的一樣啊?
3. 客戶端代碼
客戶端代碼主要是調用服務器端的接口,就是上面寫的接口,grpc通過一個stub代理來實現(xiàn),這樣我們就象調用本地的函數(shù)一樣去遠程調用函數(shù)接口了,從而達到訪問服務的目的。
客戶端的cmakelist.txt和服務器端的有點類似,我貼出來,大家看看就行:
cmake_minimum_required(VERSION 3.20)
# Note: 8 target(s) were omitted.
message("--------" $ENV{VCPKG_ROOT})set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")
project(CalculationInGrpcClient)set(_GRPC_GRPCPP gRPC::grpc++)
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
set(_REFLECTION gRPC::grpc++_reflection)set(_PROTOBUF_LIBPROTOBUF_D libprotobufd)
find_package(gRPC CONFIG REQUIRED)
find_program(_PROTOBUF_PROTOC protoc REQUIRED)
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin REQUIRED)# Generated proto sources
get_filename_component(proto "../proto/calculation.proto" ABSOLUTE)
get_filename_component(proto_name "../proto/calculation.proto" NAME_WE)
get_filename_component(proto_path "${proto}" PATH)set(proto_srcs "${proto_path}/${proto_name}.pb.cc")
set(proto_hdrs "${proto_path}/${proto_name}.pb.h")
set(grpc_srcs "${proto_path}/${proto_name}.grpc.pb.cc")
set(grpc_hdrs "${proto_path}/${proto_name}.grpc.pb.h")
message("------------------------------------------------")
message(${_PROTOBUF_PROTOC})
message(${_GRPC_CPP_PLUGIN_EXECUTABLE})
message(${proto_path})message("-------------------------------------------------")
add_custom_command(
? ? ? OUTPUT "${proto_srcs}" "${proto_hdrs}" "${grpc_srcs}" "${grpc_hdrs}"
? ? ? COMMAND ${_PROTOBUF_PROTOC}
? ? ? ARGS --grpc_out "${proto_path}"
? ? ? ? --cpp_out "${proto_path}"
? ? ? ? -I "${proto_path}"
? ? ? ? --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
? ? ? ? "${proto}"
? ? ? DEPENDS "${proto}")# Include generated *.pb.h files
include_directories(
? ? "${proto_path}"
? ? )file(GLOB PUBLIC_HEADER ${CMAKE_CURRENT_BINARY_DIR}/*.h
? ? ? ? ? ? ? ? ? ? ? ? ${PROJECT_SOURCE_DIR}/../proto/*.h)add_executable(${PROJECT_NAME} CalculationClient.cc ${proto_srcs} ${grpc_srcs})
target_link_libraries(${PROJECT_NAME} PRIVATE gRPC::gpr gRPC::upb gRPC::grpc gRPC::grpc++)message("protobuf libs are:")
message(${_PROTOBUF_LIBPROTOBUF_D})
?下面就是要介紹客戶端的代碼模塊了,我這邊簡單封裝了一個客戶端類去調用服務,代碼如下,
大家看看簡單的request/reply調用方式。
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>
#include <calculation.grpc.pb.h>
#include <calculation.pb.h>
#include <thread>
using grpc::Server;
using grpc::ServerBuilder;
using ::grpc::ServerContext;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerWriter;
using grpc::Status;
class CalculationInGrpcClient final {
public:
CalculationInGrpcClient(CalculationInGrpcClient& param) = delete;
CalculationInGrpcClient& operator=(CalculationInGrpcClient& param) = delete;
CalculationInGrpcClient(std::shared_ptr<grpc::Channel> channelPtr);
~CalculationInGrpcClient(){};
bool RequestAddOperation(const int32_t a, const int32_t b, int32_t& result);
bool RequestMultiplyOperation(const int32_t a, const int32_t b, int32_t& result);
private:
std::unique_ptr<data_handler::CalculationInterface::Stub> mStub;
};
CalculationInGrpcClient::CalculationInGrpcClient(
std::shared_ptr<grpc::Channel> channel)
: mStub(data_handler::CalculationInterface::NewStub(channel)) {}
bool CalculationInGrpcClient::RequestAddOperation(int32_t a, int32_t b,
int32_t& result) {
grpc::Status grcpStatus;
grpc::ClientContext context;
data_handler::AddReply reply;
data_handler::AddRequest request;
request.set_param1(a);
request.set_param2(b);
grcpStatus = mStub->Add(&context, request, &reply);
if (grcpStatus.error_code() == ::grpc::StatusCode::OK) {
result = static_cast<int32_t>(reply.result());
std::cout << "After adding operation, the result is: "
<< result
<< std::endl;
return true;
} else {
std::cout << "Server not running..." << std::endl;
}
return false;
}
bool CalculationInGrpcClient::RequestMultiplyOperation(int32_t a, int32_t b,
int32_t& result) {
grpc::Status grcpStatus;
grpc::ClientContext context;
data_handler::MultiplyReply reply;
data_handler::MultiplyRequest request;
request.set_param1(a);
request.set_param2(b);
grcpStatus = mStub->Multiply(&context, request, &reply);
if (grcpStatus.error_code() == ::grpc::StatusCode::OK) {
result = static_cast<int32_t>(reply.result());
std::cout << "After Multiplication operation, the result is: "
<< result
<< std::endl;
return true;
} else {
std::cout << "Server not running..." << std::endl;
}
return false;
}
void showHelp() {
std::cout << "Calculation starts : \r\n\
Press 'q' to exit the calculator. \r\n\
"
<< std::endl;
}
bool FindParamters(const std::string& src, const char operation, int32_t& left, int32_t& right) {
auto it = src.find(operation);
if (it != std::string::npos) {
std::string leftParam = src.substr(0, it);
std::string rightParam = src.substr(it + 1, src.length() - it - 1);
left = atoi(leftParam.c_str());
right = atoi(rightParam.c_str());
return true;
}
return false;
}
int main() {
showHelp();
auto grpcChannel = grpc::CreateChannel("127.0.0.1:50051",
grpc::InsecureChannelCredentials());
if (!grpcChannel) {
printf("Failed to create gRPC channel\n");
return 0;
}
std::unique_ptr<CalculationInGrpcClient> clientPtr =
std::make_unique<CalculationInGrpcClient>(grpcChannel);
bool running = true;
while (running) {
std::string strTmp;
std::getline(std::cin, strTmp);
int32_t a = 0;
int32_t b = 0;
int32_t result = 0;
if (FindParamters(strTmp, '+', a, b)) {
if (clientPtr) {
clientPtr->RequestAddOperation(a, b, result);
}
} else if (FindParamters(strTmp, '*', a, b)) {
if (clientPtr) {
clientPtr->RequestMultiplyOperation(a, b, result);
}
} else {
// reserve
}
if (strTmp.find('q') != std::string::npos) {
// reset running flag and shutdown server
running = false;
}
}
return 0;
}
代碼里面的request, reply基本上是固定格式:
? ? grpc::Status grcpStatus;
? ? grpc::ClientContext context;
? ? data_handler::MultiplyReply reply;
? ? data_handler::MultiplyRequest request;
? ? request.set_param1(a);
? ? request.set_param2(b);
? ? grcpStatus = mStub->Multiply(&context, request, &reply);
主要是stub去調用服務器端的接口,而前的context, request, reply都是準備工作。
grpc的createChannel綁定了服務器端的IP和port,進行服務器端和客戶端通信,grpc都封裝好了,固定格式調用就行。
4. 編譯生成后,運行服務器端后,在運行客戶端
文章來源:http://www.zghlxwxcb.cn/news/detail-433001.html
?好了,就先到這里吧,代碼只是demo,大家看看就行,里面有些不嚴謹?shù)牡胤剑喽鄵鷵?span toymoban-style="hidden">文章來源地址http://www.zghlxwxcb.cn/news/detail-433001.html
到了這里,關于gRPC結合vcpkg在x64-windows平臺visual studio2019 cmake工程里面的應用的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!