proto 文件
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
编译 proto 文件
protoc --cpp_out=. helloworld.proto
protoc --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/bin/grpc_cpp_plugin helloworld.proto
获得四个文件
服务端代码
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "helloworld.grpc.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("wanjun Hello ");
reply->set_message(prefix + request->name());
return Status::OK;
}
};
void RunServer() {
std::string server_address("0.0.0.0:50051");
GreeterServiceImpl service;
ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
// 监听给定的地址
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *synchronous* service.
builder.RegisterService(&service);
// Finally assemble the server.
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
// Wait for the server to shutdown. Note that some other thread must be
// responsible for shutting down the server for this call to ever return.
server->Wait();
}
int main(int argc, char** argv) {
RunServer();
return 0;
}
编译指令
g++ -std=c++11 *.cc -o server -pthread -lprotobuf -lgrpc++
编译成功后便可执行
详细解析
class GreeterServiceImpl final : public Greeter::Service
我们的Greeter服务器自动生成了一个嵌套类Service,用于继承并实现我们的虚函数。
在server.cc文件中,我们构造了Service的子类对象GreeterServiceImpl用于实现我们特定的方法。
Greeter::Service 源码
class Service : public ::grpc::Service {
public:
Service();
virtual ~Service();
// Sends a greeting
virtual ::grpc::Status SayHello(::grpc::ServerContext* context, const ::helloworld::HelloRequest* request, ::helloworld::HelloReply* response);
};
由此可见只有一个虚函数SayHello
该虚函数在 .cc 文件中的实现为
::grpc::Status Greeter::Service::SayHello(::grpc::ServerContext* context, const ::helloworld::HelloRequest* request, ::helloworld::HelloReply* response) {
(void) context;
(void) request;
(void) response;
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
并没有任何任何实现,只是给虚函数提供了一个基类方法。
SayHello 方法的参数如下
grpc::ServerContext context 上下文
helloworld::HelloRequest request 传入的参数(请求)
helloworld::HelloReply* response 传出的结果(回复)
在 .proto 文件中我们已经定义了 HelloRequest & HelloReply 两个Message
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("wanjun Hello ");
reply->set_message(prefix + request->name());
return Status::OK;
}
override 表示覆盖基类中的虚函数,在实现中为获取了每个request的 name field ,并且添加了一个前缀作为reply返回。
RunServer() 函数
void RunServer() {
std::string server_address("0.0.0.0:50051");
GreeterServiceImpl service;
ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
// 监听给定的地址
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *synchronous* service.
builder.RegisterService(&service);
// Finally assemble the server.
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
// Wait for the server to shutdown. Note that some other thread must be
// responsible for shutting down the server for this call to ever return.
server->Wait();
}
ServerBuilder 作为最重要的一个类
/// A builder class for the creation and startup of \a grpc::Server instances.
一个builder用于创建和开启一个grpc服务器实例
ServerBuilder& AddListeningPort(const grpc::string& addr_uri,
std::shared_ptr<ServerCredentials> creds,
int* selected_port = nullptr);
为一个grpc::Server绑定一个端口。
ServerBuilder& RegisterService(Service* service);
注册一个服务,函数调用并不会获得service的所有权。
virtual std::unique_ptr<Server> BuildAndStart();
返回一个运行的服务器,已经准备好了去处理函数调用。
智能指针 std::unique_ptr 会自动清理内存。
Server
/// Block until the server shuts down.
/// \warning The server must be either shutting down or some other thread must
/// call \a Shutdown for this function to ever return.
void Wait() override;
阻塞等待服务器关闭
Client 客户端
#include <string>
#include <iostream>
#include <memory>
#include <grpcpp/grpcpp.h>
#include "helloworld.grpc.pb.h"
using grpc::ClientContext;
using grpc::Channel;
using grpc::Status;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::Greeter;
// static method : Greeter::NewStub
class wjClient{
public:
wjClient(std::shared_ptr<Channel> channel)
:stub_(Greeter::NewStub(channel)){
}
std::string SayHello(std::string name){
ClientContext context;
HelloReply reply;
HelloRequest request;
request.set_name(name);
Status status = stub_->SayHello(&context,request,&reply);
if(status.ok()){
return reply.message();
}else{
return "failure";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc,char *argv[]){
auto channel = grpc::CreateChannel("0.0.0.0:5001",grpc::InsecureChannelCredentials());
wjClient client(channel);
// block until get result from RPC server
std::string result = client.SayHello("wanjun");
printf("get result [%s]\n",result.c_str());
return 0;
}
解析
在Greeter 类中 StubInterface 类实现了提供给 Client 客户端使用的接口
class Stub final : public StubInterface
表示 Stub 继承自 StubInterface 并且不可被再继承,在我们的客户端代码中当做 wjClient 的私有变量 stub_ 使用。
stub_ 的赋值使用 Greeter::NewStub() 类静态方法获取,后续调用 SayHello() 完成对 RPC server 远程服务器的调用。
Channel 通道
每个 Client 都需要 Channel 来实现对远程服务器的调用,使用 grpc::CreateChannel() 函数来创建一个通信管道。