目录
5. 编写CMakeLists.txt 生成*.pb.h , *.pb.c, *.grpc.pb.cc;*grpc.pb.h文件
6. 在gRPC_Server中实现proto文件定义的接口方法
7. 新建gRPC_Client文件夹,并编写Client代码,完成gRPC接口测试
1. 新建cmake工程
子项目名称为gRPC_Server,后面用做为gRPC服务,创建成功之后,VS2019将自动的帮我们新建一个gRPC_Server.h和gRPC_Server.cpp文件以及一个CMakeLists.txt脚本文件。
为了后面好管理,我们再新建cmake,gRPC_Client,depends和proto四个文件夹,其中cmake文件夹:保存一些自定义的或者第三方的cmake文件;gRPC_Client:保存gRPC客户端程序,用于测试gRPC_Server中gRPC服务接口;depends:保存第三方的依赖库、头文件和源文件等;proto:存放自定义的proto文件。
2. 编写proto文件
定义一个calculator服务,就一个接口Add方法
3 准备环境
在windows或者Linux(Ubuntu/CentOS/RetHat等)安装cMake,具体过程请参考百度,这里就不展开了。
安装好cmake后,如果你的是window系统,双击CMake应用程序,将出现下面的对话框,这个工具的目的主要是,对刚刚新建的cMake工程进行配置并生成平台相关的工程,如在windows中会生成MSVC工程,Unix中生成Makefile工程,从而实现一套代码可在不同平台上运行的目的。
4. 将第三方依赖文件放到depends中
本篇要演示的是gRPC接口的调用过程,因此依赖gRPC和protobuf两个第三方库,这里我们从官网下载相关的头文件和源文件以及release版本的库文件并将这些文件放入到depends文件夹中。
5. 编写CMakeLists.txt 生成*.pb.h , *.pb.c, *.grpc.pb.cc;*grpc.pb.h文件
首先,在根节点中的CMakeLists.txt添加子目录并设置编译器、工程名称等
接着,修改gRPC_Server中的CMakeLists.txt内容,这里我们需要做如下工作:
1)在depends文件夹中添加gRPC和protobuf的依赖库、源文件和头文件;
2)编写自定义的cmake文件:findgrpc.cmake和findprotobuf.cmake,这两个文件负责链接depends中gRPC和protobuf的依赖库、源文件和头文件;
3)修改gRPC_Server中的CMakeLists.txt,生成*.pb.h , *.pb.c, *.grpc.pb.cc;*grpc.pb.h接口文件
4)添加连接库和源文件以及头文件,生成gRPC_Server.exe
findgrpc.cmake脚本如下:
# Find required gRPC package
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else()
add_definitions(-D_WIN32_WINNT=0x600)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
set(PLATFORM windows)
else()
if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
set(PLATFORM aarch64)
else()
set(PLATFORM linux)
endif()
endif()
find_package(Threads REQUIRED)
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/depends/gRPC/${PLATFORM}/cmake)
message(STATUS ${CMAKE_SOURCE_DIR}/depends/gRPC/${PLATFORM}/cmake)
set(Protobuf_DIR ${CMAKE_SOURCE_DIR}/depends/gRPC/${PLATFORM}/cmake)
set(gRPC_DIR ${CMAKE_SOURCE_DIR}/depends/gRPC/${PLATFORM}/lib/cmake/grpc)
set(gRPC_BIN_DIR ${CMAKE_SOURCE_DIR}/depends/gRPC/${PLATFORM}/bin)
set(protobuf_MODULE_COMPATIBLE TRUE)
find_package(Protobuf CONFIG REQUIRED)
if(PROTOBUF_FOUND)
message(STATUS "Using Protobuf v${Protobuf_VERSION}")
else()
message("protobuf library is needed but can't be found")
endif()
find_package(gRPC CONFIG REQUIRED)
message(STATUS "Using grpc v${gRPC_VERSION}")
set(PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
set(GRPC_GRPCPP gRPC::grpc++)
set(GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_cpp_plugin>)
set(REFLECTION gRPC::grpc++_reflection)
set(PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>)
findprotobuf.cmake如下
# 判断系统类型
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
set(PROTOBUF_DIR_TOTAL ${CMAKE_SOURCE_DIR}/depends/Protobuf)
set(Protobuf_INCLUDE_DIR ${PROTOBUF_DIR_TOTAL}/include)
message("-------find protobuf, windows mode path: ${PROTOBUF_DIR_TOTAL}-----")
set(Protobuf_LIBRARY ${PROTOBUF_DIR_TOTAL}/lib/windows)
set(Protobuf_PROTOC_EXECUTABLE ${PROTOBUF_DIR_TOTAL}/bin/windows/protoc.exe)
set(PROTOBUF_LIB libprotobuf.lib)
set(PROTOC_LIB libprotoc.lib)
else()
if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
set(AARCH64_PATH ${PROJECT_SOURCE_DIR}/depends/Protobuf)
set(Protobuf_INCLUDE_DIR ${AARCH64_PATH}/include)
set(Protobuf_LIBRARY ${AARCH64_PATH}/lib/aarch64)
set(Protobuf_PROTOC_EXECUTABLE ${AARCH64_PATH}/bin/aarch64/protoc)
else()
set(AARCH64_PATH ${PROJECT_SOURCE_DIR}/depends/Protobuf)
set(Protobuf_INCLUDE_DIR ${AARCH64_PATH}/include)
set(Protobuf_LIBRARY ${AARCH64_PATH}/lib/linux)
set(Protobuf_PROTOC_EXECUTABLE ${AARCH64_PATH}/bin/linux/protoc)
endif()
endif()
set(PROTOBUF_LIB protobuf)
set(PROTOC_LIB protoc)
# 加载依赖包
find_package(Protobuf REQUIRED)
if(PROTOBUF_FOUND)
message(STATUS "Protobuf FOUND!" ${PROTOBUF_INCLUDE_DIRS})
include_directories(${PROTOBUF_INCLUDE_DIRS})
link_directories(${Protobuf_LIBRARY})
else()
message(FATAL_ERROR "protobuf library is needed but can't be found")
endif()
gRPC_Server中的CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
# 设置工程名称
project ("gRPC_Server")
set(CMAKE_PROJECT_NAME gRPC_Server)
# 包含gRPC和protobuf头文件和proto接口文件
include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/findgrpc.cmake")
include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/findprotobuf.cmake")
# 加载依赖包
find_package(protobuf CONFIG REQUIRED)
find_package(gRPC CONFIG REQUIRED)
find_package(Threads)
# 获取文件和路径
set(grpc_proto_file_name calculator)
get_filename_component(grpc_proto "${CMAKE_CURRENT_SOURCE_DIR}/../proto/${grpc_proto_file_name}.proto" ABSOLUTE)
get_filename_component(grpc_proto_path "${grpc_proto}" PATH)
# Cmake find modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
# 获取文件和路径
set(grpc_proto_file_name calculator)
set(grpc_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/${grpc_proto_file_name}.pb.cc")
set(grpc_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/${grpc_proto_file_name}.pb.h")
set(grpc_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/${grpc_proto_file_name}.grpc.pb.cc")
set(grpc_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/${grpc_proto_file_name}.grpc.pb.h")
# 生成 *.ph.cc.h 文件 by protoc.exe
add_custom_command(
OUTPUT "${grpc_proto_srcs}"
"${grpc_proto_hdrs}"
"${grpc_grpc_srcs}"
"${grpc_grpc_hdrs}"
COMMAND ${PROTOBUF_PROTOC}
ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
-I "${grpc_proto_path}"
--plugin=protoc-gen-grpc="${GRPC_CPP_PLUGIN_EXECUTABLE}"
"${grpc_proto}"
DEPENDS "${grpc_proto}"
)
# 添加头文件
set(GRPCSERVER_HDRS_DIR
${CMAKE_CURRENT_BINARY_DIR}
${grpc_grpc_hdrs}
${grpc_proto_hdrs}
${PROTO_HDRS}
${DEFAULT_INCLUDE_DIRECTORIES}
"gRPC_Server.h"
)
# 通过这行,后面在编译时,将自动的把这些头文件连接到我们的工程中,注意,这里新手容易入坑!
include_directories(${GRPCSERVER_HDRS_DIR})
# 添加源文件
set(GRPCSERVER_SRC_FILES
${grpc_proto_srcs}
${grpc_grpc_srcs}
${PROTO_SRCS}
"gRPC_Server.cpp"
)
# 关闭优化
SET_SOURCE_FILES_PROPERTIES(${GRPCSERVER_SRC_FILES} PROPERTIES COMPILE_FLAGS -Od)
# 添加编译源文件
add_executable(${PROJECT_NAME} ${GRPCSERVER_SRC_FILES})
# 添加目标连接库
target_link_libraries(${PROJECT_NAME}
${REFLECTION}
${GRPC_GRPCPP}
${PROTOBUF_LIBPROTOBUF}
${DEFAULT_LIBRARIES}
)
单击Rebuild生成gRPC_Server.exe和gRPC和Protobuf的接口文件
6. 在gRPC_Server中实现proto文件定义的接口方法
首先,我们需要在gRPC_Server中引用刚刚生成的接口文件calculator.grpc.pb.h和grpc++/grpc++.h
接着,实现接口方法,具体如下图所示:
#include "gRPC_Server.h"
#include "calculator.grpc.pb.h"
#include "grpc++/grpc++.h"
using namespace std;
class CalcualtorService : public calc::Caltulator::Service
{
public:
virtual ::grpc::Status Add(::grpc::ServerContext* context, const ::calc::InParams* request, ::calc::OutParams* response) override
{
response->set_sum(request->a() + request->b());
return grpc::Status::OK;
}
};
int main()
{
std::string server_address("0.0.0.0:5152");
CalcualtorService calcSrv;
grpc::ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&calcSrv);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
server->Wait();
return 0;
}
最后,我们再重新编译
7. 新建gRPC_Client文件夹,并编写Client代码,完成gRPC接口测试
在客户端中添加main.cpp,测试代码如下:
#include <iostream>
#include<cstdio>
#include <memory>
#include "calculator.grpc.pb.h"
#include "grpc++/grpc++.h"
class Client
{
public:
Client(std::shared_ptr<grpc::Channel> channel) : stub_(calc::Caltulator::NewStub(channel))
{}
google::protobuf::int32 Add(google::protobuf::int32 a, google::protobuf::int32 b)
{
calc::InParams request;
request.set_a(a);
request.set_b(b);
calc::OutParams response;
grpc::ClientContext context;
grpc::Status status = stub_->Add(&context, request, &response);
if (status.ok())
{
return response.sum();
}
else
{
return -1;
}
}
private:
std::unique_ptr<calc::Caltulator::Stub> stub_;
};
int main()
{
std::cout << "=====================Test Add==================" << std::endl;
Client client(grpc::CreateChannel("localhost:5152", grpc::InsecureChannelCredentials()));
auto result = client.Add(1, 260);
std::cout << "1 + 260 = " << result << std::endl;
getchar();
return 0;
}
编写CMakeLists.txt脚本文件
cmake_minimum_required (VERSION 3.8)
# 设置工程名称并选择编译语言c++
project(gRPC_Client C CXX)
# 设置工程名称
set(CMAKE_PROJECT_NAME ${PROJECT_NAME})
# 检查平台
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
set(PLATFORM windows)
else()
if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
set(PLATFORM aarch64)
else()
set(PLATFORM linux)
endif()
endif()
# 包含头文件
set(CMAKE_GRPC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/depends/gRPC/${PLATFORM}/include")
# calculator.pb.cc文件在gRPC_Server文件夹中,需要注意下路径,这里也是新手容易掉坑的地方
set(CMAKE_CURRENT_BIN_DIR "${CMAKE_CURRENT_BINARY_DIR}/../gRPC_Server")
include_directories(${CMAKE_CURRENT_BIN_DIR} ${CMAKE_GRPC_DIR})
message(STATUS "[CMAKE_CURRENT_BIN_DIR]->" ${CMAKE_CURRENT_BIN_DIR})
# 包含第三方脚本
include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/findgrpc.cmake")
# 获取文件和路径
set(grpc_proto_file_name calculator)
set(grpc_proto_srcs "${CMAKE_CURRENT_BIN_DIR}/${grpc_proto_file_name}.pb.cc")
set(grpc_proto_hdrs "${CMAKE_CURRENT_BIN_DIR}/${grpc_proto_file_name}.pb.h")
set(grpc_grpc_srcs "${CMAKE_CURRENT_BIN_DIR}/${grpc_proto_file_name}.grpc.pb.cc")
set(grpc_grpc_hdrs "${CMAKE_CURRENT_BIN_DIR}/${grpc_proto_file_name}.grpc.pb.h")
# 包含编译的源文件
aux_source_directory(. DIR_SRCS)
set(CMAKECLIENT_SRC_FILES
${grpc_proto_srcs}
${grpc_grpc_srcs}
${PROTO_SRCS}
${DIR_SRCS}
)
# 包含头文件
set(CMAKECLIENT_HDRS_DIR
${CMAKE_CURRENT_BINARY_DIR}
${grpc_grpc_hdrs}
${grpc_proto_hdrs}
${PROTO_HDRS}
${DEFAULT_INCLUDE_DIRECTORIES}
)
include_directories(${CMAKECLIENT_HDRS_DIR})
message(STATUS "[CMAKECLIENT_SRC_FILES]->"${PROJECT_NAME} ${CMAKECLIENT_SRC_FILES})
add_executable(${PROJECT_NAME} ${CMAKECLIENT_SRC_FILES})
# 添加目标连接库
target_link_libraries(${PROJECT_NAME}
${REFLECTION}
${GRPC_GRPCPP}
${PROTOBUF_LIBPROTOBUF}
${DEFAULT_LIBRARIES}
)
最后在根节点中添加刚刚新建的子目录
# 最低版本
cmake_minimum_required (VERSION 3.8)
# 工程名称
project ("CMake_GRPC")
# 设置release模式
set(CMAKE_BUILD_TYPE "Release")
# 选择编译语言c++
project(CMakeServer C CXX)
# 设置编译器
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else()
add_definitions(-D_WIN32_WINNT=0x600)
endif()
# 添加子工程
add_subdirectory ("gRPC_Server")
# 添加客户端子工程
add_subdirectory ("gRPC_Client")
重新编译
8. 测试gRPC接口
先打开服务端程序;接着打开客户端程序,测试结果如下:
参考文献:
[1] https://blog.csdn.net/vc66vcc/article/details/80597027
[2] https://blog.csdn.net/weixin_28927079/article/details/97262243
总结:
以上是VS2019创建cMake工程并实现gRPC客户服务端程序的基本过程,希望通过简单的例子让大家对CMake、gRPC和protobuf有个基本的认识。为大家跨平台编程方面提供一点小小的参考。今天先写到这里,后面会将代码链接地址一同附上,希望对大家工作有助,谢谢!
代码链接地址:https://download.csdn.net/download/pathfinder1987/19733881?spm=1001.2014.3001.5501