grpc_cb icon indicating copy to clipboard operation
grpc_cb copied to clipboard

C++ gRPC library with callback interface.

gRPC_cb

C++ gRPC library with protobuf and callback interface. It is easier to use than grpc++. It depends on grpc_cb_core.

Install with conan

  1. Install conan.
  2. Add conan repository: conan remote add remote_bintray_jinq0123 https://api.bintray.com/conan/jinq0123/conan
  3. Install: conan install grpc_cb/0.2@jinq0123/testing
    • The result grpc_cb.lib is in ~/.conan/data/grpc_cb/0.2/jinq0123/testing/package/...
  4. Or create: conan create . user/channel --build missing
    • The result grpc_cb.lib is in ~/.conan/data/grpc_cb/0.2/user/channel/package/...

(package: https://bintray.com/jinq0123/conan/grpc_cb%3Ajinq0123 )

VS solution

See premake/README.md to use premake5 to generate VS solution.

Done

  • grpc_cpp_cb_plugin
  • grpc_cb library
  • helloworld example
  • route_guide example

Todo

  1. Connect and disconnect event.
  2. Export for unity.
  3. Compression
  4. Security
  5. Metadata

Tutorial

Tutorial shows some codes in the route_guide example. See doc/advanced_usage.md for more usage examples.

Defining the service

See examples/protos/route_guide.proto.

// Interface exported by the server.
service RouteGuide {
  // A simple RPC.
  rpc GetFeature(Point) returns (Feature) {}

  // A server-to-client streaming RPC.
  rpc ListFeatures(Rectangle) returns (stream Feature) {}

  // A client-to-server streaming RPC.
  rpc RecordRoute(stream Point) returns (RouteSummary) {}

  // A Bidirectional streaming RPC.
  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}
...

Generating client and server code

examples/protos/generate.bat is an example to generate client and server interfaces from .proto file, which runs:

	protoc -I . --cpp_out=../cpp_cb/route_guide route_guide.proto
	protoc -I . --grpc_out=../cpp_cb/route_guide --plugin=protoc-gen-grpc=grpc_cpp_cb_plugin.exe route_guide.proto

This generates the following files in directory examples/cpp_cb/route_guide

  • route_guide.pb.h, generated message classes header
  • route_guide.pb.cc, the implementation of message classes
  • route_guide.grpc_cb.pb.h, generated service classes header
  • route_guide.grpc_cb.pb.cc, the implementation of service classes

The generated namespace RouteGuide contains

  • a Stub class for clients to call.
  • a Service class for servers to implement.

Creating the client

See examples/cpp_cb/route_guide/route_guide_cb_client.cc.

Creating a stub

  1. Create a shared Channel, specifying the server address.

    ChannelSptr channel(new Channel("localhost:50051"));
    
  2. Instantiate a Stub

    Stub stub(channel);
    

Calling service methods

  • Sync call
    • Simple RPC: SyncGetFeature()

      oint point = MakePoint(0, 0);
      eature feature;
      tatus status = stub.SyncGetFeature(point, &feature);
      
    • Server-side streaming RPC: SyncListFeatures()

      uto sync_reader(stub_->SyncListFeatures(rect));
      hile (sync_reader.ReadOne(&feature)) {
      cout << feature.name() << endl;
      
      tatus status = sync_reader.RecvStatus();
      
    • Client-side streaming RPC: SyncRecordRoute()

      uto sync_writer(stub_->SyncRecordRoute());
      or (int i = 0; i < kPoints; i++) {
      const Feature& f = GetRandomFeature();
      if (!sync_writer.Write(f.location())) {
      	// Broken stream.
      	break;
      }
      
      
      / Recv reponse and status.
      outeSummary stats;
      tatus status = sync_writer.Close(&stats);
      
    • Bidirectional streaming RPC: SyncRouteChat()

      uto sync_reader_writer(stub_->SyncRouteChat());
      uto f = std::async(std::launch::async, [sync_reader_writer]() {
      RunWriteRouteNote(sync_reader_writer);
      );
      
      outeNote server_note;
      hile (sync_reader_writer.ReadOne(&server_note))
      PrintServerNote(server_note);
      
      .wait();
      tatus status = sync_reader_writer.RecvStatus();
      
      oid RunWriteRouteNote(Stus::RouteChat_SyncReaderWriter sync_reader_writer) {
      std::vector<RouteNote> notes{ ... };
      for (const RouteNote& note : notes) {
      	sync_reader_writer.Write(note);
      	RandomSleep();
      }
      sync_reader_writer.CloseWriting();
      
      
  • Asycn call
    • Simple RPC: AsyncGetFeature()

      • With response callback

        oint point = MakePoint(0, 0);
        tub.AsyncGetFeature(point,
        [](const Feature& feature) {
        	PrintFeature(feature);
        });
        
      • Ignoring response

        tub.AsyncGetFeature(point);
        
      • With error callback

        tub.AsyncGetFeature(point,
        [](const Feature& feature) { PrintFeature(feature); },
        [](const Status& err) {
        	cout << err.GetDetails() << endl;
        });  // AsyncGetFeature()
        
    • Run the stub

      • Async calls need

        tub.Run();  // until stub.Shutdown()
        
      • It can run in other thread.

      • It can be before or after async calls.

      • stub.Shutdown() or ~Stub() to end stub.Run().

    • Server-side streaming RPC: AsyncListFeatures()

      tub.AsyncListFeatures(rect,
      [](const Feature& feature) {
      	cout << feature.name() << endl;
      },
      [&stub](const Status& status) {
      	stub.Shutdown();  // To break Run().
      });
      tub.Run();  // until stub.Shutdown()
      
    • Client-side streaming RPC: AsyncRecordRoute()

      uto async_writer = stub.AsyncRecordRoute();
      or (int i = 0; i < kPoints; i++) {
      const Feature& f = GetRandomFeature();
      if (!async_writer.Write(f.location())) {
      	break;
      }
      
      / Recv reponse and status.
      sync_writer.Close([](const Status& status, const RouteSummary& resp) {
      if (status.ok())
      	cout << resp.point_count() << endl;
      );
      
    • Bidirectional streaming RPC: AsyncRouteChat()

      td::atomic_bool bReaderDone = false;
      uto async_reader_writer(
      stub.AsyncRouteChat([&bReaderDone](const Status& status) {
      	bReaderDone = true;
      }));
      
      sync_reader_writer.ReadEach(
      [](const RouteNote& note) { PrintServerNote(note); });
      
      td::vector<RouteNote> notes{ ... };
      or (const RouteNote& note : notes) {
      async_reader_writer.Write(note);
      
      sync_reader_writer.CloseWriting();
      

Creating the server

See examples/cpp_cb/route_guide/route_guide_server.cc.

Implementing RouteGuide service

  1. Define a RouteGuideImpl class that implements the generated RouteGuide::Service interface. Service is always asynchronous.

    class RouteGuideImpl final : public routeguide::RouteGuide::Service {
    	...
    }
    
  2. Simple RPC: GetFeature()

    • Reply immediately

      oid GetFeature(const Point& point,
      	const GetFeature_Replier& replier) override {
      Feature feature;
      feature.set_name("...");
      replier.Reply(feature);
      
      
    • Reply later

      oid GetFeature(const Point& point,
      	const GetFeature_Replier& replier) override {
      GetFeature_Replier replier_copy(replier);
      std::thread thd([replier_copy]() {
      	Sleep(1000);
      	Feature feature;
      	feature.set_name("...");
      	replier_copy.Reply(feature);
      });
      thd.detach();
      
      
  3. Server-side streaming RPC: ListFeatures()

    void ListFeatures(const routeguide::Rectangle& rectangle,
    		ListFeatures_Writer writer) override {
    	std::thread t([writer]() {
    		for (const Feature& f : feature_vector) {
    			if (!writer.Write(f)) break;
    			Sleep(1000);
    		}
    	});  // thread t
    	t.detach();
    }
    
  4. Client-side streaming RPC: RecordRoute()

    • Should return a shared reader:

      ecordRoute_ReaderSptr RecordRoute(
      RecordRoute_Replier replier) override {
      return std::make_shared<RecordRoute_ReaderImpl>(feature_vector_);
        // RecordRoute()
      
    • Should implement a RecordRoute_Reader:

      lass RecordRoute_ReaderImpl
      	: public routeguide::RouteGuide::Service::RecordRoute_Reader {
      ...
      
      
    • Implement virtual methods

      • OnMsg(const Request& msg)
        • Default noop.
      • OnError(const Status& status)
        • Default replys error.
      • OnEnd()
        • Default noop.
  5. Bidirectional streaming RPC: RouteChat()

    • Should return a shared reader.
      outeChat_ReaderSptr RouteChat(RouteChat_Writer writer) override {
      return std::make_shared<Reader>();
      
      
    • Implement a reader.
      lass Reader : public RouteChat_Reader {
      protected:
      	void OnMsg(const RouteNote& msg) override {
      		for (const RouteNote& n : received_notes_) {
      			GetWriter().Write(n);
      		}  // for
      		received_notes_.push_back(msg);
      	}  // OnMsg()
      
      	void OnEnd() override {
      		RouteChat_Writer writer = GetWriter();
      		std::thread t([writer]() {
      			std::this_thread::sleep_for(std::chrono::seconds(1));
      			writer.Write(RouteNote());
      		});
      		t.detach();
      	}  // OnEnd()
      
      private:
      	std::vector<RouteNote> received_notes_;
      ;  // class Reader
      

Starting the server

  1. Instantiate server and add listening port.

    Server svr;
    svr.AddListeningPort("0.0.0.0:50051");
    
  2. Instantiate service and register to server.

    RouteGuideImpl service(db_path);
    svr.RegisterService(service);
    
  3. Blocking run server.

    svr.Run();