來源:微信公衆号「程式設計學習基地」
文章目錄
-
-
- proto檔案編輯
-
- 生成C++代碼
- 序列化接口
- grpc server端
-
- 命名空間
- 重寫服務
- 啟動服務
- 完整代碼
- grpc client端
-
- 命名空間
- 定義用戶端
- 建立通道
- 完整代碼
-
proto檔案編輯
syntax = "proto3";
package helloworld;
// 定義服務
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 helloworld;
是
helloworld
第五行:grpc服務的名稱
service Greeter
是
Greeter
後面的請求響應結構,無論名字是大寫還是小寫,最終調用的時候都是小寫。
生成C++代碼
protoc --cpp_out=. helloworld.proto
生成C++代碼helloworld.pb.h和helloworld.pb.cc。
生成grpc代碼
protoc -I ./ --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` helloworld.proto
序列化接口
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中解析出消息對象
grpc server端
命名空間
記住首先一定要開放命名空間
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::PlayStartReq;
using helloworld::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>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::PlayStartReq;
using helloworld::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;
}
grpc client端
命名空間
記住首先一定要開放命名空間
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::PlayStartReq;
using helloworld::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>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::PlayStartReq;
using helloworld::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;
}