grpc实例之C++实现
·
下面实验都是定义的同一个函数进行远程过程调用,
因此c++,python的客户端和服务端都是互通的
grpc实例之Python实现
grpc实例之C++实现
grpc详解和安装
一. 创建配置文件(极其简单)
这个配置文件msg.proto和grpc实例之Python实现用的是一个文件
这里不进行赘述了
// msg.proto
syntax = "proto3"; // 规定使用proto3的语法
service MsgService { // 定义服务, 流数据放到括号里面
rpc GetMsg (MsgRequest) returns (MsgResponse){}
}
message MsgRequest { // 请求的结构, 也可以定义int32,int64,double,float
string name = 1;
int32 num1 = 2;
double num2 = 3;
}
message MsgResponse { // 回应的结果
string msg = 1;
int32 num1 = 2;
double num2 = 3;
}
二. 生成C++头文件
2.1 如何确定命令
- 因为grpc给出了一个helloworld, 所以可以根据里面的Makefile文件知道他的编译逻辑
- 首先是将第一章中的配置文件通过proto命令生成对应的应用头文件(msg.grpc.pb)和消息头文件(msg.pb), 使用的命令如下
# 生成`msg.grpc.pb` 服务类
protoc -I ../ --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ../msg.proto
# ../是.proto文件所在文件夹, ../msg.proto是文件 --grpc_out=.这个点表示文件保存到当前文件夹
# 生成`msg.pb` 消息类
protoc -I ../ --cpp_out=. ../msg.proto
# ../是.proto文件所在文件夹, ../msg.proto是文件 --grpc_out=.这个点表示文件保存到当前文件夹
三. 服务器端
- 引入刚刚生成的头文件和grpc头文件
- 定义类继承MsgService::Service
- 实现GetMsg方法
- 所有的参数赋值都通过方法的set_argc*()实现
- 运行服务即可
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h> // 和python一样, import grp
// 在包含两个信息和应用的头文件
#include "msg.grpc.pb.h"
#include "msg.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
// service MsgService { // 定义服务, 流数据放到括号里面
// rpc GetMsg (MsgRequest) returns (MsgResponse){}
// }
// message MsgRequest { // 请求的结构, 也可以定义int32,int64,double,float
// string name = 1;
// int32 num1 = 2;
// double num2 = 3;
// }
// message MsgResponse { // 回应的结果
// string msg = 1;
// int32 num1 = 2;
// double num2 = 3;
// }
//
// 不知道为啥, 这里生成并没有定义namespace, 所以可以在引用头文件之后直接使用
// using msg::MsgRequest; //上面定义的请求结构
// using msg::MsgResponse; // 上面定义的响应结构
// using msg::MsgService; // 上面定义的类
//
// 这个类随意起名, 只要继承 MsgService::Service就行了
// 具体见msg.grpc.pb.cc中MsgService::Service::Service()
class MyMsgService final : public MsgService::Service {
Status GetMsg(ServerContext* context, const MsgRequest* request,
MsgResponse* reply) override {
std::string str1("Hello ");
reply->set_msg(str1 + request->name()); // 给reply.msg赋值
reply->set_num1(32);
reply->set_num2(3.14155);
return Status::OK;
}
};
void RunServer() {
std::string server_address("0.0.0.0:50051");
MyMsgService 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;
}
四. 客户端
- 引入刚刚生成的头文件和grpc头文件
- 定义类继承MsgServiceClient
- 实现GetMsg方法
- 所有的参数赋值都通过方法的set_argc*()实现
- 运行服务即可
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h> // 和python一样, import grp
// 在包含两个信息和应用的头文件
#include "msg.grpc.pb.h"
#include "msg.pb.h"
// 这是通用工具
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
// service MsgService { // 定义服务, 流数据放到括号里面
// rpc GetMsg (MsgRequest) returns (MsgResponse){}
// }
// message MsgRequest { // 请求的结构, 也可以定义int32,int64,double,float
// string name = 1;
// int32 num1 = 2;
// double num2 = 3;
// }
// message MsgResponse { // 回应的结果
// string msg = 1;
// int32 num1 = 2;
// double num2 = 3;
// }
//
// 不知道为啥, 这里生成并没有定义namespace, 所以可以在引用头文件之后直接使用
// using msg::MsgRequest; //上面定义的请求结构
// using msg::MsgResponse; // 上面定义的响应结构
// using msg::MsgService; // 上面定义的类
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);
// Act upon its status.
if (status.ok()) {
// std::cout<<reply.msg()<<std::endl;
// printf("num1 = %d; num2=%f\n", reply.num1(), reply.num2());
// return reply.msg(); // reply.msg(), reply.num1(), reply.num2();
return reply; // reply.msg(), reply.num1(), reply.num2();
} 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");
// std::string reply = z_msg.GetMsg(user, 234, 3.1415926);
MsgResponse reply = z_msg.GetMsg(user, 234, 3.1415926);
std::cout<<reply.msg()<<std::endl;
printf("num1 = %d; num2=%f\n", reply.num1(), reply.num2());
// std::cout << "Greeter received: " << reply << std::endl;
return 0;
}
五. 编译文件Makefile
- 设置proto路径
- 设置system-check生成对应头文件
- 设置编译client和server
- 完成
# add by zjq
# to test cpp
HOST_SYSTEM = $(shell uname | cut -f 1 -d_)
SYSTEM ?= $(HOST_SYSTEM)
CXX = g++
CPPFLAGS += `pkg-config --cflags protobuf grpc`
CXXFLAGS += -std=c++11
# 如果是win, else Ubuntu
ifeq ($(SYSTEM),Darwin)
LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\
-lgrpc++_reflection\
-ldl
else
LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\
-Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed\
-ldl
endif
# protoc根据.proto文件生成对应的头文件
PROTOC = protoc # 工具名称, 如命令行要运行的protoc **
GRPC_CPP_PLUGIN = grpc_cpp_plugin
GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
PROTOS_PATH = ../ # 存放.proto文件的相对位置
vpath %.proto $(PROTOS_PATH)
# 显然, 先检测是否已经生成过对应的头文件了
all: system-check client server
client: msg.pb.o msg.grpc.pb.o msg_client.o
$(CXX) $^ $(LDFLAGS) -o $@
echo "生成client命令执行的是: $(CXX) $^ $(LDFLAGS) -o $@"
server: msg.pb.o msg.grpc.pb.o msg_server.o
$(CXX) $^ $(LDFLAGS) -o $@
echo "生成server命令执行的是: $(CXX) $^ $(LDFLAGS) -o $@"
clean:
rm -f *.o client server *.pb.cc *.pb.h
# 其实这句最关键, 用于利用proto工具, 将.proto文件生成对应的信息管道头文件
.PRECIOUS: %.grpc.pb.cc
%.grpc.pb.cc: %.proto
$(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $<
echo "$(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $<"
.PRECIOUS: %.pb.cc
%.pb.cc: %.proto
$(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $<
# The following is to test your system and ensure a smoother experience.
# They are by no means necessary to actually compile a grpc-enabled software.
# 下面是确保系统内已经安装了grpc和protoc
PROTOC_CMD = which $(PROTOC)
PROTOC_CHECK_CMD = $(PROTOC) --version | grep -q libprotoc.3
PLUGIN_CHECK_CMD = which $(GRPC_CPP_PLUGIN)
HAS_PROTOC = $(shell $(PROTOC_CMD) > /dev/null && echo true || echo false)
ifeq ($(HAS_PROTOC),true)
HAS_VALID_PROTOC = $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false)
endif
HAS_PLUGIN = $(shell $(PLUGIN_CHECK_CMD) > /dev/null && echo true || echo false)
SYSTEM_OK = false
ifeq ($(HAS_VALID_PROTOC),true)
ifeq ($(HAS_PLUGIN),true)
SYSTEM_OK = true
endif
endif
system-check:
ifneq ($(HAS_VALID_PROTOC),true)
@echo " DEPENDENCY ERROR"
@echo
@echo "You don't have protoc 3.0.0 installed in your path."
@echo "Please install Google protocol buffers 3.0.0 and its compiler."
@echo "You can find it here:"
@echo
@echo " https://github.com/google/protobuf/releases/tag/v3.0.0"
@echo
@echo "Here is what I get when trying to evaluate your version of protoc:"
@echo
-$(PROTOC) --version
@echo
@echo
endif
ifneq ($(HAS_PLUGIN),true)
@echo " DEPENDENCY ERROR"
@echo
@echo "You don't have the grpc c++ protobuf plugin installed in your path."
@echo "Please install grpc. You can find it here:"
@echo
@echo " https://github.com/grpc/grpc"
@echo
@echo "Here is what I get when trying to detect if you have the plugin:"
@echo
-which $(GRPC_CPP_PLUGIN)
@echo
@echo
endif
ifneq ($(SYSTEM_OK),true)
@false
endif
六. 文件结构
```shell
├── c_test
│ ├── Makefile
│ ├── client
│ ├── msg.grpc.pb.cc
│ ├── msg.grpc.pb.h
│ ├── msg.grpc.pb.o
│ ├── msg.pb.cc
│ ├── msg.pb.h
│ ├── msg.pb.o
│ ├── msg_client.cc
│ ├── msg_client.o
│ ├── msg_server.cc
│ ├── msg_server.o
│ └── server
├── msg.proto
└── python_test
├── msg.proto # 这个文件和外面的文件一样, 可以删除
├── msg_client.py
├── msg_pb2.py
├── msg_pb2_grpc.py
├── msg_server.py
更多推荐
所有评论(0)