Intro to ROS Part 6: Services, Requests, and Responses with C++
2025-10-30 | By ShawnHymel
Single Board Computers Raspberry Pi SBC
In the previous episode, we explored how ROS 2 services allow for synchronous communication between nodes. We implemented both a server and a client using Python to demonstrate the request/response pattern, which is ideal for one-time interactions like commanding a robot or retrieving specific data. In this tutorial, we will walk through the same concept, but this time using C++. You’ll learn how to write and run your own service server and client nodes in C++ within a ROS 2 workspace.
In this tutorial, we will see how services work by creating a custom server and client in C++ ROS nodes. You can watch a video version of this tutorial here:
The Docker image and code for this series can be found here: https://github.com/ShawnHymel/introduction-to-ros
ROS 2 Services Recap

Before we dive into code, let’s briefly revisit what services are in ROS 2. Unlike topics, which allow asynchronous communication and broadcasting, services work synchronously between two nodes: a client sends a request and waits for a response from a server. The interface used in a service call defines both the request and response fields. A popular example used in tutorials is example_interfaces/srv/AddTwoInts, which simply accepts two integers and returns their sum.
This model is ideal for tasks where you need an immediate reply or need to trigger an event in another node. Let’s start building our own service server in C++.
Creating the Service Server
Start by launching your ROS 2 Docker container and navigating to the shared workspace:
docker run --rm -it -e PUID=$(wsl id -u) -e PGID=$(wsl id -g) -p 22002:22 -p 3000:3000 -v "${PWD}\workspace:/config/workspace" env-ros2
Inside your C++ package (e.g., my_cpp_pkg), create a file named minimal_server.cpp with the following content:
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"
/**
 * Server example that adds two integers
 */
class MinimalServer : public rclcpp::Node
{
public:
  /**
   * Constructor
   */
  MinimalServer() : Node("minimal_server")
  {
    // Create a service (server) object
    server_ = this->create_service<example_interfaces::srv::AddTwoInts>(
      "add_ints",
      std::bind(
        &MinimalServer::server_callback,
        this,
        std::placeholders::_1,
        std::placeholders::_2));
  }
  private:
  /**
   * Responds with sum of request integers
   */
  void server_callback(
    const example_interfaces::srv::AddTwoInts::Request::SharedPtr req,
    const example_interfaces::srv::AddTwoInts::Response::SharedPtr resp)
  {
    // Contents of response is automatically sent back to client
    resp->sum = req->a + req->b;
    // Log request
    RCLCPP_INFO(
      this->get_logger(),
      "Received request: a=%d, b=%d",
      (int)req->a,
      (int)req->b);
  }
  // Declare member variables
  rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr server_;
};
/**
 * Main entrypoint
 */
int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  auto node = std::make_shared<MinimalServer>();
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
Next, modify your CMakeLists.txt file to include the new server executable:
# Define minimal_server target and link libraries
add_executable(minimal_server src/minimal_server.cpp)
ament_target_dependencies(minimal_server rclcpp std_msgs)
install(TARGETS
  …
  minimal_server
  DESTINATION lib/${PROJECT_NAME})
Build the package:
colcon build --packages-select my_cpp_pkg
Then, run the server:
source install/setup.bash ros2 run my_cpp_pkg minimal_server
In another terminal, list and call the service:
ros2 service list
ros2 service info /add_ints
ros2 service call /add_ints example_interfaces/srv/AddTwoInts "{a: 10, b: 12}"

You can also inspect the service using the rqt GUI by selecting Plugins > Services > Service Caller.

Understanding the Server
The server in ROS 2 acts much like a web server. It listens for incoming requests, performs an operation, and sends back a response. In our example, the server waits for two integers from the client, adds them, and returns the sum.
When we define the service using create_service, we specify three things: the type of service (e.g., AddTwoInts), the service name (e.g., add_ints), and a callback function to handle the request. This callback receives two shared pointers: one for the request and one for the response. The logic within the callback performs the desired operation and sets the appropriate fields in the response.
This pattern is highly extensible. You could just as easily replace the integer addition with a more complex operation, like querying a database, computing a path for navigation, or interfacing with external hardware. The power of ROS services lies in this flexibility, enabling complex behavior to be triggered on demand by other nodes.
Creating the Service Client
Now, let’s build a client that periodically sends requests to the server. Create minimal_client.cpp with the following code:
#include <random>
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"
/**
 * Client example that periodically calls the server
 */
class MinimalClient : public rclcpp::Node
{
  
public:
  /**
   * Constructor
   */
  MinimalClient() : Node("minimal_client")
  {
    // Create a client object
    client_ = this->create_client<example_interfaces::srv::AddTwoInts>(
      "add_ints");
    // Wait for service
    while (!client_->wait_for_service(std::chrono::seconds(2)))
    {
      RCLCPP_WARN(this->get_logger(), "Waiting for service...");
    }
    // Seed random number generator with current time
    std::srand(std::time(nullptr));
    // Periodically call method
    timer_ = this->create_wall_timer(
      std::chrono::milliseconds(2000),
      std::bind(&MinimalClient::timer_callback, this));
  }
private:
  /**
   * Send request to server asking it to add two integers
   */
  void timer_callback()
  {
    // Fill out request message
    auto req = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
    req->a = std::rand() % 11;
    req->b = std::rand() % 11;
    // Send request to server and set callback
    client_->async_send_request(
      req,
      std::bind(
        &MinimalClient::response_callback,
        this,
        std::placeholders::_1));
  }
  /**
   * Log result when received from server
   */
  void response_callback(
    rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture future)
  {
    auto resp = future.get();
    RCLCPP_INFO(this->get_logger(), "Result: %d", (int)resp->sum);
  }
  // Declare member variables
  rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client_;
  rclcpp::TimerBase::SharedPtr timer_;
};
/**
 * Main entrypoint
 */
int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  auto node = std::make_shared<MinimalClient>();
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}
Update your CMakeLists.txt file to include the client:
# Define minimal_client target and link libraries
add_executable(minimal_client src/minimal_client.cpp)
ament_target_dependencies(minimal_client rclcpp std_msgs)
install(TARGETS
…
minimal_server
minimal_client
DESTINATION lib/${PROJECT_NAME})
Rebuild the package:
colcon build --packages-select my_cpp_pkg
Run the client node in one terminal:
source install/setup.bash
ros2 run my_cpp_pkg minimal_client
And start the server in another:
source install/setup.bash
ros2 run my_cpp_pkg minimal_server
You should see your client wait for the service and then print out the responses.

Understanding the Client
The client in a ROS 2 service interaction initiates the communication. It creates a request, sends it to the server, and then waits for a response. In C++, this is done using create_client to set up the client and async_send_request to send the request asynchronously.
Unlike synchronous function calls, async_send_request does not block the main thread. Instead, it returns a SharedFuture object immediately. This object holds the result of the service call, which will be populated once the response is received. To handle the result, we attach a callback using std::bind. This is where the power of asynchronous programming shines—your node remains responsive while waiting for data.
It’s important to use SharedFuture rather than Future because ROS 2 might also need to inspect the result internally (e.g., for logging). The shared version ensures multiple consumers can safely call .get() on the result.
In our example, the client periodically generates random numbers and sends them as a request to the server. When the response is received, the callback logs the result. This pattern mimics real-world robotic systems, where sensor data or control commands are sent at regular intervals.
Wrapping Up
In this tutorial, we implemented ROS 2 service communication using C++. You created a service server node to handle addition requests and a client node that periodically sends random integer pairs. This builds on what you’ve already learned in Python and demonstrates how the same concepts apply across languages in ROS 2.
Services are essential for tasks where you need a response or need to coordinate specific actions between nodes. In the next tutorial, we’ll see how to create custom interfaces for topics and services. Stay tuned!
 
                 
                 
                 
 
 
 
 Settings
        Settings
     Fast Delivery
                                    Fast Delivery
                                 Free Shipping
                                    Free Shipping
                                 Incoterms
                                    Incoterms
                                 Payment Types
                                    Payment Types
                                





 Marketplace Product
                                    Marketplace Product
                                 
 
         
         
                 
                 
                 
                 
                 
                 
                 
                 
                 
                 
                     
                                 
                                 
                                 
                         
                                 
                                 
                                 
                                 
                                 
                                 
                                 
                                 Hong Kong, China
Hong Kong, China