gRPC系列文章 gRPC++ HelloWorld项目
我看很多人一上来就把gRPC自带的ssl库给替换成openssl,实际上对于ssl没有特别的需求大可不必去把gRPC默认的ssl替换。
开发环境
- Windows 7
- Visual studio 2015
- gRPC 1.27.0
gRPC四类服务方法
gRPC 允许你定义四类服务方法:
单项 RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
服务端流式 RPC,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
客户端流式 RPC,即客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
新建解决方案
用vs2015新建一个解决方案:两个c++控制台项目
HelloWorld Server端项目
准备proto文件helloworld.proto
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
大家可以选择在vs安装一个编辑proto文件的插件,方便以后编辑proto。
生成proto的源文件
grpc.pb源文件
PS C:\Program Files (x86)\grpc\bin> .\protoc.exe --grpc_out="C:\Users\stand\Documents\Visual Studio 2015\Projects\gRPC-E
xamples\gRPC-Example-HelloWorldServer" --plugin="protoc-gen-grpc=grpc_cpp_plugin.exe" --proto_path="C:\Users\stand\Docum
ents\Visual Studio 2015\Projects\gRPC-Examples\gRPC-Example-HelloWorldServer" helloworld.proto
- grpc_out:编译目标文件输出目录
- plugin:指定语言的plugin
- proto_path:proto文件查找目录
- 最后指定要编译的proto文件名
pb文件
PS C:\Program Files (x86)\grpc\bin> .\protoc.exe --cpp_out="C:\Users\stand\Documents\Visual Studio 2015\Projects\gRPC-E
xamples\gRPC-Example-HelloWorldServer" --plugin="protoc-gen-grpc=grpc_cpp_plugin.exe" --proto_path="C:\Users\stand\Docum
ents\Visual Studio 2015\Projects\gRPC-Examples\gRPC-Example-HelloWorldServer" helloworld.proto
运行以上两条命令之后会生成四个源文件:
源文件里的内容就是helloworld - rpc 的stub。
将这四个文件添加进项目中。
添加C++头文件包含目录
添加依赖的静态库
需要手动添加静态库,不然会出现“无法解析的符号”从而导致编译失败。
C:\Program Files (x86)\grpc\lib\libprotobufd.lib;
C:\Program Files (x86)\grpc\lib\grpc.lib;
C:\Program Files (x86)\grpc\lib\gpr.lib;
C:\Program Files (x86)\grpc\lib\upb.lib;
C:\Program Files (x86)\grpc\lib\grpc++.lib;
C:\Program Files (x86)\grpc\lib\address_sorting.lib;
C:\Program Files (x86)\grpc\lib\cares.lib;
C:\Program Files (x86)\grpc\lib\ssl.lib;
C:\Program Files (x86)\grpc\lib\zlibstaticd.lib;
C:\Program Files (x86)\grpc\lib\crypto.lib;
C:\Program Files (x86)\grpc\lib\absl_base.lib;
C:\Program Files (x86)\grpc\lib\absl_bad_optional_access.lib;
C:\Program Files (x86)\grpc\lib\absl_dynamic_annotations.lib;
C:\Program Files (x86)\grpc\lib\absl_int128.lib;
C:\Program Files (x86)\grpc\lib\absl_strings.lib;
C:\Program Files (x86)\grpc\lib\absl_strings_internal.lib;
C:\Program Files (x86)\grpc\lib\absl_throw_delegate.lib
码代码
Windows项目不要忘记了向源文件中添加“#include “stdafx.h””
HelloWorld Server端mian源文件
#include "stdafx.h"
#include "helloworld.pb.h"
#include "helloworld.grpc.pb.h"
#include "grpcpp\grpcpp.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello ");
std::string postfix(" power by C++");
reply->set_message(prefix + request->name()+postfix);
return Status::OK;
}
};
void RunServer() {
std::string server_address("0.0.0.0:50051");
GreeterServiceImpl 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();
}
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
cout << "ready go to sleeping" << endl;
RunServer();
return 0;
}
HelloWorld Client端项目
Client端需要和Server一样的引入头文件和静态库文件。
HelloWorld Client端main源文件
#include "stdafx.h"
#include <iostream>
#include <memory>
#include <string>
//#include <grpcpp/grpcpp.h>
#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::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
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) {
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);
// Container for the data we expect from the server.
HelloReply reply;
// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContext context;
// The actual RPC.
Status status = stub_->SayHello(&context, request, &reply);
// Act upon its status.
if (status.ok()) {
return reply.message();
}
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) {
// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint (in this case,
// localhost at port 50051). We indicate that the channel isn't authenticated
// (use of InsecureChannelCredentials()).
GreeterClient greeter(grpc::CreateChannel(
"localhost:50051", grpc::InsecureChannelCredentials()));
std::string user("world");
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;
std::cin.get();
return 0;
}
测试
生成解决方案
在debug目录运行程序
ok