1 # gRPC C++ Hello World Tutorial 2 3 ### Install gRPC 4 Make sure you have installed gRPC on your system. Follow the 5 [BUILDING.md](../../../BUILDING.md) instructions. 6 7 ### Get the tutorial source code 8 9 The example code for this and our other examples lives in the `examples` 10 directory. Clone this repository to your local machine by running the 11 following command: 12 13 14 ```sh 15 $ git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc 16 ``` 17 18 Change your current directory to examples/cpp/helloworld 19 20 ```sh 21 $ cd examples/cpp/helloworld/ 22 ``` 23 24 ### Defining a service 25 26 The first step in creating our example is to define a *service*: an RPC 27 service specifies the methods that can be called remotely with their parameters 28 and return types. As you saw in the 29 [overview](#protocolbuffers) above, gRPC does this using [protocol 30 buffers](https://developers.google.com/protocol-buffers/docs/overview). We 31 use the protocol buffers interface definition language (IDL) to define our 32 service methods, and define the parameters and return 33 types as protocol buffer message types. Both the client and the 34 server use interface code generated from the service definition. 35 36 Here's our example service definition, defined using protocol buffers IDL in 37 [helloworld.proto](../../protos/helloworld.proto). The `Greeting` 38 service has one method, `hello`, that lets the server receive a single 39 `HelloRequest` 40 message from the remote client containing the user's name, then send back 41 a greeting in a single `HelloReply`. This is the simplest type of RPC you 42 can specify in gRPC - we'll look at some other types later in this document. 43 44 ```protobuf 45 syntax = "proto3"; 46 47 option java_package = "ex.grpc"; 48 49 package helloworld; 50 51 // The greeting service definition. 52 service Greeter { 53 // Sends a greeting 54 rpc SayHello (HelloRequest) returns (HelloReply) {} 55 } 56 57 // The request message containing the user's name. 58 message HelloRequest { 59 string name = 1; 60 } 61 62 // The response message containing the greetings 63 message HelloReply { 64 string message = 1; 65 } 66 67 ``` 68 69 <a name="generating"></a> 70 ### Generating gRPC code 71 72 Once we've defined our service, we use the protocol buffer compiler 73 `protoc` to generate the special client and server code we need to create 74 our application. The generated code contains both stub code for clients to 75 use and an abstract interface for servers to implement, both with the method 76 defined in our `Greeting` service. 77 78 To generate the client and server side interfaces: 79 80 ```sh 81 $ make helloworld.grpc.pb.cc helloworld.pb.cc 82 ``` 83 Which internally invokes the proto-compiler as: 84 85 ```sh 86 $ protoc -I ../../protos/ --grpc_out=. --plugin=protoc-gen-grpc=grpc_cpp_plugin ../../protos/helloworld.proto 87 $ protoc -I ../../protos/ --cpp_out=. ../../protos/helloworld.proto 88 ``` 89 90 ### Writing a client 91 92 - Create a channel. A channel is a logical connection to an endpoint. A gRPC 93 channel can be created with the target address, credentials to use and 94 arguments as follows 95 96 ```cpp 97 auto channel = CreateChannel("localhost:50051", InsecureChannelCredentials()); 98 ``` 99 100 - Create a stub. A stub implements the rpc methods of a service and in the 101 generated code, a method is provided to created a stub with a channel: 102 103 ```cpp 104 auto stub = helloworld::Greeter::NewStub(channel); 105 ``` 106 107 - Make a unary rpc, with `ClientContext` and request/response proto messages. 108 109 ```cpp 110 ClientContext context; 111 HelloRequest request; 112 request.set_name("hello"); 113 HelloReply reply; 114 Status status = stub->SayHello(&context, request, &reply); 115 ``` 116 117 - Check returned status and response. 118 119 ```cpp 120 if (status.ok()) { 121 // check reply.message() 122 } else { 123 // rpc failed. 124 } 125 ``` 126 127 For a working example, refer to [greeter_client.cc](greeter_client.cc). 128 129 ### Writing a server 130 131 - Implement the service interface 132 133 ```cpp 134 class GreeterServiceImpl final : public Greeter::Service { 135 Status SayHello(ServerContext* context, const HelloRequest* request, 136 HelloReply* reply) override { 137 std::string prefix("Hello "); 138 reply->set_message(prefix + request->name()); 139 return Status::OK; 140 } 141 }; 142 143 ``` 144 145 - Build a server exporting the service 146 147 ```cpp 148 GreeterServiceImpl service; 149 ServerBuilder builder; 150 builder.AddListeningPort("0.0.0.0:50051", grpc::InsecureServerCredentials()); 151 builder.RegisterService(&service); 152 std::unique_ptr<Server> server(builder.BuildAndStart()); 153 ``` 154 155 For a working example, refer to [greeter_server.cc](greeter_server.cc). 156 157 ### Writing asynchronous client and server 158 159 gRPC uses `CompletionQueue` API for asynchronous operations. The basic work flow 160 is 161 - bind a `CompletionQueue` to a rpc call 162 - do something like a read or write, present with a unique `void*` tag 163 - call `CompletionQueue::Next` to wait for operations to complete. If a tag 164 appears, it indicates that the corresponding operation is complete. 165 166 #### Async client 167 168 The channel and stub creation code is the same as the sync client. 169 170 - Initiate the rpc and create a handle for the rpc. Bind the rpc to a 171 `CompletionQueue`. 172 173 ```cpp 174 CompletionQueue cq; 175 auto rpc = stub->AsyncSayHello(&context, request, &cq); 176 ``` 177 178 - Ask for reply and final status, with a unique tag 179 180 ```cpp 181 Status status; 182 rpc->Finish(&reply, &status, (void*)1); 183 ``` 184 185 - Wait for the completion queue to return the next tag. The reply and status are 186 ready once the tag passed into the corresponding `Finish()` call is returned. 187 188 ```cpp 189 void* got_tag; 190 bool ok = false; 191 cq.Next(&got_tag, &ok); 192 if (ok && got_tag == (void*)1) { 193 // check reply and status 194 } 195 ``` 196 197 For a working example, refer to [greeter_async_client.cc](greeter_async_client.cc). 198 199 #### Async server 200 201 The server implementation requests a rpc call with a tag and then wait for the 202 completion queue to return the tag. The basic flow is 203 204 - Build a server exporting the async service 205 206 ```cpp 207 helloworld::Greeter::AsyncService service; 208 ServerBuilder builder; 209 builder.AddListeningPort("0.0.0.0:50051", InsecureServerCredentials()); 210 builder.RegisterService(&service); 211 auto cq = builder.AddCompletionQueue(); 212 auto server = builder.BuildAndStart(); 213 ``` 214 215 - Request one rpc 216 217 ```cpp 218 ServerContext context; 219 HelloRequest request; 220 ServerAsyncResponseWriter<HelloReply> responder; 221 service.RequestSayHello(&context, &request, &responder, &cq, &cq, (void*)1); 222 ``` 223 224 - Wait for the completion queue to return the tag. The context, request and 225 responder are ready once the tag is retrieved. 226 227 ```cpp 228 HelloReply reply; 229 Status status; 230 void* got_tag; 231 bool ok = false; 232 cq.Next(&got_tag, &ok); 233 if (ok && got_tag == (void*)1) { 234 // set reply and status 235 responder.Finish(reply, status, (void*)2); 236 } 237 ``` 238 239 - Wait for the completion queue to return the tag. The rpc is finished when the 240 tag is back. 241 242 ```cpp 243 void* got_tag; 244 bool ok = false; 245 cq.Next(&got_tag, &ok); 246 if (ok && got_tag == (void*)2) { 247 // clean up 248 } 249 ``` 250 251 To handle multiple rpcs, the async server creates an object `CallData` to 252 maintain the state of each rpc and use the address of it as the unique tag. For 253 simplicity the server only uses one completion queue for all events, and runs a 254 main loop in `HandleRpcs` to query the queue. 255 256 For a working example, refer to [greeter_async_server.cc](greeter_async_server.cc). 257 258 259 260 261