Operating System 24 | gRPC and protobuf Starter Tutorial
Operating System 24 | gRPC and protobuf Starter Tutorial

1. gRPC Setup
In C++, we need to build and install gRPC before we test our Hello World example. Suppose we have MacOS, and first we need to choose a directory to hold locally installed packages. This page assumes that the environment variable MY_INSTALL_DIR
holds this directory path.
$ export MY_INSTALL_DIR=$HOME/.local
Ensure that the directory exists,
$ mkdir -p $MY_INSTALL_DIR
Add the local bin
folder to your path variable,
$ export PATH="$PATH:$MY_INSTALL_DIR/bin"
You need version 3.19.6
or later of cmake
. Install it by,
$ wget -q -O cmake-linux.sh https://github.com/Kitware/CMake/releases/download/v3.19.6/cmake-3.19.6-Linux-x86_64.sh
$ sh cmake-linux.sh -- --skip-license --prefix=$MY_INSTALL_DIR
$ rm cmake-linux.sh
Then check the version of cmake
,
$ cmake --version
cmake version 3.19.6
Install the basic tools required to build gRPC,
$ brew install autoconf automake libtool pkg-config
Then, clone the gRPC
repo,
$ git clone --recurse-submodules -b v1.35.0 https://github.com/grpc/grpc
While not mandatory, gRPC
applications usually IDL proto3
for service definitions and data serialization,
$ cd grpc
$ mkdir -p cmake/build
$ pushd cmake/build
$ cmake -DgRPC_INSTALL=ON \
-DgRPC_BUILD_TESTS=OFF \
-DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \
../..
$ make install -j4 // 4 means that you should have a 4-core system
$ popd
2. Helloworld Example
First, let’s check the helloworld example. The example code is part of the grpc
repo source, which you cloned as part of the steps of the previous section. First, let’s change to the example’s directory,
$ cd examples/cpp/helloworld
Then build the example using cmake
,
$ mkdir -p cmake/build
$ pushd cmake/build
$ cmake -DCMAKE_PREFIX_PATH=$MY_INSTALL_DIR ../..
$ make -j4
After we meet,
[100%] Built target greeter_client
Then it means that we can test our code. From the first terminal, we can use
$ ./greeter_server
Server listening on 0.0.0.0:50051
In the second terminal, run
$ ./greeter_client
And we will receive the following message,
Greeter received: Hello world
Congratulations! You’ve just run a client-server application with gRPC.
3. Protocol Buffer
In the last section, we have discussed that we can use the XDR files to construct the client stub and the server stub. For the gRPC, we will use protobuf by default. This is a serializing open-source compiler developed by Google. We can install the protobuf by,
$ brew install protobuf

4. Protocol Buffer Version
The first step when working with protocol buffers is to define the structure for the data you want to serialize in a proto file, which is an ordinary text file with a .proto
extension. In the first line of a .proto
file, we must explicitly point out that we will use proto3
because by default, we will be using proto2
.
syntax = "proto3";
5. Proto Message
Protocol buffer data is structured as messages, where each message is a small logical record of information containing a series of name-value pairs called fields. Here’s a simple example in the helloworld example,
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
We can try to compile the following simple.proto code by the compiler protoc
,
By,
$ mkdir -p build
$ protoc simple.proto --cpp_out=./build
$ ls ./build
simple.pb.cc simple.pb.h
6. Field and Field Number
In the previous example, we have found out that in a specific message, we must have name-value pairs called fields. And these fields can be used to transfer the data. But why do we must include a field number after each field? Before we answer this question, let’s try to compile the following code,
If we compile this code, we will find the following error,
simple.proto:6:16: Field number 1 has already been used in "HelloRequest" by field "name".
This means that the field number must be unique for each field in each message. However, in two different messages, we can use the same field number. For example, in the helloworld example, field name
and field message
in two different messages can have the same field number.
But why should we include this field number? Let’s see an explanation from the StackOverflow. It says that the field numbers are used to match fields when serializing and deserializing the data. So if we don’t include this value, there will be ambiguity from both sides. For example, if we want to transfer both the string name and an int32 typed code, the client and the server will not know how to interpret the message data because we can have two different cases,

If we attach a field number for each field, then it will be easier because now both the server and the client know how to serialize and deserialize the data.
7. Proto Services
In the .proto
file, we should also define our gRPC services. For example, in the helloworld example, both the server and the client stub have a SayHello()
RPC method that takes a HelloRequest
parameter from the client and returns a HelloResponse
from the server, and that this method is defined like this,
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
You can add another service simply and now let’s have a try. Suppose after the greeter, we would like to send a password to the server and the server will send back the int32
type private key. So we can update the new .proto
file as,
Before we can use the new service method, we need to recompile the updated proto file. From the directory …/examples/cpp/helloworld/cmake/build
, we should run,
$ make -j4
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.
Then, we still need to implement and call the new method in the human-written parts of our example application.
Finally, from the directory …/examples/cpp/helloworld/cmake/build
, we should run,
$ make -j4
Then, from one terminal,
$ ./greeter_server
Server listening on 0.0.0.0:50051
From another terminal,
$ ./greeter_client
Greeter received: Hello world
Private key: 102513