diff --git a/cpp/helloworld/README.md b/cpp/helloworld/README.md index bc7df3f8c7a..c406051e800 100644 --- a/cpp/helloworld/README.md +++ b/cpp/helloworld/README.md @@ -89,8 +89,172 @@ $ protoc -I ../../protos/ --cpp_out=. ../../protos/helloworld.proto ### Writing a client -This is an incomplete tutorial. For now the reader should refer to [greeter_client.cc](https://github.com/grpc/grpc-common/blob/master/cpp/helloworld/greeter_client.cc). +- Create a channel. A channel is a logical connection to an endpoint. A gRPC + channel can be created with the target address, credentials to use and + arguments as follows + + ``` + auto channel = CreateChannel("localhost:50051", InsecureCredentials(), ChannelArguments()); + ``` + +- Create a stub. A stub implements the rpc methods of a service and in the + generated code, a method is provided to created a stub with a channel: + + ``` + auto stub = helloworld::Greeter::NewStub(channel); + ``` + +- Make a unary rpc, with `ClientContext` and request/response proto messages. + + ``` + ClientContext context; + HelloRequest request; + request.set_name("hello"); + HelloReply reply; + Status status = stub->SayHello(&context, request, &reply); + ``` + +- Check returned status and response. + + ``` + if (status.ok()) { + // check reply.message() + } else { + // rpc failed. + } + ``` + +For a working example, refer to [greeter_client.cc](https://github.com/grpc/grpc-common/blob/master/cpp/helloworld/greeter_client.cc). ### Writing a server -This is an incomplete tutorial. For now the reader should refer to [greeter_server.cc](https://github.com/grpc/grpc-common/blob/master/cpp/helloworld/greeter_server.cc). +- Implement the service interface + + ``` + class GreeterServiceImpl final : public Greeter::Service { + Status SayHello(ServerContext* context, const HelloRequest* request, + HelloReply* reply) override { + std::string prefix("Hello "); + reply->set_message(prefix + request->name()); + return Status::OK; + } + }; + + ``` + +- Build a server exporting the service + + ``` + GreeterServiceImpl service; + ServerBuilder builder; + builder.AddListeningPort("0.0.0.0:50051", grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + std::unique_ptr server(builder.BuildAndStart()); + ``` + +For a working example, refer to [greeter_server.cc](https://github.com/grpc/grpc-common/blob/master/cpp/helloworld/greeter_server.cc). + +### Writing asynchronous client and server + +gRPC uses `CompletionQueue` API for asynchronous operations. The basic work flow +is +- bind a `CompletionQueue` to a rpc call +- do something like a read or write, present with a unique `void*` tag +- call `CompletionQueue::Next` to wait for operations to complete. If a tag + appears, it indicates that the corresponding operation is complete. + +#### Async client + +The channel and stub creation code is the same as the sync client. + +- Initiate the rpc and create a handle for the rpc. Bind the rpc to a + `CompletionQueue`. + + ``` + CompletionQueue cq; + auto rpc = stub->AsyncSayHello(&context, request, &cq); + ``` + +- Ask for reply and final status, with a unique tag + + ``` + Status status; + rpc->Finish(&reply, &status, (void*)1); + ``` + +- Wait for the completion queue to return the next tag. The reply and status are + ready once the tag passed into the corresponding `Finish()` call is returned. + + ``` + void* got_tag; + bool ok = false; + cq.Next(&got_tag, &ok); + if (ok && got_tag == (void*)1) { + // check reply and status + } + ``` + +For a working example, refer to [greeter_async_client.cc](https://github.com/grpc/grpc-common/blob/master/cpp/helloworld/greeter_async_client.cc). + +#### Async server + +The server implementation requests a rpc call with a tag and then wait for the +completion queue to return the tag. The basic flow is + +- Build a server exporting the async service + + ``` + helloworld::Greeter::AsyncService service; + ServerBuilder builder; + builder.AddListeningPort("0.0.0.0:50051", InsecureServerCredentials()); + builder.RegisterAsyncService(&service); + auto cq = builder.AddCompletionQueue(); + auto server = builder.BuildAndStart(); + ``` + +- Request one rpc + + ``` + ServerContext context; + HelloRequest request; + ServerAsyncResponseWriter responder; + service.RequestSayHello(&context, &request, &responder, &cq, &cq, (void*)1); + ``` + +- Wait for the completion queue to return the tag. The context, request and + responder are ready once the tag is retrieved. + + ``` + HelloReply reply; + Status status; + void* got_tag; + bool ok = false; + cq.Next(&got_tag, &ok); + if (ok && got_tag == (void*)1) { + // set reply and status + responder.Finish(reply, status, (void*)2); + } + ``` + +- Wait for the completion queue to return the tag. The rpc is finished when the + tag is back. + + ``` + void* got_tag; + bool ok = false; + cq.Next(&got_tag, &ok); + if (ok && got_tag == (void*)2) { + // clean up + } + ``` + +To handle multiple rpcs, the async server creates an object `CallData` to +maintain the state of each rpc and use the address of it as the unique tag. For +simplicity the server only uses one completion queue for all events, and runs a +main loop in `HandleRpcs` to query the queue. + +For a working example, refer to [greeter_async_server.cc](https://github.com/grpc/grpc-common/blob/master/cpp/helloworld/greeter_async_server.cc). + + + +