1、 proto文件编辑

player.proto

syntax = "proto3";
package Player;

// 定义服务
service Greeter {
  // 定义服务函数
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc PlayStart (PlayStartReq) returns (PlayStartRes){}  
}

// 定义SayHello服务的请求结构
message HelloRequest {
  string name = 1;
  string test = 2;
}

// 定义SayHello服务的响应结构
message HelloReply {
  string message = 1;
}

// 定义播放请求结构
message PlayStartReq {
  uint32 sessionid  = 1;   
  string eleStmName = 2;
}
// 定义播放响应结构
message PlayStartRes{
  int32  result     = 1; 
  string extension    = 2;
}

第二行:包的名字 package Player; 是 Player

第五行:grpc服务的名称 service Greeter 是 Greeter

后面的请求响应结构,无论名字是大写还是小写,最终调用的时候都是小写。

2、生成C++代码

protoc --cpp_out=. player.proto

protoc --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/bin/grpc_cpp_plugin player.proto

生成C++代码player.pb.h和player.pb.cc。

pkg-config --libs protobuf -L/usr/local/lib/x86_64-linux-gnu -lprotobuf -pthread

3、序列化接口

bool SerializeToString(string* output) const

序列化消息,将消息对象以string方式输出。

bool ParseFromString(const string& data)

反序列化消息,解析给定的string为消息对象

bool SerializeToOstream(ostream* output) const

序列化消息,将消息对象写入给定的c++ ostream中

bool ParseFromIstream(istream* input)

反序列化消息,从给定的c++ istream中解析出消息对象

4、grpc server端

命名空间

记住首先一定要开放命名空间

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using Player::Greeter;
using Player::HelloReply;
using Player::HelloRequest;
using Player::PlayStartReq;
using Player::PlayStartRes;

重写服务

定义服务端的类,继承 proto 文件定义的 grpc 服务 Greeter ,重写 grpc 服务定义的方法

class GreeterServiceImpl : public Greeter::Service {
  //重写 hello 方法
  virtual Status SayHello(ServerContext* context, const HelloRequest* request,HelloReply* reply) override {
    std::string prefix("Hello ");
    std::cout << "test:"<<request->test() << std::endl;
    reply->set_message(prefix + request->name());
    return Status::OK;
  }
  //重写 开流 方法
  virtual Status PlayStart(ServerContext* context, const PlayStartReq* request,PlayStartRes* reply) override {
    std::string prefix("Hello ");
    std::cout << "session id:" << request->sessionid() << std::endl;
    std::cout << "eleStmName:" << request->elestmname() << std::endl;
    reply->set_result(10);
    reply->set_extension("extension");
    return Status::OK;
  }
};

在重写方法的时候可以通过 request 结构里面的成员名函数获取改成员,例如:

message PlayStartReq {
  uint32 sessionid  = 1;   
  string eleStmName = 2;
}
const PlayStartReq* request;
std::cout << "session id:" << request->sessionid() << std::endl;
std::cout << "eleStmName:" << request->elestmname() << std::endl;

同时通过 reply 结构里面的 set_ + 成员名 函数设置成员,例如:

message PlayStartRes{
  int32  result     = 1; 
  string extension    = 2;
}
PlayStartRes* reply;
reply->set_result(10);
reply->set_extension("extension");

启动服务

std::string server_address("0.0.0.0:50051");
  /* 定义重写的服务类 */
  GreeterServiceImpl service;

  /* 创建工厂类 */
  ServerBuilder builder;
  /* 监听端口和地址 */
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());

  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIME_MS, 5000);
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 10000);
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
  /* 注册服务 */
  builder.RegisterService(&service);
  /** 创建和启动一个RPC服务器*/
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;
  /* 进入服务事件循环 */
  server->Wait();

完整代码

#include <iostream>
#include <memory>
#include <string>

#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>


#include "player.grpc.pb.h"
#include "player.grpc.pb.h"

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using Player::Greeter;
using Player::HelloReply;
using Player::HelloRequest;
using Player::PlayStartReq;
using Player::PlayStartRes;

class GreeterServiceImpl : public Greeter::Service {
  //重写 hello 方法
  virtual Status SayHello(ServerContext* context, const HelloRequest* request,HelloReply* reply) override {
    std::string prefix("Hello ");
    std::cout << "test:"<<request->test() << std::endl;
    reply->set_message(prefix + request->name());
    return Status::OK;
  }
  //重写 开流 方法
  virtual Status PlayStart(ServerContext* context, const PlayStartReq* request,PlayStartRes* reply) override {
    std::string prefix("Hello ");
    std::cout << "session id:" << request->sessionid() << std::endl;
    std::cout << "eleStmName:" << request->elestmname() << std::endl;
    reply->set_result(10);
    reply->set_extension("extension");
    return Status::OK;
  }
};

void RunServer() {
  std::string server_address("0.0.0.0:50051");
  /* 定义重写的服务类 */
  GreeterServiceImpl service;

  /* 创建工厂类 */
  ServerBuilder builder;
  /* 监听端口和地址 */
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());

  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIME_MS, 5000);
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 10000);
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
  /* 注册服务 */
  builder.RegisterService(&service);
  /** 创建和启动一个RPC服务器*/
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;
  /* 进入服务事件循环 */
  server->Wait();
}

int main(int argc, char** argv) {
  RunServer();

  return 0;
}

5、 grpc client端

命名空间

记住首先一定要开放命名空间

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using Player::Greeter;
using Player::HelloReply;
using Player::HelloRequest;
using Player::PlayStartReq;
using Player::PlayStartRes;

定义客户端

定义客户端的类,实现两个方法用来发送grpc请求以及接收grpc响应

class GreeterClient
{
public:
    GreeterClient(std::shared_ptr<Channel> channel)
        : stub_(Greeter::NewStub(channel)) {}
    std::string SayHello(const std::string &user)
    {
        HelloRequest request;
        request.set_name(user);
        request.set_test("test");
        HelloReply reply;
        ClientContext context;
        Status status = stub_->SayHello(&context, request, &reply);
        if (status.ok())
        {
            return reply.message();
        }
        else
        {
            std::cout << status.error_code() << ": " << status.error_message()<< std::endl;
            return "RPC failed";
        }
    }
    
std::string Test(const std::string &user)
{
    ClientContext context;
    PlayStartReq req;
    PlayStartRes res;
    req.set_sessionid(10);
    req.set_elestmname("BStar");

    Status status = stub_->PlayStart(&context, req, &res);

    if (status.ok())
    {
        std::cout << "result:" << res.result() << std::endl;
        return res.extension();
    }
    else
    {
        std::cout << status.error_code() << ": " << status.error_message()<< std::endl;
        return "RPC failed";
    }
}
private:
    std::unique_ptr<Greeter::Stub> stub_;
};

主要是定义两个方法用来发送grpc请求以及接收grpc响应

同样在实现方法的时候可以通过 request 结构里面的 set_ + 成员名 函数设置成员

message PlayStartReq { uint32 sessionid = 1;
string eleStmName = 2; }

PlayStartReq req; req.set_sessionid(10); req.set_elestmname("BStar");

同时通过 request 结构里面的成员名函数获取改成员

message PlayStartRes{ int32 result = 1; string extension = 2; } PlayStartRes res; std::cout << "result:" << res.result() << std::endl; 创建通道 定义客户端的时候初始化通道

//定义服务端口和地址 std::string target_str = "localhost:50051"; //创建通道 GreeterClient greeter(grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials())); std::string user("world"); //std::string reply = greeter.SayHello(user); //发送请求 std::string reply = greeter.Test(user); std::cout << "Greeter received: " << reply << std::endl;

完整代码

#include <iostream>
#include <memory>
#include <string>

#include <grpcpp/grpcpp.h>


#include "player.grpc.pb.h"


using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using Player::Greeter;
using Player::HelloReply;
using Player::HelloRequest;
using Player::PlayStartReq;
using Player::PlayStartRes;

class GreeterClient
{
public:
    GreeterClient(std::shared_ptr<Channel> channel)
        : stub_(Greeter::NewStub(channel)) {}

    // Assembles the client's payload, sends it and presents the response back
    // from the server.
    std::string SayHello(const std::string &user)
    {
    
        HelloRequest request;
        request.set_name(user);
        request.set_test("Greeter test");
    
        HelloReply reply;
        ClientContext context;
        Status status = stub_->SayHello(&context, request, &reply);
    
        if (status.ok())
        {
            return reply.message();
        }
        else
        {
            std::cout << status.error_code() << ": " << status.error_message()
                      << std::endl;
            return "RPC failed";
        }
    }
    std::string Test(const std::string &user)
    {
        ClientContext context;
        PlayStartReq req;
        PlayStartRes res;
        req.set_sessionid(10);
        req.set_elestmname(user);
    
        Status status = stub_->PlayStart(&context, req, &res);
    
        if (status.ok())
        {
            std::cout << "result:" << res.result() << std::endl;
            return res.extension();
        }
        else
        {
            std::cout << status.error_code() << ": " << status.error_message() << std::endl;
            return "RPC failed";
        }
    }

private:
    std::unique_ptr<Greeter::Stub> stub_;
};

int main(int argc, char **argv)
{
    //定义服务端口和地址
    std::string target_str = "localhost:50051";
    std::string reply;
    //创建通道
    GreeterClient greeter(grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
    std::string user("world");
    reply = greeter.SayHello(user);
    std::cout << "Greeter received: " << reply << std::endl;
    //发送请求
    reply = greeter.Test(user);
    std::cout << "Greeter received: " << reply << std::endl;

    return 0;

}

参考链接:https://github.com/0voice

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐