gRPC (gRPC Remote Procedure Calls) is a modern, high-performance RPC framework that can run in any environment. It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, load balancing, and more. In this blog post, we’ll walk through building a simple gRPC application in Rust and then capture and analyze the network traffic using tcpdump
.
Prerequisites
Before we begin, make sure you have the following installed:
- Rust: Install Rust from rustup.rs.
- Protocol Buffers Compiler (
protoc
): Installprotoc
from here. - tcpdump: Install
tcpdump
using your package manager (e.g.,apt-get install tcpdump
on Ubuntu).
Step 1: Setting Up the Rust Project
First, let’s create a new Rust project:
cargo new grpc-examplecd grpc-example
Next, add the necessary dependencies to your Cargo.toml
file:
[package]name = "grpc-example"version = "0.1.0"edition = "2021"
[dependencies]tonic = "0.7"prost = "0.11"tokio = { version = "1", features = ["full"] }
[build-dependencies]tonic-build = "0.7"
Here, we’re using tonic
, a gRPC implementation for Rust, and prost
for Protocol Buffers.
Step 2: Defining the gRPC Service
Create a proto
directory in your project root and add a hello.proto
file:
syntax = "proto3";
package hello;
service Greeter { rpc SayHello (HelloRequest) returns (HelloReply);}
message HelloRequest { string name = 1;}
message HelloReply { string message = 1;}
This defines a simple gRPC service with a single SayHello
method.
Step 3: Generating Rust Code from Protobuf
Next, we’ll generate Rust code from the .proto
file. Create a build.rs
file in the root of your project:
fn main() { tonic_build::compile_protos("proto/hello.proto").unwrap();}
This will generate the necessary Rust code when you build your project.
Step 4: Implementing the gRPC Server
Now, let’s implement the gRPC server. Modify src/main.rs
as follows:
use tonic::{transport::Server, Request, Response, Status};use hello::greeter_server::{Greeter, GreeterServer};use hello::{HelloRequest, HelloReply};
pub mod hello { tonic::include_proto!("hello");}
#[derive(Debug, Default)]pub struct MyGreeter {}
#[tonic::async_trait]impl Greeter for MyGreeter { async fn say_hello( &self, request: Request<HelloRequest>, ) -> Result<Response<HelloReply>, Status> { let name = request.into_inner().name; let reply = HelloReply { message: format!("Hello, {}!", name), }; Ok(Response::new(reply)) }}
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let addr = "[::1]:50051".parse()?; let greeter = MyGreeter::default();
println!("GreeterServer listening on {}", addr);
Server::builder() .add_service(GreeterServer::new(greeter)) .serve(addr) .await?;
Ok(())}
This code sets up a gRPC server that listens on localhost:50051
and responds to SayHello
requests.
Step 5: Running the Server
Run the server using:
cargo run
The server should now be running and listening for incoming gRPC requests.
Step 6: Capturing Packets with tcpdump
To capture the network traffic, open a new terminal and run tcpdump
:
tcpdump -i lo -w grpc_traffic.pcap port 50051
This command captures all traffic on the loopback interface (lo
) on port 50051
and writes it to a file called grpc_traffic.pcap
.
Step 7: Making a gRPC Request
In another terminal, let’s make a gRPC request to our server. You can use a tool like grpcurl
:
grpcurl -proto ./hello.proto -plaintext -d '{"name": "World"}' localhost:50051 hello.Greeter/SayHello
Note: if you add reflections, you do not have to provide the proto file, checkout the grpcurl for more details You should see a response like.
{ "message": "Hello, World!"}
Step 8: Analyzing the Captured Packets
Stop the tcpdump
process by pressing Ctrl+C
. Now, let’s analyze the captured packets using Wireshark
or tcpdump
itself.
To analyze with tcpdump
:
tcpdump -r grpc_traffic.pcap
You should see the HTTP/2 frames, including the gRPC request and response. Look for frames that contain the SayHello
method and the corresponding response.
Alternatively, open grpc_traffic.pcap
in Wireshark for a more detailed analysis. You can filter for http2
to see only HTTP/2 traffic.
https://protobuf.dev/programming-guides/encoding/
tcpdump -qns 0 -X -r grpc_traffic.pcap
Conclusion
In this blog post, we built a simple gRPC server in Rust, captured the network traffic using tcpdump
, and analyzed the packets. gRPC is a powerful tool for building efficient, type-safe APIs, and Rust’s ecosystem makes it easy to get started. By understanding the underlying network traffic, you can gain deeper insights into how gRPC works and how to troubleshoot potential issues.
Feel free to expand on this example by adding more services, implementing client-side code, or exploring advanced features like streaming and authentication. Happy coding!
https://protobuf.dev/programming-guides/proto3/
21:52:33.263066 IP6 ::1.55746 > ::1.50051: tcp 1306002 967e 00a2 0640 0000 0000 0000 0000 `..~...@........0000 0000 0000 0001 0000 0000 0000 0000 ................0000 0000 0000 0001 d9c2 c383 25c2 a109 ............%...6cf1 19cf 8018 0200 00aa 0000 0101 080a l...............37a4 b2bc 37a4 b2bc 0000 6401 0400 0000 7...7.....d.....0183 8645 9162 72d1 41d7 c561 4a92 d8c6 ...E.br.A..aJ...e1fa c65a 283f 418b a0e4 1d13 9d09 b8d8 ...Z(?A.........00d8 7f5f 8b1d 75d0 620d 263d 4c4d 6564 ..._..u.b.&=LMed7a94 9aca c96d 9430 15df 5c4a 4d65 645a z....m.0..\JMedZ63b0 15dc 0ae0 4002 7465 864d 8335 05b1 c.....@.te.M.5..1f40 8e9a cac8 b0c8 42d6 958b 510f 21aa .@......B...Q.!.9b83 9bd9 ab00 000c 0001 0000 0001 0000 ................0000 070a 0557 6f72 6c64 .....World