Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 1 | # gRPC C++ Hello World Tutorial |
| 2 | |
| 3 | ### Install gRPC |
Jan Tattermusch | f389e52 | 2018-06-12 17:26:31 +0200 | [diff] [blame] | 4 | Make sure you have installed gRPC on your system. Follow the |
| 5 | [BUILDING.md](../../../BUILDING.md) instructions. |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 6 | |
| 7 | ### Get the tutorial source code |
| 8 | |
Stanley Cheung | 0a26821 | 2015-08-27 14:38:38 -0700 | [diff] [blame] | 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 |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 11 | following command: |
| 12 | |
| 13 | |
| 14 | ```sh |
Mehrdad Afshari | 976eb7c | 2017-07-10 21:54:56 +0000 | [diff] [blame] | 15 | $ git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 16 | ``` |
| 17 | |
Stanley Cheung | 0a26821 | 2015-08-27 14:38:38 -0700 | [diff] [blame] | 18 | Change your current directory to examples/cpp/helloworld |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 19 | |
| 20 | ```sh |
Stanley Cheung | 0a26821 | 2015-08-27 14:38:38 -0700 | [diff] [blame] | 21 | $ cd examples/cpp/helloworld/ |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 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 |
yang-g | b00a3f6 | 2015-08-28 14:19:37 -0700 | [diff] [blame] | 37 | [helloworld.proto](../../protos/helloworld.proto). The `Greeting` |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 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 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 44 | ```protobuf |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 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 |
Nicolas "Pixel" Noble | b6413de | 2015-04-10 00:24:09 +0200 | [diff] [blame] | 81 | $ make helloworld.grpc.pb.cc helloworld.pb.cc |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 82 | ``` |
| 83 | Which internally invokes the proto-compiler as: |
| 84 | |
| 85 | ```sh |
Nicolas "Pixel" Noble | b6413de | 2015-04-10 00:24:09 +0200 | [diff] [blame] | 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 |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 88 | ``` |
| 89 | |
| 90 | ### Writing a client |
| 91 | |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 96 | ```cpp |
Julien Boeuf | 8c48a2a | 2015-10-17 22:23:02 -0700 | [diff] [blame] | 97 | auto channel = CreateChannel("localhost:50051", InsecureChannelCredentials()); |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 103 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 104 | auto stub = helloworld::Greeter::NewStub(channel); |
| 105 | ``` |
| 106 | |
| 107 | - Make a unary rpc, with `ClientContext` and request/response proto messages. |
| 108 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 109 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 119 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 120 | if (status.ok()) { |
| 121 | // check reply.message() |
| 122 | } else { |
| 123 | // rpc failed. |
| 124 | } |
| 125 | ``` |
| 126 | |
yang-g | b00a3f6 | 2015-08-28 14:19:37 -0700 | [diff] [blame] | 127 | For a working example, refer to [greeter_client.cc](greeter_client.cc). |
Abhishek Kumar | 2f22e84 | 2015-02-20 16:39:32 -0800 | [diff] [blame] | 128 | |
| 129 | ### Writing a server |
| 130 | |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 131 | - Implement the service interface |
| 132 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 133 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 147 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 | |
yang-g | b00a3f6 | 2015-08-28 14:19:37 -0700 | [diff] [blame] | 155 | For a working example, refer to [greeter_server.cc](greeter_server.cc). |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 |
yang-g | 2add980 | 2015-08-27 13:03:49 -0700 | [diff] [blame] | 163 | - call `CompletionQueue::Next` to wait for operations to complete. If a tag |
| 164 | appears, it indicates that the corresponding operation is complete. |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 165 | |
| 166 | #### Async client |
| 167 | |
| 168 | The channel and stub creation code is the same as the sync client. |
| 169 | |
yang-g | 2add980 | 2015-08-27 13:03:49 -0700 | [diff] [blame] | 170 | - Initiate the rpc and create a handle for the rpc. Bind the rpc to a |
| 171 | `CompletionQueue`. |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 172 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 173 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 180 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 181 | Status status; |
| 182 | rpc->Finish(&reply, &status, (void*)1); |
| 183 | ``` |
| 184 | |
yang-g | 2add980 | 2015-08-27 13:03:49 -0700 | [diff] [blame] | 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. |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 187 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 188 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 | |
yang-g | b00a3f6 | 2015-08-28 14:19:37 -0700 | [diff] [blame] | 197 | For a working example, refer to [greeter_async_client.cc](greeter_async_client.cc). |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 198 | |
| 199 | #### Async server |
| 200 | |
yang-g | 2add980 | 2015-08-27 13:03:49 -0700 | [diff] [blame] | 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 |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 203 | |
| 204 | - Build a server exporting the async service |
| 205 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 206 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 207 | helloworld::Greeter::AsyncService service; |
| 208 | ServerBuilder builder; |
| 209 | builder.AddListeningPort("0.0.0.0:50051", InsecureServerCredentials()); |
Yuchen Zeng | c67a8ec | 2016-03-17 11:36:59 -0700 | [diff] [blame] | 210 | builder.RegisterService(&service); |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 211 | auto cq = builder.AddCompletionQueue(); |
| 212 | auto server = builder.BuildAndStart(); |
| 213 | ``` |
| 214 | |
| 215 | - Request one rpc |
| 216 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 217 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 218 | ServerContext context; |
| 219 | HelloRequest request; |
| 220 | ServerAsyncResponseWriter<HelloReply> responder; |
| 221 | service.RequestSayHello(&context, &request, &responder, &cq, &cq, (void*)1); |
| 222 | ``` |
| 223 | |
yang-g | 2add980 | 2015-08-27 13:03:49 -0700 | [diff] [blame] | 224 | - Wait for the completion queue to return the tag. The context, request and |
| 225 | responder are ready once the tag is retrieved. |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 226 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 227 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 | |
yang-g | 2add980 | 2015-08-27 13:03:49 -0700 | [diff] [blame] | 239 | - Wait for the completion queue to return the tag. The rpc is finished when the |
| 240 | tag is back. |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 241 | |
James Eady | 2e9ce91 | 2015-11-04 14:54:50 -0500 | [diff] [blame] | 242 | ```cpp |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 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 | |
yang-g | b00a3f6 | 2015-08-28 14:19:37 -0700 | [diff] [blame] | 256 | For a working example, refer to [greeter_async_server.cc](greeter_async_server.cc). |
yang-g | 0c63351 | 2015-08-26 15:08:43 -0700 | [diff] [blame] | 257 | |
| 258 | |
| 259 | |
| 260 | |