diff options
Diffstat (limited to 'examples/cpp/helloworld')
-rw-r--r-- | examples/cpp/helloworld/greeter_async_client.cc | 52 | ||||
-rw-r--r-- | examples/cpp/helloworld/greeter_async_server.cc | 60 | ||||
-rw-r--r-- | examples/cpp/helloworld/greeter_client.cc | 24 | ||||
-rw-r--r-- | examples/cpp/helloworld/greeter_server.cc | 15 |
4 files changed, 119 insertions, 32 deletions
diff --git a/examples/cpp/helloworld/greeter_async_client.cc b/examples/cpp/helloworld/greeter_async_client.cc index 605fb7fb3a..923c8ffa74 100644 --- a/examples/cpp/helloworld/greeter_async_client.cc +++ b/examples/cpp/helloworld/greeter_async_client.cc @@ -35,13 +35,8 @@ #include <memory> #include <string> -#include <grpc/grpc.h> -#include <grpc/support/log.h> -#include <grpc++/channel.h> -#include <grpc++/client_context.h> -#include <grpc++/completion_queue.h> -#include <grpc++/create_channel.h> -#include <grpc++/security/credentials.h> +#include <grpc++/grpc++.h> + #include "helloworld.grpc.pb.h" using grpc::Channel; @@ -58,39 +53,72 @@ class GreeterClient { explicit GreeterClient(std::shared_ptr<Channel> channel) : stub_(Greeter::NewStub(channel)) {} + // Assambles 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 producer-consumer queue we use to communicate asynchronously with the + // gRPC runtime. CompletionQueue cq; + + // Storage for the status of the RPC upon completion. Status status; + // stub_->AsyncSayHello() perform the RPC call, returning an instance we + // store in "rpc". Because we are using the asynchronous API, we need the + // hold on to the "rpc" instance in order to get updates on the ongoig RPC. std::unique_ptr<ClientAsyncResponseReader<HelloReply> > rpc( stub_->AsyncSayHello(&context, request, &cq)); + + // Request that, upon completion of the RPC, "reply" be updated with the + // server's response; "status" with the indication of whether the operation + // was successful. Tag the request with the integer 1. rpc->Finish(&reply, &status, (void*)1); void* got_tag; bool ok = false; + // Block until the next result is available in the completion queue "cq". cq.Next(&got_tag, &ok); - GPR_ASSERT(ok); + + // Verify that the result from "cq" corresponds, by its tag, our previous + // request. GPR_ASSERT(got_tag == (void*)1); + // ... and that the request was completed successfully. Note that "ok" + // corresponds solely to the request for updates introduced by Finish(). + GPR_ASSERT(ok); + // Act upon the status of the actual RPC. if (status.ok()) { return reply.message(); } else { - return "Rpc failed"; + return "RPC failed"; } } private: + // Out of the passed in Channel comes the stub, stored here, our view of the + // server's exposed services. std::unique_ptr<Greeter::Stub> stub_; }; int main(int argc, char** argv) { - GreeterClient greeter(grpc::CreateChannel( - "localhost:50051", grpc::InsecureCredentials())); + // 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 InsecureCredentials()). + GreeterClient greeter( + grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials())); std::string user("world"); - std::string reply = greeter.SayHello(user); + std::string reply = greeter.SayHello(user); // The actual RPC call! std::cout << "Greeter received: " << reply << std::endl; return 0; diff --git a/examples/cpp/helloworld/greeter_async_server.cc b/examples/cpp/helloworld/greeter_async_server.cc index 189c3afe6d..b2047a8ce5 100644 --- a/examples/cpp/helloworld/greeter_async_server.cc +++ b/examples/cpp/helloworld/greeter_async_server.cc @@ -36,13 +36,8 @@ #include <string> #include <thread> -#include <grpc/grpc.h> -#include <grpc/support/log.h> -#include <grpc++/completion_queue.h> -#include <grpc++/security/server_credentials.h> -#include <grpc++/server.h> -#include <grpc++/server_builder.h> -#include <grpc++/server_context.h> +#include <grpc++/grpc++.h> + #include "helloworld.grpc.pb.h" using grpc::Server; @@ -59,6 +54,7 @@ class ServerImpl final { public: ~ServerImpl() { server_->Shutdown(); + // Always shutdown the completion queue after the server. cq_->Shutdown(); } @@ -67,56 +63,102 @@ class ServerImpl final { std::string server_address("0.0.0.0:50051"); 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 *asynchronous* service. builder.RegisterAsyncService(&service_); + // Get hold of the completion queue used for the asynchronous communication + // with the gRPC runtime. cq_ = builder.AddCompletionQueue(); + // Finally assemble the server. server_ = builder.BuildAndStart(); std::cout << "Server listening on " << server_address << std::endl; + // Proceed to the server's main loop. HandleRpcs(); } private: + // Class encompasing the state and logic needed to serve a request. class CallData { public: + // Take in the "service" instance (in this case representing an asynchronous + // server) and the completion queue "cq" used for asynchronous communication + // with the gRPC runtime. CallData(Greeter::AsyncService* service, ServerCompletionQueue* cq) : service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) { + // Invoke the serving logic right away. Proceed(); } void Proceed() { if (status_ == CREATE) { + // As part of the initial CREATE state, we *request* that the system + // start processing SayHello requests. In this request, "this" acts are + // the tag uniquely identifying the request (so that different CallData + // instances can serve different requests concurrently), in this case + // the memory address of this CallData instance. service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_, this); + // Make this instance progress to the PROCESS state. status_ = PROCESS; } else if (status_ == PROCESS) { + // Spawn a new CallData instance to serve new clients while we process + // the one for this CallData. The instance will deallocate itself as + // part of its FINISH state. new CallData(service_, cq_); + + // The actual processing. std::string prefix("Hello "); reply_.set_message(prefix + request_.name()); + + // And we are done! Let the gRPC runtime know we've finished, using the + // memory address of this instance as the uniquely identifying tag for + // the event. responder_.Finish(reply_, Status::OK, this); status_ = FINISH; } else { + GPR_ASSERT(status_ == FINISH); + // Once in the FINISH state, deallocate ourselves (CallData). delete this; } } private: + // The means of communication with the gRPC runtime for an asynchronous + // server. Greeter::AsyncService* service_; + // The producer-consumer queue where for asynchronous server notifications. ServerCompletionQueue* cq_; + // Context for the rpc, allowing to tweak aspects of it such as the use + // of compression, authentication, as well as to send metadata back to the + // client. ServerContext ctx_; + + // What we get from the client. HelloRequest request_; + // What we send back to the client. HelloReply reply_; + + // The means to get back to the client. ServerAsyncResponseWriter<HelloReply> responder_; + + // Let's implement a tiny state machine with the following states. enum CallStatus { CREATE, PROCESS, FINISH }; - CallStatus status_; + CallStatus status_; // The current serving state. }; // This can be run in multiple threads if needed. void HandleRpcs() { + // Spawn a new CallData instance to serve new clients. new CallData(&service_, cq_.get()); - void* tag; + void* tag; // uniquely identifies a request. bool ok; while (true) { + // Block waiting to read the next event from the completion queue. The + // event is uniquely identified by its tag, which in this case is the + // memory address of a CallData instance. cq_->Next(&tag, &ok); GPR_ASSERT(ok); static_cast<CallData*>(tag)->Proceed(); diff --git a/examples/cpp/helloworld/greeter_client.cc b/examples/cpp/helloworld/greeter_client.cc index bfb7c12724..6cd8353a9f 100644 --- a/examples/cpp/helloworld/greeter_client.cc +++ b/examples/cpp/helloworld/greeter_client.cc @@ -35,11 +35,8 @@ #include <memory> #include <string> -#include <grpc/grpc.h> -#include <grpc++/channel.h> -#include <grpc++/client_context.h> -#include <grpc++/create_channel.h> -#include <grpc++/security/credentials.h> +#include <grpc++/grpc++.h> + #include "helloworld.grpc.pb.h" using grpc::Channel; @@ -54,17 +51,28 @@ class GreeterClient { GreeterClient(std::shared_ptr<Channel> channel) : stub_(Greeter::NewStub(channel)) {} + // Assambles 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 { - return "Rpc failed"; + return "RPC failed"; } } @@ -73,6 +81,10 @@ class GreeterClient { }; 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 InsecureCredentials()). GreeterClient greeter( grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials())); std::string user("world"); diff --git a/examples/cpp/helloworld/greeter_server.cc b/examples/cpp/helloworld/greeter_server.cc index b434752d87..9eab32c62b 100644 --- a/examples/cpp/helloworld/greeter_server.cc +++ b/examples/cpp/helloworld/greeter_server.cc @@ -35,11 +35,8 @@ #include <memory> #include <string> -#include <grpc/grpc.h> -#include <grpc++/security/server_credentials.h> -#include <grpc++/server.h> -#include <grpc++/server_builder.h> -#include <grpc++/server_context.h> +#include <grpc++/grpc++.h> + #include "helloworld.grpc.pb.h" using grpc::Server; @@ -50,6 +47,7 @@ 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 { @@ -64,10 +62,17 @@ void RunServer() { 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(); } |