Skip to content

Commit 342183c

Browse files
dkroenkeelBoberido
authored andcommitted
iox-#27 Add Readme for request response example
1 parent 685b9a1 commit 342183c

File tree

2 files changed

+172
-1
lines changed

2 files changed

+172
-1
lines changed
+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# request_response
2+
3+
## Introduction
4+
5+
This example demonstrates how to use iceoryx in a client-server architecture
6+
using the request-response communication pattern.
7+
The main feature is that the server only response data at the request of a client.
8+
9+
## Expected output
10+
11+
[![asciicast](https://asciinema.org/a/469913.svg)](https://asciinema.org/a/469913)
12+
13+
## Code walkthrough
14+
15+
### Client
16+
17+
At first, the includes for the client port and request-response types and runtime are needed.
18+
<!-- [geoffrey] [geoffrey][iceoryx_examples/request_response/client_cxx_basic.cpp] [iceoryx includes] -->
19+
```cpp
20+
#include "request_and_response_types.hpp"
21+
22+
#include "iceoryx_hoofs/posix_wrapper/signal_watcher.hpp"
23+
#include "iceoryx_posh/popo/client.hpp"
24+
#include "iceoryx_posh/runtime/posh_runtime.hpp"
25+
```
26+
27+
Next, the iceoryx runtime is initialized. With this call,
28+
the application will be registered at `RouDi`, the routing and discovery daemon.
29+
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_basic.cpp] [initialize runtime] -->
30+
```cpp
31+
constexpr char APP_NAME[] = "iox-cpp-request-response-client-basic";
32+
iox::runtime::PoshRuntime::initRuntime(APP_NAME);
33+
```
34+
35+
After creating the runtime, the client port is created based on a ServiceDescription.
36+
<!--[geoffrey][iceoryx_examples/request_response/client_cxx_basic.cpp][create client]-->
37+
```cpp
38+
iox::popo::Client<AddRequest, AddResponse> client({"Example", "Request-Response", "Add"});
39+
```
40+
41+
The main goal of the client is to request from the server the sum of two numbers that the
42+
client sends. When to sum is received from the server, the received sum is re-used to insert
43+
it to the `addend` of the next request to send.
44+
This calculates a Fibonacci sequence.
45+
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_basic.cpp] [[send requests in a loop]] -->
46+
```cpp
47+
uint64_t fibonacciLast = 0;
48+
uint64_t fibonacciCurrent = 1;
49+
int64_t requestSequenceId = 0;
50+
int64_t expectedResponseSequenceId = requestSequenceId;
51+
while (!iox::posix::hasTerminationRequested())
52+
{
53+
// send request to server for sum up two numbers
54+
55+
// the server polls with an interval of 100ms
56+
constexpr std::chrono::milliseconds DELAY_TIME{150U};
57+
std::this_thread::sleep_for(DELAY_TIME);
58+
59+
// receive sum
60+
61+
constexpr std::chrono::milliseconds SLEEP_TIME{950U};
62+
std::this_thread::sleep_for(SLEEP_TIME);
63+
}
64+
```
65+
66+
In the main loop, the client prepares a request using the `loan()` API.
67+
The request is a sample consisting of two numbers `augend` and `addend` that the server shall sum up.
68+
Additionally, the sample is marked with a sequence id that is incremented before
69+
every send cycle to ensure a correct ordering of the messages
70+
(`request.getRequestHeader().setSequenceId()`).
71+
The request is transmitted to the server via the `send()` API.
72+
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_basic.cpp] [[send request]] -->
73+
```cpp
74+
client.loan()
75+
.and_then([&](auto& request) {
76+
request.getRequestHeader().setSequenceId(requestSequenceId);
77+
expectedResponseSequenceId = requestSequenceId;
78+
requestSequenceId += 1;
79+
request->augend = fibonacciLast;
80+
request->addend = fibonacciCurrent;
81+
std::cout << APP_NAME << " Send Request: " << fibonacciLast << " + " << fibonacciCurrent << std::endl;
82+
request.send();
83+
})
84+
.or_else([](auto& error) {
85+
std::cout << "Could not allocate Request! Return value = " << static_cast<uint64_t>(error) << std::endl;
86+
});
87+
```
88+
89+
In the same while loop the client receives the responses from the server using `take()`
90+
and extract the sequence id with `response.getResponseHeader().getSequenceId()`.
91+
When the server response comes in the correct order, the received sum is re-used to
92+
insert it to the `addend` of the next request to send.
93+
94+
<!-- [geoffrey] [iceoryx_examples/request_response/client_cxx_basic.cpp] [[take response]] -->
95+
```cpp
96+
while (client.take().and_then([&](const auto& response) {
97+
auto receivedSequenceId = response.getResponseHeader().getSequenceId();
98+
if (receivedSequenceId == expectedResponseSequenceId)
99+
{
100+
fibonacciLast = fibonacciCurrent;
101+
fibonacciCurrent = response->sum;
102+
std::cout << APP_NAME << " Got Response : " << fibonacciCurrent << std::endl;
103+
}
104+
else
105+
{
106+
std::cout << "Got Response with outdated sequence ID! Expected = " << expectedResponseSequenceId
107+
<< "; Actual = " << receivedSequenceId << "! -> skip" << std::endl;
108+
}
109+
}))
110+
{
111+
};
112+
```
113+
114+
### Server
115+
116+
At first, the includes for the server port and request-response types and runtime are needed.
117+
<!-- [geoffrey] [geoffrey][iceoryx_examples/request_response/server_cxx_basic.cpp] [iceoryx includes] -->
118+
```cpp
119+
#include "request_and_response_types.hpp"
120+
121+
#include "iceoryx_hoofs/posix_wrapper/signal_watcher.hpp"
122+
#include "iceoryx_posh/popo/server.hpp"
123+
#include "iceoryx_posh/runtime/posh_runtime.hpp"
124+
```
125+
126+
Next, the iceoryx runtime is initialized. With this call,
127+
the application will be registered at `RouDi`, the routing and discovery daemon.
128+
<!-- [geoffrey] [iceoryx_examples/request_response/server_cxx_basic.cpp] [initialize runtime] -->
129+
```cpp
130+
constexpr char APP_NAME[] = "iox-cpp-user-header-publisher";
131+
iox::runtime::PoshRuntime::initRuntime(APP_NAME);
132+
```
133+
134+
After creating the runtime, the server port is created based on a ServiceDescription.
135+
<!--[geoffrey][iceoryx_examples/request_response/server_cxx_basic.cpp][create server]-->
136+
```cpp
137+
iox::popo::Server<AddRequest, AddResponse> server({"Example", "Request-Response", "Add"});
138+
```
139+
140+
In the main loop, the server receives the requests from the client with
141+
`take()` and prepares with `loan()` a sample for the response.
142+
The received two numbers `augend` and `addend` are added and transmitted
143+
with the `send()` API.
144+
<!-- [geoffrey] [iceoryx_examples/request_response/server_cxx_basic.cpp] [[process requests in a loop] [take request] [send response]] -->
145+
```cpp
146+
while (!iox::posix::hasTerminationRequested())
147+
{
148+
server.take().and_then([&](const auto& request) {
149+
std::cout << APP_NAME << " Got Request: " << request->augend << " + " << request->addend << std::endl;
150+
151+
server.loan(request)
152+
.and_then([&](auto& response) {
153+
response->sum = request->augend + request->addend;
154+
std::cout << APP_NAME << " Send Response: " << response->sum << std::endl;
155+
response.send();
156+
})
157+
.or_else([&](auto& error) {
158+
std::cout << APP_NAME
159+
<< "Could not allocate Response! Return value = " << static_cast<uint64_t>(error)
160+
<< std::endl;
161+
});
162+
});
163+
164+
constexpr std::chrono::milliseconds SLEEP_TIME{100U};
165+
std::this_thread::sleep_for(SLEEP_TIME);
166+
}
167+
```
168+
169+
<center>
170+
[Check out request_response on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/request_response){ .md-button }
171+
</center>

iceoryx_meta/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ if(EXAMPLES)
7777
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/singleprocess ${CMAKE_BINARY_DIR}/iceoryx_examples/singleprocess)
7878
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/ice_access_control ${CMAKE_BINARY_DIR}/iceoryx_examples/ice_access_control)
7979
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/complexdata ${CMAKE_BINARY_DIR}/iceoryx_examples/complexdata)
80-
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/request_response ${CMAKE_BINARY_DIR}/iceoryx_examples/request_response_basic)
80+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/request_response ${CMAKE_BINARY_DIR}/iceoryx_examples/request_response)
8181
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/user_header ${CMAKE_BINARY_DIR}/iceoryx_examples/user_header)
8282
endif()
8383

0 commit comments

Comments
 (0)