学习【gRPC C++简单示例及代码】
文章目录
- 0. 启动程序
- 1. 未作修改部分
- 1.1 demo.proto
- 2. 添加注释部分
- 2.1 server.cc
- 2.2 client.cc
- 3. 重点修改部分
- 3.1 CMakeLists.txt原始代码
- 3.2 CMakeLists.txt精简修改代码
【gRPC C++简单示例及代码】原文链接
已能够进行初步修改
新建fyo文件夹,在fyo中新建build、include文件夹
进入build文件夹,输入cmake ..
和make -j
,实现了链接中下列代码的功能:
protoc --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` demo.proto
protoc --cpp_out=. demo.proto
0. 启动程序
终端1
./demoServer
终端2
./demoClient
1. 未作修改部分
1.1 demo.proto
syntax = "proto3";
package ZYF;
service YServer {
rpc GetNum(Num) returns (Res) {}
rpc GetVec(stream Vec) returns (stream Vec) {}
}
message Num {
int32 x = 1;
}
message Res {
int32 y = 1;
}
message Vec {
repeated int32 v = 1;
}
2. 添加注释部分
2.1 server.cc
#include <iostream>
#include <string>
#include <grpcpp/grpcpp.h>
#include "demo.grpc.pb.h"
class DemoServiceImpl final : public ZYF::YServer::Service {
// Simple request and response
grpc::Status GetNum(grpc::ServerContext* context, const ZYF::Num* req, ZYF::Res* res) override {
std::cout << "Req get:" << req->x() << std::endl; // fyo: 注意到此处的形式是'req->x()',若改成'req.x()'会报错
res->set_y(req->x() + 1); // fyo: 此处根据'req->x()'的值,利用'set_y()'对y进行赋值
// fyo: 然而set_y()尚未定义,对此作何解释?
return grpc::Status::OK; // fyo: 返回个啥?
}
// Based on stream
grpc::Status GetVec(grpc::ServerContext* context, grpc::ServerReaderWriter<ZYF::Vec, ZYF::Vec>* stream) override {
ZYF::Vec req;
while (stream->Read(&req)) { // fyo: 怎么就得到循环5次?而且这里的req不是从外面传进来的,而是内部定义的?
std::cout << "[stream]: Received message!" << std::endl; // fyo:
auto vec = req.v(); // fyo: 注意到此处的形式是'req.v()'
for (auto it = vec.begin(); it != vec.end(); it++) { // fyo: 怎么就能够得到vec的长度为10???
(*it)++;
}
stream->Write(req); // fyo: 在这5次循环里面,把什么作为req写到stream里面?
// fyo: 从req到vec,最后又用req,闹着玩??
}
return grpc::Status::OK;
}
};
void RunServer() {
std::string serverAddr("0.0.0.0:50051"); // fyo: 确定地址端口?
DemoServiceImpl service; // fyo: 基于上面定义的DemoServiceImpl类进行实例化?但是暂不调用其内部的函数
grpc::ServerBuilder builder; // fyo: 创建builder
builder.AddListeningPort(serverAddr, grpc::InsecureServerCredentials()); // fyo: 调用builder函数
builder.RegisterService(&service); // fyo: 调用builder函数
std::unique_ptr<grpc::Server> server(builder.BuildAndStart()); // fyo: 于此处创建server?
std::cout << "Server listening on:" << serverAddr << std::endl; // fyo: 打印端口地址
server->Wait(); // fyo: 在server持续等待的过程中,一经启动,就会触发DemoServiceImpl()函数,可以重复无数遍!
// fyo: wait过程中,什么是触发条件?为什么触发后仍然会恢复到wait状态,而不是直接结束?
}
int main(int argc, char** argv) { // fyo: main函数
RunServer(); // fyo: 调用RunServer函数
return 0;
}
2.2 client.cc
#include <iostream>
#include <string>
#include <grpcpp/grpcpp.h>
#include "demo.grpc.pb.h"
class Client {
public:
Client(std::shared_ptr<grpc::Channel> channel):stub_(ZYF::YServer::NewStub(channel)){}
int SendRequest() {
ZYF::Num req; // fyo: 'ZYF::Num'指的是proto中定义的Num?以Num为准对req进行实例化?
req.set_x(3); // fyo: 此处的'set_x'尚未经定义呀?咋就直接给定义上数值3了?
// fyo: 且server.cc文件中是以'req->x()'的形式方可print,与此处'req.set_x(3)'形式不同
ZYF::Res res; // fyo: 'ZYF::Res'指的是proto中定义的Res?
// fyo: 以Res为准对res进行实例化?
grpc::ClientContext context; // fyo: 利用grpc::ClientContext对context进行实例化*1
grpc::Status status = stub_->GetNum(&context, req, &res); // fyo: 调用server.cc文件中的DemoServiceImpl类中的GetNum?
// fyo: 还是调用server.cc文件中的grpc::Status包含的GetNum?
if (!status.ok()) {
return -1;
}
return res.y(); // fyo: 最终值取4,server.cc文件中的GetNum中进行了一个未经定义的'res->set_y(req->x() + 1)',应当是该处将res.y()定义为3+1=4
}
// Send requests based on gRPC stream
void SendRequestStream() {
grpc::ClientContext context; // fyo: 利用grpc::ClientContext对context进行实例化*2,有无必要再来一次?
std::shared_ptr<grpc::ClientReaderWriter<ZYF::Vec, ZYF::Vec>> stream(stub_->GetVec(&context)); // fyo: 调用server.cc文件中的GetVec?
for (int i = 0; i < 5; ++i) {
ZYF::Vec req; // fyo: 5次循环中,每次都以Vec定义一遍req
for (int j = 0; j < 10; ++j) { // fyo: 遍历10次,j依次+1
req.add_v(j); // fyo: 把j的值赋给req,覆盖掉之前的值还是添加新值?另外,此处的'add_v'同上面的'set_x'一样未经定义?
// fyo: 难道实际上是把值赋给'ZYF::Vec'???
// fyo: 此处'add_v'不同于前面的'set_x'和'set_y'
}
if (!stream->Write(req)) { // fyo: 是何用意?
std::cout << "The stream has been closed!" << std::endl; // fyo: 打印此条,证明程序运行中断
break;
}
std::cout << "Send a message based on stream!" << std::endl; // fyo: 打印此条,证明越过前几行代码中的条件,顺利执行后续代码
}
stream->WritesDone(); // fyo: 是何用意?要传的数据写完了?谁写的?往哪里写的?
ZYF::Vec res; // fyo: 与上文中定义req不同,此处以Vec定义res
while (stream->Read(&res)) { // fyo: 当res里面尚存未读的内容即为真?一共循环5次?怎么会循环5次?
// fyo: 难道以res同名同姓一共有5个?的源自上文代码中对'ZYF::Vec req'的“循环定义”?
std::cout << "Receive a reply!" << std::endl;
auto reply = res.v(); // fyo: 将res中的v()值赋给reply
for (auto it = reply.begin(); it != reply.end(); it++) { // fyo: 遍历reply的所有值
std::cout << (*it) << " " << std::endl;
}
std::cout << std::endl;
}
stream->Finish(); // fyo: 是何用意?数据传完了?谁传的?往哪里传的?
// fyo: 通篇也没有明说要往stream里面写数据啊
}
private:
std::unique_ptr<ZYF::YServer::Stub> stub_;
};
int main(int argc, char** argv) {
Client client(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
std::cout << "Got Response:" << client.SendRequest() << std::endl; // fyo: print值为4,上文中已做解释
client.SendRequestStream();
return 0;
}
3. 重点修改部分
3.1 CMakeLists.txt原始代码
cmake_minimum_required(VERSION 3.5.1)
project(DemoGrpc C CXX)
include(./common.cmake)
set(proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/../demo.pb.cc")
set(proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/../demo.pb.h")
set(grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/../demo.grpc.pb.cc")
set(grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/../demo.grpc.pb.h")
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
add_library(grpc_proto ${proto_srcs} ${proto_hdrs} ${grpc_srcs} ${grpc_hdrs})
target_link_libraries(grpc_proto ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF})
add_executable(demoServer "server.cc")
add_executable(demoClient "client.cc")
target_link_libraries(demoServer grpc_proto ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF})
target_link_libraries(demoClient grpc_proto ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF})
3.2 CMakeLists.txt精简修改代码
cmake_minimum_required(VERSION 3.2)
project(xxxxx)
include(./common.cmake)
# Generated sources
set(coordinate_proto_srcs "${CMAKE_BINARY_DIR}/../include/demo.pb.cc")
set(coordinate_proto_hdrs "${CMAKE_BINARY_DIR}/../include/demo.pb.h")
set(coordinate_grpc_srcs "${CMAKE_BINARY_DIR}/../include/demo.grpc.pb.cc")
set(coordinate_grpc_hdrs "${CMAKE_BINARY_DIR}/../include/demo.grpc.pb.h")
include_directories(include)
# rg_grpc_proto
add_library(coordinate_grpc_proto ${coordinate_grpc_srcs} ${coordinate_grpc_hdrs} ${coordinate_proto_srcs} {coordinate_proto_hdrs})
target_link_libraries(coordinate_grpc_proto ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF})
add_executable(demoServer "server.cc")
add_executable(demoClient "client.cc")
target_link_libraries(demoServer coordinate_grpc_proto ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF})
target_link_libraries(demoClient coordinate_grpc_proto ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF})
get_filename_component(coordinate_proto "./demo.proto" ABSOLUTE)
get_filename_component(coordinate_proto_path "${coordinate_proto}" PATH)
# 以下为较原代码新增加部分
add_custom_command(
OUTPUT "${coordinate_proto_srcs}" "${coordinate_proto_hdrs}" "${coordinate_grpc_srcs}" "${coordinate_grpc_hdrs}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS --grpc_out "${CMAKE_BINARY_DIR}/../include"
--cpp_out "${CMAKE_BINARY_DIR}/../include"
-I "${coordinate_proto_path}"
--plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
"${coordinate_proto}"
DEPENDS "${coordinate_proto}")