无论什么语言,实现grpc的核心是.proto文件,通过protoc命令将.proto文件生成grpc框架的接口文件*gb*, 该文件中包含.proto定义的方法及参数接口,服务器,客户端相关操作接口;然后再编写服务器,客户端代码,调用*gb*文件生成的接口。
直接实例代码介绍其过程:
核心文件:hello.proto
syntax = "proto3"; // 规定使用proto3的语法
service MsgService { // 定义服务, 流数据放到括号里面
rpc GetMsg (MsgRequest) returns (MsgResponse){}
}
message MsgRequest { // 请求的结构, 也可以定义int32,int64,double,float等数据类型, 等号后面的数字表示第几个
string name = 1;
}
message MsgResponse { // 回应的结果
string msg = 1;
}
然后执行:
protoc -I . --cpp_out=. ./hello.proto#生成hello.pb.cc文件
protoc -I . --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ./hello.proto#生成hello.grpc.pb.cc文件
或者
protoc -I . --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ./hello.proto#一起生成两个文件
-I :(-IPATH)指定要在其中搜索导入(import)的目录。可指定多次,目录将按顺序搜索。如果没有给出,则使用当前工作目录。
--cpp_out = . : 以c++语言格式输出,等号后面为输出文件存放的路径
--grpc_out = . :输出grpc框架接口文件。等号后面为输出文件存放的路径
--plugin=`which grpc_cpp_plugin` :指定一个protobuf插件(grpc_cpp_plugin)来生成grpc代码。
hello.proto : 核心文件,可以是路径./hello.proto,或者绝对路径。
执行成功后,指定的目录下会生成两个文件:
hello_pb.cc/hello_pb.h:主要是对参数(MsgRequest,MsgResponse)的属性设置,参数类的重定向,参数成员的设置,获取等操作。
hello_pb_grpc.cc/hello_grpc_pb.h :该文件生成了proto方法(GetMsg)的属性操作接口,通过该存根实现服务器与客户端的通讯
生成这两个文件之后就可以编写服务器,客户端代码了:
服务器代码(hello_server.cc):
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "hello.grpc.pb.h"
#include "hello.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
//创建类,继承MsgService::Service, 父类在生成的hello.grpc.pb.cc/h文件中,属于grpc的服务端接口类
class MyServer final : public MsgService::Service{
Status GetMsg(ServerContext* context, const MsgRequest* request, MsgResponse* reply) override{//hello.grpc.pb文件生成的类接口
std::string str("Hello ");
reply->set_msg( str + request->name());//hello.pb文件生成的类接口,设置回复结构的成员数据
return Status::OK;
}
};
void RunServer(){
std::string server_address("0.0.0.0:50051");
MyServer service;
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "server listen on ..." << server_address << std::endl;
server->Wait();
}
int main(int argc, char **argv)
{
RunServer();
return 0;
}
客户端:
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h> // 和python一样, import grp
// 在包含两个信息和应用的头文件
#include "hello.grpc.pb.h"
#include "hello.pb.h"
// 这是通用工具
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
class MsgServiceClient {
public:
MsgServiceClient(std::shared_ptr<Channel> channel)
: stub_(MsgService::NewStub(channel)) {}
MsgResponse GetMsg(const std::string& user, int num1, double num2) {
// 请求数据数据格式化到request
MsgRequest request;
request.set_name(user);
request.set_num1(num1);
request.set_num2(num2);
// 服务器返回端
MsgResponse reply;
//客户端上下文。它可以用来传递额外的信息
//服务器和/或调整某些RPC行为。
ClientContext context;
// The actual RPC.
Status status = stub_->GetMsg(&context, request, &reply);
if (status.ok()) {
return reply;
} else {
std::cout << status.error_code() << ": " << status.error_message()
<< std::endl;
return reply;
}
}
private:
std::unique_ptr<MsgService::Stub> stub_;
};
int main(int argc, char** argv) {
MsgServiceClient z_msg(grpc::CreateChannel(
"localhost:50051", grpc::InsecureChannelCredentials()));
std::string user("world");
MsgResponse reply = z_msg.GetMsg(user);
std::cout<<reply.msg()<<std::endl;
printf("num1 = %d; num2=%f\n", reply.num1(), reply.num2());
return 0;
}
cmake编译:
CMakeLists.txt编写:
cmake_minimum_required(VERSION 3.5)
project(test2)
include(/XXX/gRPC/grpc/examples/cpp/cmake/common.cmake)#调用grpc环境配置,该路径为绝对路径
set(PWDIR ${CMAKE_CURRENT_SOURCE_DIR})
set(GRPCDIR "/XXX/gRPC/grpc/cmake/build/_install")#编译grpc时指定的安装路径
include_directories(
${PWDIR}
${GRPCDIR}/include
)
link_directories(
${GRPCDIR}/lib
)
add_library(hw_grpc_proto
"${PWDIR}/hello.grpc.pb.cc"
"${PWDIR}/hello.pb.cc"
)
target_link_libraries(hw_grpc_proto protobuf protobuf-lite protoc grpc++_reflection gRPC::grpc++)
foreach(_target
hello_server hello_client)
add_executable(${_target} "${_target}.cpp")
target_link_libraries(${_target}
hw_grpc_proto)
endforeach()
后期会发布一篇grpc程序编写的完整流程,从proto,*.pb.cc, *.grpc.pb.cc解析 到服务器 客户端代码编写,cmake编写,返回状态设置,.cmake文件解析, rpc流式传输 的全面刨析,让您一篇文章学会grpc。
感兴趣的话关注。一起交流学习。