Quick start (original) (raw)

This guide gets you started with gRPC in C++ with a simple working example.

Quick start

This guide gets you started with gRPC in C++ with a simple working example.

In the C++ world, there’s no universally accepted standard for managing project dependencies. You need to build and install gRPC before building and running this quick start’s Hello World example.

Build and locally install gRPC and Protocol Buffers

The steps in the section explain how to build and locally install gRPC and Protocol Buffers using cmake. If you’d rather use bazel, see Building from source.

Setup

Choose a directory to hold locally installed packages. This page assumes that the environment variable MY_INSTALL_DIR holds this directory path. For example:

export MY_INSTALL_DIR=$HOME/.local

Ensure that the directory exists:

Add the local bin folder to your path variable, for example:

export PATH="$MY_INSTALL_DIR/bin:$PATH"
set MY_INSTALL_DIR=%USERPROFILE%\cmake

Ensure that the directory exists:

Add the local bin folder to your path variable, for example:

set PATH=%PATH%;$MY_INSTALL_DIR\bin

Install cmake

You need version 3.16 or later of cmake. Install it by following these instructions if you don’t have it:

sudo apt install -y cmake  

Check the version of cmake:

cmake --version
cmake version 3.30.3

Under Linux, the version of the system-wide cmake can often be too old. You can install a more recent version into your local installation directory as follows:

wget -q -O cmake-linux.sh https://github.com/Kitware/CMake/releases/download/v3.30.3/cmake-3.30.3-linux-x86_64.sh
sh cmake-linux.sh -- --skip-license --prefix=$MY_INSTALL_DIR
rm cmake-linux.sh

Install other required tools

Install the basic tools required to build gRPC:

sudo apt install -y build-essential autoconf libtool pkg-config  
brew install autoconf automake libtool pkg-config  

Clone the grpc repo

Clone the grpc repo and its submodules:

git clone --recurse-submodules -b v1.73.0 --depth 1 --shallow-submodules https://github.com/grpc/grpc

Build and install gRPC and Protocol Buffers

While not mandatory, gRPC applications usually leverage Protocol Buffersfor service definitions and data serialization, and the example code usesproto3.

The following commands build and locally install gRPC and Protocol Buffers:

cd grpc  
mkdir -p cmake/build  
pushd cmake/build  
cmake -DgRPC_INSTALL=ON \  
      -DgRPC_BUILD_TESTS=OFF \  
      -DCMAKE_CXX_STANDARD=17 \  
      -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \  
      ../..  
make -j 4  
make install  
popd  
mkdir "cmake\build"  
pushd "cmake\build"  
cmake -DgRPC_INSTALL=ON -DgRPC_BUILD_TESTS=OFF -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=%MY_INSTALL_DIR% ..\..  
cmake --build . --config Release --target install -j 4  
popd  

More information:

Build the example

The example code is part of the grpc repo source, which you cloned as part of the steps of the previous section.

  1. Change to the example’s directory:
cd examples/cpp/helloworld  
  1. Build the example using cmake:
    • Linux & macOS
    mkdir -p cmake/build  
    pushd cmake/build  
    cmake -DCMAKE_PREFIX_PATH=$MY_INSTALL_DIR ../..  
    make -j 4  
    • Windows
    mkdir "cmake\build"  
    pushd "cmake\build"  
    cmake -DCMAKE_INSTALL_PREFIX=%MY_INSTALL_DIR% ..\..  
    cmake --build . --config Release -j 4  
    popd  

Try it!

Run the example from the example build directoryexamples/cpp/helloworld/cmake/build:

  1. Run the server:
  2. From a different terminal, run the client and see the client output:
./greeter_client  
Greeter received: Hello world  

Congratulations! You’ve just run a client-server application with gRPC.

Update the gRPC service

Now let’s look at how to update the application with an extra method on the server for the client to call. Our gRPC service is defined using protocol buffers; you can find out lots more about how to define a service in a .protofile in Introduction to gRPC and Basics tutorial. For now all you need to know is that both the server and the client stub have a SayHello() RPC method that takes aHelloRequest parameter from the client and returns a HelloReply from the server, and that this method is defined like this:

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

Open examples/protos/helloworld.proto and add a new SayHelloAgain() method, with the same request and response types:

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

Remember to save the file!

Regenerate gRPC code

Before you can use the new service method, you need to recompile the updated proto file.

From the example build directory examples/cpp/helloworld/cmake/build, run:

cmake --build . --config Release -j 4

This regenerates helloworld.pb.{h,cc} and helloworld.grpc.pb.{h,cc}, which contains the generated client and server classes, as well as classes for populating, serializing, and retrieving our request and response types.

Update and run the application

You have new generated server and client code, but you still need to implement and call the new method in the human-written parts of our example application.

Update the server

Open greeter_server.cc from the example’s root directory. Implement the new method like this:

class GreeterServiceImpl final : public Greeter::Service {
  Status SayHello(ServerContext* context, const HelloRequest* request,
                  HelloReply* reply) override {
     // ...
  }

  Status SayHelloAgain(ServerContext* context, const HelloRequest* request,
                       HelloReply* reply) override {
    std::string prefix("Hello again ");
    reply->set_message(prefix + request->name());
    return Status::OK;
  }
};

Update the client

A new SayHelloAgain() method is now available in the stub. We’ll follow the same pattern as for the already present SayHello() and add a newSayHelloAgain() method to GreeterClient:

class GreeterClient {
 public:
  // ...
  std::string SayHello(const std::string& user) {
     // ...
  }

  std::string SayHelloAgain(const std::string& user) {
    // Follows the same pattern as SayHello.
    HelloRequest request;
    request.set_name(user);
    HelloReply reply;
    ClientContext context;

    // Here we can use the stub's newly available method we just added.
    Status status = stub_->SayHelloAgain(&context, request, &reply);
    if (status.ok()) {
      return reply.message();
    } else {
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
      return "RPC failed";
    }
  }

Finally, invoke this new method in main():

int main(int argc, char** argv) {
  // ...
  std::string reply = greeter.SayHello(user);
  std::cout << "Greeter received: " << reply << std::endl;

  reply = greeter.SayHelloAgain(user);
  std::cout << "Greeter received: " << reply << std::endl;

  return 0;
}

Run!

Run the client and server like you did before. Execute the following commands from the example build directory examples/cpp/helloworld/cmake/build:

  1. Build the client and server after having made changes:
    • Linux & macOS
    • Windows
cmake --build . --config Release -j 4  
  1. Run the server:
  2. On a different terminal, run the client:
    You’ll see the following output:
Greeter received: Hello world  
Greeter received: Hello again world  

What’s next