1. 安装 gRPC
设置安装地址
export MY_INSTALL_DIR=$HOME/.local mkdir -p $MY_INSTALL_DIR
安装编译依赖
sudo apt install -y build-essential autoconf libtool pkg-config
下载代码并开始编译
git clone --recurse-submodules -b v1.55.0 --depth 1 --shallow-submodules https://github.com/grpc/grpc cd grpc mkdir -p cmake/build pushd cmake/build cmake -DgRPC_INSTALL=ON \ -DgRPC_BUILD_TESTS=OFF \ -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \ ../..make -j8 make install popd
2. Demo
hello.proto
protobuf
由 service 和 mssage 这两个基本部分组成,前者相当于函数,由服务端实现,客户端可以直接调用,后者则表示一种数据结构,在 cpp 中则是一个类,里面的就是类的成员变量,可以看到 message 中成员变量后面都有一个数字,在protobuf
中就是用这个数字来表示这个变量,而不是采用这些变量名。"proto3"; syntax = package hello; service Greeter{ rpc SayHello(HelloRequest) returns (HelloReply){} } message HelloRequest{ string name = 1; } message HelloReply{ string message = 1; }
server.cc
server 里面实现对应的 service 需要定义一个类来继承自动生成的代码
#include <string> #include <iostream> #include <memory> #include "absl/strings/str_format.h" #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include <grpcpp/ext/proto_server_reflection_plugin.h> #include <grpcpp/grpcpp.h> #include <grpcpp/support/status.h> #include <grpcpp/health_check_service_interface.h> #include "grpcpp/health_check_service_interface.h" #include "grpcpp/security/server_credentials.h" #include "grpcpp/server_builder.h" #include "grpcpp/server_context.h" #include "hello.grpc.pb.h" #include "hello.pb.h" (uint16_t, port, 50051, "Server port for the service"); ABSL_FLAG class GreeterServiceTmpl final : public hello::Greeter::Service{ ::Status SayHello(grpc::ServerContext* context, grpcconst hello::HelloRequest* request, ::HelloReply* reply hello) override { std::string prefix("Hello "); ->set_message(prefix + request->name()); replyreturn grpc::Status::OK; } }; void RunServer(uint16_t port){ std::string server_address = absl::StrFormat("0.0.0.0:%d", port); ; GreeterServiceTmpl service ::EnableDefaultHealthCheckService(true); grpc::reflection::InitProtoReflectionServerBuilderPlugin(); grpc ::ServerBuilder builder; grpc.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service); builder std::unique_ptr<grpc::Server> server(builder.BuildAndStart()); std::cout << "Server listening on " << server_address << std::endl; ->Wait(); server} int main(int argc, char** argv){ ::ParseCommandLine(argc, argv); absl(absl::GetFlag(FLAGS_port)); RunServer return 0; }
client.cc
#include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "grpcpp/channel.h" #include "grpcpp/client_context.h" #include "hello.grpc.pb.h" #include "hello.pb.h" #include <grpcpp/grpcpp.h> #include <iterator> #include <memory> #include <string> (std::string, target, "localhost:50051", "Server address"); ABSL_FLAG class GreeterClient{ private: std::unique_ptr<hello::Greeter::Stub> stub_; public: (std::shared_ptr<grpc::Channel> channel):stub_(hello::Greeter::NewStub(channel)){} GreeterClient std::string SayHello(const std::string& user){ ::HelloRequest request; hello.set_name(user); request ::HelloReply reply; hello::ClientContext context; grpc ::Status status = stub_->SayHello(&context, request, &reply); // 发起请求 grpc if(status.ok()){ return reply.message(); }else{ std::cout << status.error_code() << ": " << status.error_message() << std::endl; return "RPC failed"; } } }; int main(int grac, char** argv){ ::ParseCommandLine(grac, argv); abslstd::string target_str = absl::GetFlag(FLAGS_target); (grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials())); GreeterClient greeter std::string user("world"); std::string reply = greeter.SayHello(user); std::cout << "Greeter received: " << reply << std::endl; return 0; }
CMakeList.txt
# Copyright 2018 gRPC authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # cmake build file for C++ helloworld example. # Assumes protobuf and gRPC have been installed using cmake. # See cmake_externalproject/CMakeLists.txt for all-in-one cmake build # that automatically builds all the dependencies before building helloworld. cmake_minimum_required(VERSION 3.8) project(hello C CXX) include(common.cmake) # Proto file get_filename_component(hw_proto "./hello.proto" ABSOLUTE) get_filename_component(hw_proto_path "${hw_proto}" PATH) # Generated sources set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/hello.pb.cc") set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/hello.pb.h") set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/hello.grpc.pb.cc") set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/hello.grpc.pb.h") add_custom_command( OUTPUT "${hw_proto_srcs}" "${hw_proto_hdrs}" "${hw_grpc_srcs}" "${hw_grpc_hdrs}" COMMAND ${_PROTOBUF_PROTOC} ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" --cpp_out "${hw_proto_path}" -I "${_GRPC_CPP_PLUGIN_EXECUTABLE}" --plugin=protoc-gen-grpc="${hw_proto}" DEPENDS "${hw_proto}") # Include generated *.pb.h files include_directories("${CMAKE_CURRENT_BINARY_DIR}") # hw_grpc_proto add_library(hw_grpc_proto ${hw_grpc_srcs} ${hw_grpc_hdrs} ${hw_proto_srcs} ${hw_proto_hdrs}) target_link_libraries(hw_grpc_proto ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF}) # Targets greeter_[async_](client|server) foreach(_target server client)add_executable(${_target} "${_target}.cc") target_link_libraries(${_target} hw_grpc_protoabsl::flags absl::flags_parse ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF}) endforeach()
编译
proto
文件protoc -I . --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` hello.proto protoc -I . --cpp_out=. hello.proto
第一行会生成:
hello.grpc.pb.h
和hello.grpc.pb.cc
, 前者包含你的消息(message)的声明,后者包含你的消息类的实现第二行会生成:
hello.pb.cc
和hello.pb.h
,前者包含你的服务(service)的声明,后者包含你的服务类的实现
3. 关键概念
stub
On the client side, the client has a stub (referred to as just a client in some languages) that provides the same methods as the server.
这个词不是蛮好翻译,可以理解把他理解为一个接口,通过这个接口调用远程的函数就像调用本地函数一样。
channel
A gRPC channel provides a connection to a gRPC server on a specified host and port. It is used when creating a client stub. Clients can specify channel arguments to modify gRPC’s default behavior, such as switching message compression on or off. A channel has state, including
connected
andidle
.channel 就是指 server 和 client 之间的连接
4. WireShark 抓包
设置 protobuf 搜索路径,让 WireShark 认识 protobuf
Edit->Preferences > Protocols > Protobuf.
设置 decode,让 WireShark 可以成功的解析 HTTP2 协议
Analyze -> decode as -> add