# Request response

## Introduction

This example demonstrates how to use iceoryx in a client-server architecture
using the request-response communication pattern. The client sends a request with
two consecutive fibonacci numbers and the server responds with the next number in
the sequence.

We provide three examples, the very basic typed and untyped examples
and the most natural setup combining a server with a Listener and a client using
a WaitSet. Since you can find the general setup and functionality of the client
and the server also in the Listener/WaitSet example, we will describe only this
one.

## Expected output

[![asciicast](https://asciinema.org/a/476829.svg)](https://asciinema.org/a/476829)

## Code walkthrough

In the following scenario the client (client_cxx_waitset.cpp) uses the WaitSet to wait for a response from the server
(server_cxx_listener.cpp). The server uses the Listener API to take and process the requests from the client.

The client is inspired by the `iox-cpp-waitset-basic` example from the [WaitSet](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/waitset)
example and the server from the `iox-cpp-callbacks-subscriber` in the [Listener](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/callbacks)
example.

This is the most recommended way to create an efficient client-server combination with iceoryx.

### Client using WaitSet

At first, the includes for the client port, request-response types, WaitSet, and runtime are needed.

```cpp
#include "request_and_response_types.hpp"

#include "iceoryx_hoofs/posix_wrapper/signal_watcher.hpp"
#include "iceoryx_posh/popo/client.hpp"
#include "iceoryx_posh/popo/wait_set.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"
```

Afterwards we prepare some `ContextData` where we can store the Fibonacci numbers
and the sequence ids that we use to ensure the correct ordering of the responses.


```cpp
struct ContextData
{
    uint64_t fibonacciLast = 0;
    uint64_t fibonacciCurrent = 1;
    int64_t requestSequenceId = 0;
    int64_t expectedResponseSequenceId = requestSequenceId;
};
```

Next, the iceoryx runtime is initialized. With this call,
the application will be registered at `RouDi`, the routing and discovery daemon.

```cpp
iox::runtime::PoshRuntime::initRuntime(APP_NAME);
```

After creating the runtime, the client is created and attached to the WaitSet.
The [options](https://iceoryx.io/v2.0.0/getting-started/examples/iceoptions/) can be used to alter the behavior of the client, like setting the response
queue capacity or blocking behavior when the response queue is full or the server is too slow.
The `ClientOptions` are similar to `PublisherOptions`/`SubscriberOptions`.


```cpp
iox::popo::WaitSet<> waitset;

iox::popo::ClientOptions options;
options.responseQueueCapacity = 2U;
iox::popo::Client<AddRequest, AddResponse> client({"Example", "Request-Response", "Add"}, options);

// attach client to waitset
waitset.attachState(client, iox::popo::ClientState::HAS_RESPONSE).or_else([](auto) {
    std::cerr << "failed to attach client" << std::endl;
    std::exit(EXIT_FAILURE);
});
```

Since the client requests the sum of two numbers from the server, we provide the
structs `AddRequest` and `AddResponse` as template parameters. When the sum is
received, it is re-used as the `addend` of the next request to send. This
calculates a Fibonacci sequence.


```cpp
struct AddRequest
{
    uint64_t augend{0};
    uint64_t addend{0};
};
```


```cpp
struct AddResponse
{
    uint64_t sum{0};
};
```

In the main loop, the client prepares first a request using the `loan()` API.
The request is a sample consisting of the two numbers `augend` and `addend` that
the server shall sum up. Additionally, the sample is marked with a sequence id
that is incremented before every send cycle to ensure a correct ordering of the
messages. The request is transmitted to the server via the `send()` API.

```cpp
client.loan()
    .and_then([&](auto& request) {
        request.getRequestHeader().setSequenceId(ctx.requestSequenceId);
        ctx.expectedResponseSequenceId = ctx.requestSequenceId;
        ctx.requestSequenceId += 1;
        request->augend = ctx.fibonacciLast;
        request->addend = ctx.fibonacciCurrent;
        std::cout << APP_NAME << " Send Request: " << ctx.fibonacciLast << " + " << ctx.fibonacciCurrent
                  << std::endl;
        request.send().or_else(
            [&](auto& error) { std::cout << "Could not send Request! Error: " << error << std::endl; });
    })
    .or_else([](auto& error) { std::cout << "Could not allocate Request! Error: " << error << std::endl; });
```

Once the request has been sent, we block and wait for samples to arrive. Then we
iterate over the notification vector to check if we were triggered from our client:

```cpp
auto notificationVector = waitset.timedWait(iox::units::Duration::fromSeconds(5));

for (auto& notification : notificationVector)
{
    if (notification->doesOriginateFrom(&client))
    {
        // ...
    }
}
```

The client receives the responses from the server using `take()`
and extracts the sequence id with `response.getResponseHeader().getSequenceId()`.
When the server response comes in the correct order, the received sum is stored
in the `ContextData` struct `ctx` for usage in the next request.


```cpp
while (client.take().and_then([&](const auto& response) {
    auto receivedSequenceId = response.getResponseHeader().getSequenceId();
    if (receivedSequenceId == ctx.expectedResponseSequenceId)
    {
        ctx.fibonacciLast = ctx.fibonacciCurrent;
        ctx.fibonacciCurrent = response->sum;
        std::cout << APP_NAME << " Got Response : " << ctx.fibonacciCurrent << std::endl;
    }
    else
    {
        std::cout << "Got Response with outdated sequence ID! Expected = "
                  << ctx.expectedResponseSequenceId << "; Actual = " << receivedSequenceId
                  << "! -> skip" << std::endl;
    }
}))
{
}
```

### Server using Listener

At first, the includes for the server port, Listener, request-response types and runtime are needed.

```cpp
#include "request_and_response_types.hpp"

#include "iceoryx_hoofs/posix_wrapper/signal_watcher.hpp"
#include "iceoryx_posh/popo/listener.hpp"
#include "iceoryx_posh/popo/server.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"
```

Then a callback is created that shall be called when the server receives a request.
In this case the calculation and the sending of the response is done in the Listener callback.
If there are more resource-consuming tasks,
this could also be outsourced with a thread pool to handle the requests.


```cpp
void onRequestReceived(iox::popo::Server<AddRequest, AddResponse>* server)
{
    while (server->take().and_then([&](const auto& request) {
        std::cout << APP_NAME << " Got Request: " << request->augend << " + " << request->addend << std::endl;

        server->loan(request)
            .and_then([&](auto& response) {
                response->sum = request->augend + request->addend;
                std::cout << APP_NAME << " Send Response: " << response->sum << std::endl;
                response.send().or_else(
                    [&](auto& error) { std::cout << "Could not send Response! Error: " << error << std::endl; });
            })
            .or_else([](auto& error) { std::cout << "Could not allocate Response! Error: " << error << std::endl; });
    }))
    {
    }
}
```

The server provides the `take()` method for receiving requests and the `loan()` and `send()` methods
for sending the responses with the sum of the two numbers.

Next, the iceoryx runtime is initialized.

```cpp
iox::runtime::PoshRuntime::initRuntime(APP_NAME);
```

After creating the runtime, the server port is created based on a `ServiceDescription`. Similar to the client,
the `options` are used to alter the behavior of the server, like setting the request
queue capacity or blocking behavior when the request queue is full or the client is too slow.

```cpp
iox::popo::ServerOptions options;
options.requestQueueCapacity = 10U;
iox::popo::Server<AddRequest, AddResponse> server({"Example", "Request-Response", "Add"}, options);
```

Now we want to listen to an incoming server event and call the previously created callback
whenever a request has been received.
This is done with the following call:

```cpp
listener
    .attachEvent(
        server, iox::popo::ServerEvent::REQUEST_RECEIVED, iox::popo::createNotificationCallback(onRequestReceived))
    .or_else([](auto) {
        std::cerr << "unable to attach server" << std::endl;
        std::exit(EXIT_FAILURE);
    });
```

With that the preparation is done and the main thread can just sleep or do other things:


```cpp
iox::posix::waitForTerminationRequest();
```

Once the user wants to shutdown the server, the server event is detached from the listener:

```cpp
listener.detachEvent(server, iox::popo::ServerEvent::REQUEST_RECEIVED);
```


[Check out request_response on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/request_response){ .md-button }

