From 1c56cae3e195e92e29e143248e7a41a7f91e74d5 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Fri, 3 May 2024 11:59:35 +0200 Subject: [PATCH 01/12] Added Service Sample for separate Server and Client --- ecal/service/CMakeLists.txt | 4 +- .../sample_client}/CMakeLists.txt | 2 +- .../samples/sample_client/src/main.cpp | 146 ++++++++++++++++++ .../samples/sample_server/CMakeLists.txt | 39 +++++ .../samples/sample_server/src/main.cpp | 116 ++++++++++++++ .../samples/sample_standalone/CMakeLists.txt | 39 +++++ .../sample_standalone}/src/main.cpp | 0 7 files changed, 344 insertions(+), 2 deletions(-) rename ecal/service/{sample => samples/sample_client}/CMakeLists.txt (97%) create mode 100644 ecal/service/samples/sample_client/src/main.cpp create mode 100644 ecal/service/samples/sample_server/CMakeLists.txt create mode 100644 ecal/service/samples/sample_server/src/main.cpp create mode 100644 ecal/service/samples/sample_standalone/CMakeLists.txt rename ecal/service/{sample => samples/sample_standalone}/src/main.cpp (100%) diff --git a/ecal/service/CMakeLists.txt b/ecal/service/CMakeLists.txt index 83a34bb9c0..dd248b13ac 100644 --- a/ecal/service/CMakeLists.txt +++ b/ecal/service/CMakeLists.txt @@ -21,7 +21,9 @@ add_subdirectory(ecal_service) # Samples if(ECAL_CORE_BUILD_SAMPLES) - add_subdirectory(sample) + add_subdirectory(samples/sample_client) + add_subdirectory(samples/sample_server) + add_subdirectory(samples/sample_standalone) endif() # Tests diff --git a/ecal/service/sample/CMakeLists.txt b/ecal/service/samples/sample_client/CMakeLists.txt similarity index 97% rename from ecal/service/sample/CMakeLists.txt rename to ecal/service/samples/sample_client/CMakeLists.txt index 0296bbc6e4..90c4cd4a67 100644 --- a/ecal/service/sample/CMakeLists.txt +++ b/ecal/service/samples/sample_client/CMakeLists.txt @@ -16,7 +16,7 @@ # # ========================= eCAL LICENSE ================================= -project(service_sample) +project(service_sample_client) find_package(Threads REQUIRED) diff --git a/ecal/service/samples/sample_client/src/main.cpp b/ecal/service/samples/sample_client/src/main.cpp new file mode 100644 index 0000000000..0f104640e5 --- /dev/null +++ b/ecal/service/samples/sample_client/src/main.cpp @@ -0,0 +1,146 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2023 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include +#include +#include + +void print_usage(const std::string& arg0) +{ + std::cout << "Usage: " << arg0 << " Host:Port [Host:Port ...] [--protocol-version ]" << std::endl; +} + +std::pair parse_server(const std::string& server) +{ + // Find the last ':' to split into host and port + auto pos = server.find_last_of(':'); + if (pos == std::string::npos) + { + throw std::runtime_error("Invalid server: " + server); + } + + return std::make_pair(server.substr(0, pos), static_cast(std::stoi(server.substr(pos + 1)))); +} + +int main(int argc, char** argv) +{ + // Convert command line arguments into vector + std::vector args; + for (int i = 0; i < argc; ++i) + { + args.push_back(argv[i]); + } + + std::vector> server_list; + std::uint8_t protocol_version = 1; + + // Parse command line arguments + for (size_t i = 1; i < args.size(); ++i) + { + if (args[i] == "-h" || args[i] == "--help") + { + print_usage(args[0]); + return 0; + } + else if (args[i] == "--protocol-version") + { + if (i + 1 < args.size()) + { + protocol_version = static_cast(std::stoi(args[i + 1])); + ++i; + } + else + { + print_usage(args[0]); + return 1; + } + } + else + { + try + { + server_list.push_back(parse_server(args[i])); + } + catch (const std::exception& e) + { + std::cerr << e.what() << std::endl; + print_usage(args[0]); + return 1; + } + } + } + + // Fail if the server list is empty + if (server_list.empty()) + { + print_usage(args[0]); + return 1; + } + + // Create an io_context + auto io_context = std::make_shared(); + + // Create a client manager + auto client_manager = eCAL::service::ClientManager::create(io_context, eCAL::service::default_logger("Server", eCAL::service::LogLevel::Info)); + + // Create and start an io_context thread. + // The io_context will be stopped, when the server_manager and client_manager are stopped. + std::thread io_context_thread([&io_context]() { io_context->run(); }); + + // Client Callback + // + // This callback will be called, when the service call is finished. + auto client_response_callback + = [](const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + if (error) + std::cerr << "Error calling service: " << error.ToString() << std::endl; + else + std::cout << "Received response: " << *response << std::endl; + }; + + // Event callbacks (empty) + auto client_event_callback = [](eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) {}; + + // Create client + // The client will connect to the server on the given port. + auto client = client_manager->create_client(protocol_version, server_list[0].first, server_list[0].second, client_event_callback); // TODO: use entire list + + // Call the service non-blocking. The response will be passed to the callback. + int counter = 1; + while(true) + { + const auto request = std::make_shared("Hello World " + std::to_string(counter)); + client->async_call_service(request, client_response_callback); + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + ++counter; + } + + std::cout << "Shutting down :)" << std::endl; + + // Use managers to stop servers and clients + client_manager->stop(); + + // Join the io_context thread + io_context_thread.join(); + +} diff --git a/ecal/service/samples/sample_server/CMakeLists.txt b/ecal/service/samples/sample_server/CMakeLists.txt new file mode 100644 index 0000000000..ffeca863a7 --- /dev/null +++ b/ecal/service/samples/sample_server/CMakeLists.txt @@ -0,0 +1,39 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2023 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +project(service_sample_server) + +find_package(Threads REQUIRED) + +set(sources + src/main.cpp +) + +add_executable(${PROJECT_NAME} ${sources}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + ecal_service) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER core/service) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${sources} +) diff --git a/ecal/service/samples/sample_server/src/main.cpp b/ecal/service/samples/sample_server/src/main.cpp new file mode 100644 index 0000000000..3ce2c9d7f8 --- /dev/null +++ b/ecal/service/samples/sample_server/src/main.cpp @@ -0,0 +1,116 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2023 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include +#include +#include + +void print_usage(const std::string& arg0) +{ + std::cout << "Usage: " << arg0 << " [--port ] [--protocol-version ]" << std::endl; +} + +int main(int argc, char** argv) +{ + uint16_t port = 0; + std::uint8_t protocol_version = 1; + + // convert command line arguments into vector + std::vector args; + for (int i = 0; i < argc; ++i) + { + args.push_back(argv[i]); + } + + // parse command line arguments + for (size_t i = 1; i < args.size(); ++i) + { + if (args[i] == "-h" || args[i] == "--help") + { + print_usage(args[0]); + return 0; + } + else if (args[i] == "--port") + { + if (i + 1 < args.size()) + { + port = static_cast(std::stoi(args[i + 1])); + ++i; + } + else + { + print_usage(args[0]); + return 1; + } + } + else if (args[i] == "--protocol-version") + { + if (i + 1 < args.size()) + { + protocol_version = static_cast(std::stoi(args[i + 1])); + ++i; + } + else + { + print_usage(args[0]); + return 1; + } + } + else + { + print_usage(args[0]); + return 1; + } + } + + // Create an io_context + auto io_context = std::make_shared(); + + // Create a server manager + auto server_manager = eCAL::service::ServerManager::create(io_context, eCAL::service::default_logger("Server", eCAL::service::LogLevel::Info)); + + // Server Service callback + // + // This callback will be called, when a client calls the service. + // It is responsible for filling the response object. + auto server_service_callback + = [](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + *response = "Response on \"" + *request + "\""; + }; + + // Event callbacks (empty) + auto server_event_callback = [](eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) {}; + + // Create server + // If the port is 0, the server will choose a port automatically + auto server = server_manager->create_server(protocol_version, port, server_service_callback, true, server_event_callback); + + // Print server port + std::cout << "Started Service Server on port: " << server->get_port() << std::endl; + std::cout << "Using protocol version: " << std::to_string(protocol_version) << std::endl; + + // Start io_context in main thread + io_context->run(); + + // Use managers to stop server + server_manager->stop(); +} diff --git a/ecal/service/samples/sample_standalone/CMakeLists.txt b/ecal/service/samples/sample_standalone/CMakeLists.txt new file mode 100644 index 0000000000..0806b506a7 --- /dev/null +++ b/ecal/service/samples/sample_standalone/CMakeLists.txt @@ -0,0 +1,39 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2023 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +project(service_sample_standalone) + +find_package(Threads REQUIRED) + +set(sources + src/main.cpp +) + +add_executable(${PROJECT_NAME} ${sources}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + ecal_service) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER core/service) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${sources} +) diff --git a/ecal/service/sample/src/main.cpp b/ecal/service/samples/sample_standalone/src/main.cpp similarity index 100% rename from ecal/service/sample/src/main.cpp rename to ecal/service/samples/sample_standalone/src/main.cpp From eaca1933184d2adcce2215a42d3a6aa6a5566019 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Fri, 3 May 2024 15:38:04 +0200 Subject: [PATCH 02/12] Service V1 protocol now supports a list of hosts to connect to --- .../include/ecal/service/client_manager.h | 15 +-- .../include/ecal/service/client_session.h | 48 ++++---- .../ecal_service/src/client_manager.cpp | 9 +- .../ecal_service/src/client_session.cpp | 57 +++++---- .../src/client_session_impl_v1.cpp | 108 +++++++++++++----- .../ecal_service/src/client_session_impl_v1.h | 35 +++--- .../samples/sample_client/src/main.cpp | 4 +- 7 files changed, 160 insertions(+), 116 deletions(-) diff --git a/ecal/service/ecal_service/include/ecal/service/client_manager.h b/ecal/service/ecal_service/include/ecal/service/client_manager.h index 39170c3bc1..0c09ae9014 100644 --- a/ecal/service/ecal_service/include/ecal/service/client_manager.h +++ b/ecal/service/ecal_service/include/ecal/service/client_manager.h @@ -23,10 +23,12 @@ #include #include #include - -#include #include #include +#include +#include + +#include namespace eCAL { @@ -141,16 +143,15 @@ namespace eCAL * stopped from this central place. * * @param protocol_version The protocol version to use for the client session. If 0, the legacy buggy protocol will be used. - * @param address The address of the server to connect to + * @param address The address of the server to connect to TODO: Update documentation * @param port The port of the server to connect to * @param event_callback The callback, that will be called, when the client has connected to the server or disconnected from it. The callback will be executed in the io_context thread. * * @return A shared_ptr to the newly created ClientSession instance */ - std::shared_ptr create_client(std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const ClientSession::EventCallbackT& event_callback); + std::shared_ptr create_client(std::uint8_t protocol_version + , const std::vector>& server_list + , const ClientSession::EventCallbackT& event_callback); /** * @brief Returns the number of managed client sessions diff --git a/ecal/service/ecal_service/include/ecal/service/client_session.h b/ecal/service/ecal_service/include/ecal/service/client_session.h index c7edeef3a0..6011b0485e 100644 --- a/ecal/service/ecal_service/include/ecal/service/client_session.h +++ b/ecal/service/ecal_service/include/ecal/service/client_session.h @@ -133,7 +133,7 @@ namespace eCAL * * @param io_context The io_context to use for the session and all callbacks. * @param protocol_version The protocol version to use for the session. When this is 0, the legacy buggy protocol is used. - * @param address The address of the server to connect to. May be an IP or a Hostname, IPv6 is supported. + * @param address The address of the server to connect to. May be an IP or a Hostname, IPv6 is supported. TODO: Update documentation * @param port The port of the server to connect to. * @param event_callback The callback to be called when the session's state changes, i.e. when the session successfully connected to a server or disconnected from it. * @param logger The logger to use for logging. @@ -141,35 +141,31 @@ namespace eCAL * * @return The new ClientSession instance as a shared_ptr. */ - static std::shared_ptr create(const std::shared_ptr& io_context - , std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger - , const DeleteCallbackT& delete_callback); + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger + , const DeleteCallbackT& delete_callback); - static std::shared_ptr create(const std::shared_ptr& io_context - , std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger = default_logger("Service Client")); + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger = default_logger("Service Client")); - static std::shared_ptr create(const std::shared_ptr& io_context - , std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const DeleteCallbackT& delete_callback); + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const DeleteCallbackT& delete_callback); protected: - ClientSession(const std::shared_ptr& io_context - , std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger); + ClientSession(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger); public: // Delete copy constructor and assignment operator diff --git a/ecal/service/ecal_service/src/client_manager.cpp b/ecal/service/ecal_service/src/client_manager.cpp index 3843fb07c0..eddde048c1 100644 --- a/ecal/service/ecal_service/src/client_manager.cpp +++ b/ecal/service/ecal_service/src/client_manager.cpp @@ -52,10 +52,9 @@ namespace eCAL /////////////////////////////////////////////////////// // Public API /////////////////////////////////////////////////////// - std::shared_ptr ClientManager::create_client(std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const ClientSession::EventCallbackT& event_callback) + std::shared_ptr ClientManager::create_client(std::uint8_t protocol_version + , const std::vector>& server_list + , const ClientSession::EventCallbackT& event_callback) { const std::lock_guard lock(client_manager_mutex_); if (stopped_) @@ -74,7 +73,7 @@ namespace eCAL } }; - auto client = ClientSession::create(io_context_, protocol_version, address, port, event_callback, logger_, deleter); + auto client = ClientSession::create(io_context_, protocol_version, server_list, event_callback, logger_, deleter); sessions_.emplace(client.get(), client); return client; } diff --git a/ecal/service/ecal_service/src/client_session.cpp b/ecal/service/ecal_service/src/client_session.cpp index c3a032f0c3..a624a92569 100644 --- a/ecal/service/ecal_service/src/client_session.cpp +++ b/ecal/service/ecal_service/src/client_session.cpp @@ -31,13 +31,12 @@ namespace eCAL { namespace service { - std::shared_ptr ClientSession::create(const std::shared_ptr& io_context - , std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger - , const DeleteCallbackT& delete_callback) + std::shared_ptr ClientSession::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger + , const DeleteCallbackT& delete_callback) { auto deleter = [delete_callback](ClientSession* session) { @@ -45,43 +44,41 @@ namespace eCAL delete session; // NOLINT(cppcoreguidelines-owning-memory) }; - return std::shared_ptr(new ClientSession(io_context, protocol_version, address, port, event_callback, logger), deleter); + return std::shared_ptr(new ClientSession(io_context, protocol_version, server_list, event_callback, logger), deleter); } - std::shared_ptr ClientSession::create(const std::shared_ptr& io_context - , std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger) + std::shared_ptr ClientSession::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger) { - return std::shared_ptr(new ClientSession(io_context, protocol_version, address, port, event_callback, logger)); + return std::shared_ptr(new ClientSession(io_context, protocol_version, server_list, event_callback, logger)); } - std::shared_ptr ClientSession::create(const std::shared_ptr& io_context - , std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const DeleteCallbackT& delete_callback) + std::shared_ptr ClientSession::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const DeleteCallbackT& delete_callback) { - return ClientSession::create(io_context, protocol_version, address, port, event_callback, default_logger("Service Client"), delete_callback); + return ClientSession::create(io_context, protocol_version, server_list, event_callback, default_logger("Service Client"), delete_callback); } - ClientSession::ClientSession(const std::shared_ptr& io_context - , std::uint8_t protocol_version - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger) + ClientSession::ClientSession(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger) { if (protocol_version == 0) { - impl_ = ClientSessionV0::create(io_context, address, port, event_callback, logger); + // TODO: Enable V0 protocol again + //impl_ = ClientSessionV0::create(io_context, address, port, event_callback, logger); } else { - impl_ = ClientSessionV1::create(io_context, address, port, event_callback, logger); + impl_ = ClientSessionV1::create(io_context, server_list, event_callback, logger); } } diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.cpp b/ecal/service/ecal_service/src/client_session_impl_v1.cpp index 25cd2e7a69..e0354ee71f 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v1.cpp @@ -42,27 +42,24 @@ namespace eCAL ///////////////////////////////////// // Constructor, Destructor, Create ///////////////////////////////////// - std::shared_ptr ClientSessionV1::create(const std::shared_ptr& io_context - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger) + std::shared_ptr ClientSessionV1::create(const std::shared_ptr& io_context + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger) { - std::shared_ptr instance(new ClientSessionV1(io_context, address, port, event_callback, logger)); + std::shared_ptr instance(new ClientSessionV1(io_context, server_list, event_callback, logger)); - instance->resolve_endpoint(); + instance->resolve_endpoint(0); return instance; } - ClientSessionV1::ClientSessionV1(const std::shared_ptr& io_context - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger) + ClientSessionV1::ClientSessionV1(const std::shared_ptr& io_context + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger) : ClientSessionBase(io_context, event_callback) - , address_ (address) - , port_ (port) + , server_list_ (server_list) , service_call_queue_strand_(*io_context) , resolver_ (*io_context) , logger_ (logger) @@ -83,21 +80,44 @@ namespace eCAL ////////////////////////////////////// // Connection establishement ////////////////////////////////////// - void ClientSessionV1::resolve_endpoint() + void ClientSessionV1::resolve_endpoint(size_t server_list_index) { - ECAL_SERVICE_LOG_DEBUG(logger_, "Resolving endpoint [" + address_ + ":" + std::to_string(port_) + "]..."); + ECAL_SERVICE_LOG_DEBUG(logger_, "Resolving endpoint [" + server_list_[server_list_index].first + ":" + std::to_string(server_list_[server_list_index].second) + "]..."); - const asio::ip::tcp::resolver::query query(address_, std::to_string(port_)); + const asio::ip::tcp::resolver::query query(server_list_[server_list_index].first, std::to_string(server_list_[server_list_index].second)); resolver_.async_resolve(query - , service_call_queue_strand_.wrap([me = enable_shared_from_this::shared_from_this()] + , service_call_queue_strand_.wrap([me = enable_shared_from_this::shared_from_this(), server_list_index] (asio::error_code ec, const asio::ip::tcp::resolver::iterator& resolved_endpoints) { if (ec) { - const std::string message = "Failed resolving endpoint [" + me->address_ + ":" + std::to_string(me->port_) + "]: " + ec.message(); - me->logger_(LogLevel::Error, message); - me->handle_connection_loss_error(message); +#if ECAL_SERVICE_LOG_DEBUG_ENABLED + { + const std::string message = "Failed resolving endpoint [" + me->server_list_[server_list_index].first + ":" + std::to_string(me->server_list_[server_list_index].second) + "]: " + ec.message(); + ECAL_SERVICE_LOG_DEBUG(me->logger_, message); + } +#endif + + if (server_list_index + 1 < me->server_list_.size()) + { + // Try next possible endpoint + me->resolve_endpoint(server_list_index + 1); + } + else + { + std::string message = "Failed resolving any endpoint: "; + for (size_t j = 0; j < me->server_list_.size(); ++j) + { + message += me->server_list_[j].first + ":" + std::to_string(me->server_list_[j].second); + if (j + 1 < me->server_list_.size()) + { + message += ", "; + } + } + me->logger_(LogLevel::Error, message); + me->handle_connection_loss_error(message); + } return; } else @@ -105,7 +125,7 @@ namespace eCAL #if ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED // Verbose-debug log of all endpoints { - std::string endpoints_str = "Resolved endpoints for " + me->address_ + ": "; + std::string endpoints_str = "Resolved endpoints for " + me->server_list_[server_list_index].first + ": "; for (auto it = resolved_endpoints; it != asio::ip::tcp::resolver::iterator(); ++it) { endpoints_str += endpoint_to_string(*it) + ", "; @@ -113,12 +133,16 @@ namespace eCAL ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, endpoints_str); } #endif //ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED - me->connect_to_endpoint(resolved_endpoints); + { + std::lock_guard chosen_endpoint_lock(me->chosen_endpoint_mutex_); + me->chosen_endpoint_ = me->server_list_[server_list_index]; + } + me->connect_to_endpoint(resolved_endpoints, server_list_index); } })); } - void ClientSessionV1::connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints) + void ClientSessionV1::connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints, size_t server_list_index) { // Convert the resolved_endpoints iterator to an endpoint sequence // (i.e. a vector of endpoints) @@ -131,14 +155,36 @@ namespace eCAL const std::lock_guard socket_lock(socket_mutex_); asio::async_connect(socket_ , *endpoint_sequence - , service_call_queue_strand_.wrap([me = shared_from_this(), endpoint_sequence](asio::error_code ec, const asio::ip::tcp::endpoint& endpoint) + , service_call_queue_strand_.wrap([me = shared_from_this(), endpoint_sequence, server_list_index](asio::error_code ec, const asio::ip::tcp::endpoint& endpoint) { (void)endpoint; if (ec) { - const std::string message = "Failed to connect to endpoint [" + me->address_ + ":" + std::to_string(me->port_) + "]: " + ec.message(); - me->logger_(LogLevel::Error, message); - me->handle_connection_loss_error(message); + { + // Log an error + const std::string message = "Failed to connect to endpoint [" + me->chosen_endpoint_.first + ":" + std::to_string(me->chosen_endpoint_.second) + "]: " + ec.message(); + me->logger_(LogLevel::Error, message); + } + + // If there are more servers available, try the next one + if (server_list_index + 1 < me->server_list_.size()) + { + me->resolve_endpoint(server_list_index + 1); + } + else + { + std::string message = "Failed to connect to any endpoint: "; + for (size_t j = 0; j < me->server_list_.size(); ++j) + { + message += me->server_list_[j].first + ":" + std::to_string(me->server_list_[j].second); + if (j + 1 < me->server_list_.size()) + { + message += ", "; + } + } + me->logger_(LogLevel::Error, message); + me->handle_connection_loss_error(message); + } return; } else @@ -479,12 +525,14 @@ namespace eCAL std::string ClientSessionV1::get_address() const { - return address_; + std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); + return chosen_endpoint_.first; } std::uint16_t ClientSessionV1::get_port() const { - return port_; + std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); + return chosen_endpoint_.second; } State ClientSessionV1::get_state() const diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.h b/ecal/service/ecal_service/src/client_session_impl_v1.h index ebd1a92529..412b33c778 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.h +++ b/ecal/service/ecal_service/src/client_session_impl_v1.h @@ -20,14 +20,17 @@ #pragma once #include "client_session_impl_base.h" + #include #include -#include - #include #include #include #include +#include +#include + +#include namespace eCAL { @@ -51,18 +54,16 @@ namespace eCAL // Constructor, Destructor, Create ///////////////////////////////////// public: - static std::shared_ptr create(const std::shared_ptr& io_context - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger_ = default_logger("Service Client V1")); + static std::shared_ptr create(const std::shared_ptr& io_context + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger_ = default_logger("Service Client V1")); protected: - ClientSessionV1(const std::shared_ptr& io_context - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger); + ClientSessionV1(const std::shared_ptr& io_context + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger); public: // Delete copy / move constructor and assignment operator @@ -78,8 +79,8 @@ namespace eCAL // Connection establishement ////////////////////////////////////// private: - void resolve_endpoint(); - void connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints); + void resolve_endpoint(size_t server_list_index); + void connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints, size_t server_list_index); void send_protocol_handshake_request(); void receive_protocol_handshake_response(); @@ -144,8 +145,10 @@ namespace eCAL static constexpr std::uint8_t MIN_SUPPORTED_PROTOCOL_VERSION = 1; static constexpr std::uint8_t MAX_SUPPORTED_PROTOCOL_VERSION = 1; - const std::string address_; //!< The original address that this client was created with. - const std::uint16_t port_; //!< The original port that this client was created with. + const std::vector> server_list_; //!< The list of servers that this client was created with. They will be tried in order. + + mutable std::mutex chosen_endpoint_mutex_; //!< Protects the chosen_endpoint_ variable. + std::pair chosen_endpoint_; //!< The endpoint that the client is currently connected to. Protected by chosen_endpoint_mutex_. asio::io_context::strand service_call_queue_strand_; asio::ip::tcp::resolver resolver_; diff --git a/ecal/service/samples/sample_client/src/main.cpp b/ecal/service/samples/sample_client/src/main.cpp index 0f104640e5..ae71053a81 100644 --- a/ecal/service/samples/sample_client/src/main.cpp +++ b/ecal/service/samples/sample_client/src/main.cpp @@ -99,7 +99,7 @@ int main(int argc, char** argv) auto io_context = std::make_shared(); // Create a client manager - auto client_manager = eCAL::service::ClientManager::create(io_context, eCAL::service::default_logger("Server", eCAL::service::LogLevel::Info)); + auto client_manager = eCAL::service::ClientManager::create(io_context, eCAL::service::default_logger("Server", eCAL::service::LogLevel::Debug)); // Create and start an io_context thread. // The io_context will be stopped, when the server_manager and client_manager are stopped. @@ -122,7 +122,7 @@ int main(int argc, char** argv) // Create client // The client will connect to the server on the given port. - auto client = client_manager->create_client(protocol_version, server_list[0].first, server_list[0].second, client_event_callback); // TODO: use entire list + auto client = client_manager->create_client(protocol_version, server_list, client_event_callback); // Call the service non-blocking. The response will be passed to the callback. int counter = 1; From a0f6f2d48dfc8f799462bf384768344ab9eab8b5 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Fri, 3 May 2024 17:11:39 +0200 Subject: [PATCH 03/12] Moved code to the proper place and added test --- .../include/ecal/service/client_session.h | 4 +- .../src/client_session_impl_v1.cpp | 9 +- .../test/src/ecal_tcp_service_test.cpp | 198 +++++++++++++----- 3 files changed, 154 insertions(+), 57 deletions(-) diff --git a/ecal/service/ecal_service/include/ecal/service/client_session.h b/ecal/service/ecal_service/include/ecal/service/client_session.h index 6011b0485e..c157baf23a 100644 --- a/ecal/service/ecal_service/include/ecal/service/client_session.h +++ b/ecal/service/ecal_service/include/ecal/service/client_session.h @@ -222,7 +222,7 @@ namespace eCAL */ eCAL::service::Error call_service(const std::shared_ptr& request, std::shared_ptr& response); - /** + /** TODO: Revise documentation * @brief Get the address that this client session has been created with. * * This function returns the address that this client session has been @@ -234,7 +234,7 @@ namespace eCAL */ std::string get_address() const; - /** + /**TODO: Revise documentation * @brief Get the port that this client session has been created with. * * This function returns the port that this client session has been diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.cpp b/ecal/service/ecal_service/src/client_session_impl_v1.cpp index e0354ee71f..f60b51c186 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v1.cpp @@ -133,10 +133,6 @@ namespace eCAL ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, endpoints_str); } #endif //ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED - { - std::lock_guard chosen_endpoint_lock(me->chosen_endpoint_mutex_); - me->chosen_endpoint_ = me->server_list_[server_list_index]; - } me->connect_to_endpoint(resolved_endpoints, server_list_index); } })); @@ -207,6 +203,11 @@ namespace eCAL } } + { + std::lock_guard chosen_endpoint_lock(me->chosen_endpoint_mutex_); + me->chosen_endpoint_ = me->server_list_[server_list_index]; + } + // Start sending the protocol handshake to the server. This will tell us the actual protocol version. me->send_protocol_handshake_request(); } diff --git a/ecal/service/test/src/ecal_tcp_service_test.cpp b/ecal/service/test/src/ecal_tcp_service_test.cpp index 67455b54d4..1084bc609b 100644 --- a/ecal/service/test/src/ecal_tcp_service_test.cpp +++ b/ecal/service/test/src/ecal_tcp_service_test.cpp @@ -59,9 +59,9 @@ eCAL::service::LoggerT critical_logger(const std::string& node_name) constexpr std::uint8_t min_protocol_version = 0; constexpr std::uint8_t max_protocol_version = 1; +// TODO: Add test for the new multi-host feature, where the second host is tried if the connection to the first one fails - -#if 1 +#if 0 TEST(ecal_service, RAII_TcpServiceServer) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -112,7 +112,7 @@ TEST(ecal_service, RAII_TcpServiceServer) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, RAII_TcpServiceClient) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -135,7 +135,7 @@ TEST(ecal_service, RAII_TcpServiceClient) // NOLINT io_context->run(); }); - auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, "127.0.0.1", 12345, client_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", 12345 }}, client_event_callback); io_context->stop(); io_thread->join(); @@ -143,7 +143,7 @@ TEST(ecal_service, RAII_TcpServiceClient) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, RAII_TcpServiceServerAndClient) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -201,7 +201,7 @@ TEST(ecal_service, RAII_TcpServiceServerAndClient) // NOLINT EXPECT_EQ(tcp_server->get_connection_count(), 0); - auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", tcp_server->get_port(), client_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", tcp_server->get_port() }}, client_event_callback); tcp_client_weak = client_v1; client_v1->async_call_service(std::make_shared("Hello World"), client_slow_response_callback); @@ -230,7 +230,7 @@ TEST(ecal_service, RAII_TcpServiceServerAndClient) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, RAII_StopDuringServiceCall) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -283,7 +283,7 @@ TEST(ecal_service, RAII_StopDuringServiceCall) // NOLINT io_context->run(); }); - auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", tcp_server->get_port(), client_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", tcp_server->get_port() }}, client_event_callback); tcp_client_weak = client_v1; client_v1->async_call_service(std::make_shared("Hello World"), client_slow_response_callback); @@ -309,7 +309,7 @@ TEST(ecal_service, RAII_StopDuringServiceCall) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, Communication_SlowCommunication) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -359,7 +359,7 @@ TEST(ecal_service, Communication_SlowCommunication) // NOLINT EXPECT_EQ(server->get_connection_count(), 0); } - auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -419,7 +419,7 @@ TEST(ecal_service, Communication_SlowCommunication) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, CallbacksConnectDisconnect_ClientDisconnectsFirst) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -470,7 +470,7 @@ TEST(ecal_service, CallbacksConnectDisconnect_ClientDisconnectsFirst) // NOLINT }; auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); - auto client = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + auto client = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -526,7 +526,7 @@ TEST(ecal_service, CallbacksConnectDisconnect_ClientDisconnectsFirst) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, CommunicationAndCallbacks_ClientsDisconnectFirst) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -604,7 +604,7 @@ TEST(ecal_service, CommunicationAndCallbacks_ClientsDisconnectFirst) // NOLINT EXPECT_EQ(server->get_connection_count(), 0); } - auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -719,7 +719,7 @@ TEST(ecal_service, CommunicationAndCallbacks_ClientsDisconnectFirst) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, CommunicationAndCallbacks_ServerDisconnectsFirst) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -777,7 +777,7 @@ TEST(ecal_service, CommunicationAndCallbacks_ServerDisconnectsFirst) // NOLINT }; auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); - auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -867,7 +867,7 @@ TEST(ecal_service, CommunicationAndCallbacks_ServerDisconnectsFirst) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunication) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -946,7 +946,7 @@ TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunication) // NOLINT num_client_event_callback_called++; }; - client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback, critical_logger("Client " + std::to_string(c)))); + client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback, critical_logger("Client " + std::to_string(c)))); } // Directly run a bunch of clients and call each client a bunch of times @@ -1022,7 +1022,7 @@ TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunication) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationNoParallelCalls) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1087,7 +1087,7 @@ TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationNoParallelCal num_clients_connected++; } }; - client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback, critical_logger("Client " + std::to_string(c)))); + client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback, critical_logger("Client " + std::to_string(c)))); } // wait for the clients to connect @@ -1149,7 +1149,7 @@ TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationNoParallelCal } #endif -#if 1 +#if 0 TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationMassivePayload) // NOLINT { // This test does not work for Protocol version 0 and there is no way to fix that (which is the reason why we invented protocol version 1) @@ -1234,7 +1234,7 @@ TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationMassivePayloa num_client_event_callback_called++; }; - client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback, critical_logger("Client " + std::to_string(c)))); + client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback, critical_logger("Client " + std::to_string(c)))); } // Directly run a bunch of clients and call each client a bunch of times @@ -1306,7 +1306,7 @@ TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationMassivePayloa } #endif -#if 1 +#if 0 TEST(ecal_service, Callback_ServerAndClientManagers) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1351,10 +1351,10 @@ TEST(ecal_service, Callback_ServerAndClientManagers) // NOLINT auto server1 = server_manager->create_server(protocol_version, 0, server_service_callback, true, increment_atomic_signalable(server1_event_callback_called)); auto server2 = server_manager->create_server(protocol_version, 0, server_service_callback, true, increment_atomic_signalable(server2_event_callback_called)); - auto client1_1 = client_manager->create_client(protocol_version, "127.0.0.1", server1->get_port(), increment_atomic_signalable(client1_1_event_callback_called)); - auto client1_2 = client_manager->create_client(protocol_version, "127.0.0.1", server1->get_port(), increment_atomic_signalable(client1_2_event_callback_called)); - auto client2_1 = client_manager->create_client(protocol_version, "127.0.0.1", server2->get_port(), increment_atomic_signalable(client2_1_event_callback_called)); - auto client2_2 = client_manager->create_client(protocol_version, "127.0.0.1", server2->get_port(), increment_atomic_signalable(client2_2_event_callback_called)); + auto client1_1 = client_manager->create_client(protocol_version, {{ "127.0.0.1", server1->get_port() }}, increment_atomic_signalable(client1_1_event_callback_called)); + auto client1_2 = client_manager->create_client(protocol_version, {{ "127.0.0.1", server1->get_port() }}, increment_atomic_signalable(client1_2_event_callback_called)); + auto client2_1 = client_manager->create_client(protocol_version, {{ "127.0.0.1", server2->get_port() }}, increment_atomic_signalable(client2_1_event_callback_called)); + auto client2_2 = client_manager->create_client(protocol_version, {{ "127.0.0.1", server2->get_port() }}, increment_atomic_signalable(client2_2_event_callback_called)); // Wait for the clients to be connected client1_1_event_callback_called.wait_for([&](int value) { return value >= 1; }, std::chrono::seconds(5)); @@ -1441,7 +1441,7 @@ TEST(ecal_service, Callback_ServerAndClientManagers) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, Callback_ServiceCallFromCallback) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1474,7 +1474,7 @@ TEST(ecal_service, Callback_ServiceCallFromCallback) // NOLINT EXPECT_EQ(num_client_response_callback1_called, 0); EXPECT_EQ(num_client_response_callback2_called, 0); - auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); const eCAL::service::ClientSession::ResponseCallbackT response_callback = [&num_client_response_callback1_called, &num_client_response_callback2_called, client_v1] @@ -1511,7 +1511,7 @@ TEST(ecal_service, Callback_ServiceCallFromCallback) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, Callback_SerializedServiceCallbacks) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1565,7 +1565,7 @@ TEST(ecal_service, Callback_SerializedServiceCallbacks) // NOLINT clients.reserve(num_clients); for (int i = 0; i < num_clients; i++) { - clients.push_back(client_manager->create_client(protocol_version, "127.0.0.1", server->get_port(), client_event_callback)); + clients.push_back(client_manager->create_client(protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback)); } num_client_event_callback_called.wait_for([&num_clients](int value) -> bool { return value >= num_clients; }, std::chrono::milliseconds(500)); @@ -1605,9 +1605,9 @@ TEST(ecal_service, Callback_SerializedServiceCallbacks) // NOLINT } #endif -#if 1 +#if 0 // Call different eCAL Service API functions from within the callbacks -TEST(ecal_service, Callback_ApiCallsFromCallbacks) +TEST(ecal_service, Callback_ApiCallsFromCallbacks) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) { @@ -1704,7 +1704,7 @@ TEST(ecal_service, Callback_ApiCallsFromCallbacks) EXPECT_EQ(num_client_response_callback_called.get(), 0); EXPECT_EQ(num_client_event_callback_called.get(), 0); - client = eCAL::service::ClientSession::create(io_context, protocol_version, "127.0.0.1", server->get_port(), client_event_callback); + client = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -1750,7 +1750,103 @@ TEST(ecal_service, Callback_ApiCallsFromCallbacks) } #endif -#if 1 +#if 2 +// Connect to a list of hosts of which the first one does not exist, so the next one is used and connected to +TEST(ecal_service, BackupHost) +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + atomic_signalable num_server_service_callback_called(0); + atomic_signalable num_server_event_callback_called (0); + atomic_signalable num_client_response_callback_called(0); + atomic_signalable num_client_event_callback_called (0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called](const std::shared_ptr& /*request*/, const std::shared_ptr& /*response*/) -> void + { + num_server_service_callback_called++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [&num_server_event_callback_called](eCAL::service::ServerEventType event, const std::string& /*message*/) -> void + { + num_server_event_callback_called++; + }; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called](eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + num_client_event_callback_called++; + }; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + + EXPECT_EQ(num_server_service_callback_called, 0); + EXPECT_EQ(num_server_event_callback_called, 0); + EXPECT_EQ(num_client_response_callback_called, 0); + EXPECT_EQ(num_client_event_callback_called, 0); + + std::vector> server_list = + { + { "NonExistingEndpoint", 123 }, // This endpoint cannot be resolved + { "127.0.0.1", 123 }, // This endpoint can be resolved, but the port is wrong + { "127.0.0.1", server->get_port() } // This endpoint is the correct one and will be tried last + }; + + auto client = eCAL::service::ClientSession::create(io_context, protocol_version, server_list, client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Wait for the connected events to be called + num_server_event_callback_called.wait_for([](int value) { return value >= 1; }, std::chrono::milliseconds(10000)); // Going through the wrong endpoints may take some time + num_client_event_callback_called.wait_for([](int value) { return value >= 1; }, std::chrono::milliseconds(10000)); // Going through the wrong endpoints may take some time + + EXPECT_EQ(num_server_service_callback_called, 0); + EXPECT_EQ(num_server_event_callback_called, 1); + EXPECT_EQ(num_client_response_callback_called, 0); + EXPECT_EQ(num_client_event_callback_called, 1); + + // Check what host the client is connected to + auto connected_host = client->get_address(); + auto connected_port = client->get_port(); + EXPECT_EQ(connected_host, "127.0.0.1"); + EXPECT_EQ(connected_port, server->get_port()); + + // Call service and wait for the response + const eCAL::service::ClientSession::ResponseCallbackT response_callback + = [&num_client_response_callback_called](const eCAL::service::Error& error, const std::shared_ptr& /*response*/) -> void + { + EXPECT_FALSE(bool(error)); + num_client_response_callback_called++; + }; + + client->async_call_service(std::make_shared("Hello World"), response_callback); + + num_client_response_callback_called.wait_for([](int value) { return value >= 1; }, std::chrono::milliseconds(500)); + + EXPECT_EQ(num_server_service_callback_called, 1); + EXPECT_EQ(num_server_event_callback_called, 1); + EXPECT_EQ(num_client_response_callback_called, 1); + EXPECT_EQ(num_client_event_callback_called, 1); + + // Shutdown + server = nullptr; + client = nullptr; + + // join the io_thread + io_context->stop(); + io_thread.join(); + } +} +#endif + +#if 0 TEST(ecal_service, ErrorCallback_ErrorCallbackNoServer) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1779,7 +1875,7 @@ TEST(ecal_service, ErrorCallback_ErrorCallbackNoServer) // NOLINT EXPECT_EQ(num_client_response_callback_called, 0); - auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, "NonExistingEndpoint", 12345, client_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "NonExistingEndpoint", 12345 }}, client_event_callback); // Run the io_service std::thread io_thread([&io_context]() @@ -1802,7 +1898,7 @@ TEST(ecal_service, ErrorCallback_ErrorCallbackNoServer) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, ErrorCallback_ErrorCallbackServerHasDisconnected) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1854,7 +1950,7 @@ TEST(ecal_service, ErrorCallback_ErrorCallbackServerHasDisconnected) // NOLINT }; auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); - auto client = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + auto client = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -1969,7 +2065,7 @@ TEST(ecal_service, ErrorCallback_ErrorCallbackServerHasDisconnected) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, ErrorCallback_ErrorCallbackClientDisconnects) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2001,7 +2097,7 @@ TEST(ecal_service, ErrorCallback_ErrorCallbackClientDisconnects) // NOLINT {}; auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); - auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -2068,7 +2164,7 @@ TEST(ecal_service, ErrorCallback_ErrorCallbackClientDisconnects) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThrough) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2156,7 +2252,7 @@ TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThrough) // NOLINT num_client_event_callback_called++; }; - client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback, critical_logger("Client " + std::to_string(c)))); + client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback, critical_logger("Client " + std::to_string(c)))); } // Directly run a bunch of clients and call each client a bunch of times @@ -2251,7 +2347,7 @@ TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThrough) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThroughWithManagers) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2340,7 +2436,7 @@ TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThroughWithManagers) // N num_client_event_callback_called++; }; - client_list.push_back(client_manager->create_client(protocol_version,"127.0.0.1", server->get_port(), client_event_callback)); + client_list.push_back(client_manager->create_client(protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback)); } // Directly run a bunch of clients and call each client a bunch of times @@ -2433,7 +2529,7 @@ TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThroughWithManagers) // N } #endif -#if 1 +#if 0 TEST(ecal_service, BlockingCall_RegularBlockingCall) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2466,7 +2562,7 @@ TEST(ecal_service, BlockingCall_RegularBlockingCall) // NOLINT {}; auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); - auto client = eCAL::service::ClientSession::create(io_context, protocol_version, "127.0.0.1", server->get_port(), client_event_callback); + auto client = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -2504,7 +2600,7 @@ TEST(ecal_service, BlockingCall_RegularBlockingCall) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, BlockingCall_BlockingCallWithErrorHalfwayThrough) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2538,7 +2634,7 @@ TEST(ecal_service, BlockingCall_BlockingCallWithErrorHalfwayThrough) // NOLINT {}; auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); - auto client = eCAL::service::ClientSession::create(io_context, protocol_version, "127.0.0.1", server->get_port(), client_event_callback); + auto client = eCAL::service::ClientSession::create(io_context, protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -2638,7 +2734,7 @@ TEST(ecal_service, BlockingCall_BlockingCallWithErrorHalfwayThrough) // NOLINT } #endif -#if 1 +#if 0 TEST(ecal_service, BlockingCall_Stopped) // NOLINT // This test shows the proper way to stop everything. I should adapt all other tests, too { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2671,7 +2767,7 @@ TEST(ecal_service, BlockingCall_Stopped) // NOLINT // This test shows the prope {}; auto server = server_manager->create_server(protocol_version, 0, server_service_callback, true, server_event_callback); - auto client = client_manager->create_client(protocol_version, "127.0.0.1", server->get_port(), client_event_callback); + auto client = client_manager->create_client(protocol_version, {{ "127.0.0.1", server->get_port() }}, client_event_callback); std::thread io_thread([&io_context]() { @@ -2739,4 +2835,4 @@ TEST(ecal_service, BlockingCall_Stopped) // NOLINT // This test shows the prope } } } -#endif \ No newline at end of file +#endif From c4808700a23e9ffd99b7d1a3fc9a3f6769620534 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Fri, 3 May 2024 17:28:00 +0200 Subject: [PATCH 04/12] Implemented Backup domains in v0 protocol --- .../ecal_service/src/client_session.cpp | 3 +- .../src/client_session_impl_v0.cpp | 111 +++++++++++++----- .../ecal_service/src/client_session_impl_v0.h | 33 +++--- .../test/src/ecal_tcp_service_test.cpp | 48 ++++---- 4 files changed, 124 insertions(+), 71 deletions(-) diff --git a/ecal/service/ecal_service/src/client_session.cpp b/ecal/service/ecal_service/src/client_session.cpp index a624a92569..535895e678 100644 --- a/ecal/service/ecal_service/src/client_session.cpp +++ b/ecal/service/ecal_service/src/client_session.cpp @@ -73,8 +73,7 @@ namespace eCAL { if (protocol_version == 0) { - // TODO: Enable V0 protocol again - //impl_ = ClientSessionV0::create(io_context, address, port, event_callback, logger); + impl_ = ClientSessionV0::create(io_context, server_list, event_callback, logger); } else { diff --git a/ecal/service/ecal_service/src/client_session_impl_v0.cpp b/ecal/service/ecal_service/src/client_session_impl_v0.cpp index 739e14b5de..cb51837167 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v0.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v0.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include namespace eCAL @@ -38,27 +39,24 @@ namespace eCAL ///////////////////////////////////// // Constructor, Destructor, Create ///////////////////////////////////// - std::shared_ptr ClientSessionV0::create(const std::shared_ptr& io_context - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger) + std::shared_ptr ClientSessionV0::create(const std::shared_ptr& io_context + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger) { - std::shared_ptr instance(new ClientSessionV0(io_context, address, port, event_callback, logger)); + std::shared_ptr instance(new ClientSessionV0(io_context, server_list, event_callback, logger)); - instance->resolve_endpoint(); + instance->resolve_endpoint(0); // TODO: Write a test that checks what happens when the server_list is empty return instance; } - ClientSessionV0::ClientSessionV0(const std::shared_ptr& io_context - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger) + ClientSessionV0::ClientSessionV0(const std::shared_ptr& io_context + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger) : ClientSessionBase(io_context, event_callback) - , address_ (address) - , port_ (port) + , server_list_ (server_list) , service_call_queue_strand_(*io_context) , resolver_ (*io_context) , logger_ (logger) @@ -78,21 +76,44 @@ namespace eCAL ////////////////////////////////////// // Connection establishement ////////////////////////////////////// - void ClientSessionV0::resolve_endpoint() + void ClientSessionV0::resolve_endpoint(size_t server_list_index) { - ECAL_SERVICE_LOG_DEBUG(logger_, "Resolving endpoint [" + address_ + ":" + std::to_string(port_) + "]..."); + ECAL_SERVICE_LOG_DEBUG(logger_, "Resolving endpoint [" + server_list_[server_list_index].first + ":" + std::to_string(server_list_[server_list_index].second) + "]..."); - const asio::ip::tcp::resolver::query query(address_, std::to_string(port_)); + const asio::ip::tcp::resolver::query query(server_list_[server_list_index].first, std::to_string(server_list_[server_list_index].second)); resolver_.async_resolve(query - , service_call_queue_strand_.wrap([me = enable_shared_from_this::shared_from_this()] + , service_call_queue_strand_.wrap([me = enable_shared_from_this::shared_from_this(), server_list_index] (asio::error_code ec, const asio::ip::tcp::resolver::iterator& resolved_endpoints) { if (ec) { - const std::string message = "Failed resolving endpoint [" + me->address_ + ":" + std::to_string(me->port_) + "]: " + ec.message(); - me->logger_(LogLevel::Error, message); - me->handle_connection_loss_error(message); +#if ECAL_SERVICE_LOG_DEBUG_ENABLED + { + const std::string message = "Failed resolving endpoint [" + me->server_list_[server_list_index].first + ":" + std::to_string(me->server_list_[server_list_index].second) + "]: " + ec.message(); + ECAL_SERVICE_LOG_DEBUG(me->logger_, message); + } +#endif + + if (server_list_index + 1 < me->server_list_.size()) + { + // Try next possible endpoint + me->resolve_endpoint(server_list_index + 1); + } + else + { + std::string message = "Failed resolving any endpoint: "; + for (size_t j = 0; j < me->server_list_.size(); ++j) + { + message += me->server_list_[j].first + ":" + std::to_string(me->server_list_[j].second); + if (j + 1 < me->server_list_.size()) + { + message += ", "; + } + } + me->logger_(LogLevel::Error, message); + me->handle_connection_loss_error(message); + } return; } else @@ -100,7 +121,7 @@ namespace eCAL #if ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED // Verbose-debug log of all endpoints { - std::string endpoints_str = "Resolved endpoints for " + me->address_ + ": "; + std::string endpoints_str = "Resolved endpoints for " + me->server_list_[server_list_index].first + ": "; for (auto it = resolved_endpoints; it != asio::ip::tcp::resolver::iterator(); ++it) { endpoints_str += endpoint_to_string(*it) + ", "; @@ -108,12 +129,12 @@ namespace eCAL ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, endpoints_str); } #endif //ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED - me->connect_to_endpoint(resolved_endpoints); + me->connect_to_endpoint(resolved_endpoints, server_list_index); } })); } - void ClientSessionV0::connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints) + void ClientSessionV0::connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints, size_t server_list_index) { // Convert the resolved_endpoints iterator to an endpoint sequence // (i.e. a vector of endpoints) @@ -126,14 +147,36 @@ namespace eCAL const std::lock_guard socket_lock(socket_mutex_); asio::async_connect(socket_ , *endpoint_sequence - , service_call_queue_strand_.wrap([me = shared_from_this(), endpoint_sequence](asio::error_code ec, const asio::ip::tcp::endpoint& endpoint) + , service_call_queue_strand_.wrap([me = shared_from_this(), endpoint_sequence, server_list_index](asio::error_code ec, const asio::ip::tcp::endpoint& endpoint) { (void)endpoint; if (ec) { - const std::string message = "Failed to connect to endpoint [" + me->address_ + ":" + std::to_string(me->port_) + "]: " + ec.message(); - me->logger_(LogLevel::Error, message); - me->handle_connection_loss_error(message); + { + // Log an error + const std::string message = "Failed to connect to endpoint [" + me->chosen_endpoint_.first + ":" + std::to_string(me->chosen_endpoint_.second) + "]: " + ec.message(); + me->logger_(LogLevel::Error, message); + } + + // If there are more servers available, try the next one + if (server_list_index + 1 < me->server_list_.size()) + { + me->resolve_endpoint(server_list_index + 1); + } + else + { + std::string message = "Failed to connect to any endpoint: "; + for (size_t j = 0; j < me->server_list_.size(); ++j) + { + message += me->server_list_[j].first + ":" + std::to_string(me->server_list_[j].second); + if (j + 1 < me->server_list_.size()) + { + message += ", "; + } + } + me->logger_(LogLevel::Error, message); + me->handle_connection_loss_error(message); + } return; } else @@ -156,6 +199,12 @@ namespace eCAL } } + { + // Set the chosen endpoint + std::lock_guard chosen_endpoint_lock(me->chosen_endpoint_mutex_); + me->chosen_endpoint_ = me->server_list_[server_list_index]; + } + const std::string message = "Connected to server. Using protocol version 0."; me->logger_(LogLevel::Info, "[" + get_connection_info_string(me->socket_) + "] " + message); @@ -356,12 +405,14 @@ namespace eCAL std::string ClientSessionV0::get_address() const { - return address_; + std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); + return chosen_endpoint_.first; } std::uint16_t ClientSessionV0::get_port() const { - return port_; + std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); + return chosen_endpoint_.second; } State ClientSessionV0::get_state() const diff --git a/ecal/service/ecal_service/src/client_session_impl_v0.h b/ecal/service/ecal_service/src/client_session_impl_v0.h index 96017f1be3..a6309da8a3 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v0.h +++ b/ecal/service/ecal_service/src/client_session_impl_v0.h @@ -20,13 +20,16 @@ #pragma once #include "client_session_impl_base.h" -#include + #include +#include #include #include #include #include +#include +#include namespace eCAL { @@ -50,18 +53,16 @@ namespace eCAL // Constructor, Destructor, Create ///////////////////////////////////// public: - static std::shared_ptr create(const std::shared_ptr& io_context - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger = default_logger("Service Client V1")); + static std::shared_ptr create(const std::shared_ptr& io_context + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger = default_logger("Service Client V1")); protected: - ClientSessionV0(const std::shared_ptr& io_context - , const std::string& address - , std::uint16_t port - , const EventCallbackT& event_callback - , const LoggerT& logger); + ClientSessionV0(const std::shared_ptr& io_context + , const std::vector>& server_list + , const EventCallbackT& event_callback + , const LoggerT& logger); public: // Delete copy / move constructor and assignment operator @@ -77,8 +78,8 @@ namespace eCAL // Connection establishement ////////////////////////////////////// private: - void resolve_endpoint(); - void connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints); + void resolve_endpoint(size_t server_list_index); + void connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints, size_t server_list_index); ////////////////////////////////////// // Service calls @@ -137,8 +138,10 @@ namespace eCAL ////////////////////////////////////// private: - const std::string address_; //!< The original address that this client was created with. - const std::uint16_t port_; //!< The original port that this client was created with. + const std::vector> server_list_; //!< The list of servers that this client was created with. They will be tried in order. + + mutable std::mutex chosen_endpoint_mutex_; //!< Protects the chosen_endpoint_ variable. + std::pair chosen_endpoint_; //!< The endpoint that the client is currently connected to. Protected by chosen_endpoint_mutex_. asio::io_context::strand service_call_queue_strand_; asio::ip::tcp::resolver resolver_; diff --git a/ecal/service/test/src/ecal_tcp_service_test.cpp b/ecal/service/test/src/ecal_tcp_service_test.cpp index 1084bc609b..a44f4ae963 100644 --- a/ecal/service/test/src/ecal_tcp_service_test.cpp +++ b/ecal/service/test/src/ecal_tcp_service_test.cpp @@ -61,7 +61,7 @@ constexpr std::uint8_t max_protocol_version = 1; // TODO: Add test for the new multi-host feature, where the second host is tried if the connection to the first one fails -#if 0 +#if 1 TEST(ecal_service, RAII_TcpServiceServer) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -112,7 +112,7 @@ TEST(ecal_service, RAII_TcpServiceServer) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, RAII_TcpServiceClient) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -143,7 +143,7 @@ TEST(ecal_service, RAII_TcpServiceClient) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, RAII_TcpServiceServerAndClient) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -230,7 +230,7 @@ TEST(ecal_service, RAII_TcpServiceServerAndClient) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, RAII_StopDuringServiceCall) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -309,7 +309,7 @@ TEST(ecal_service, RAII_StopDuringServiceCall) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, Communication_SlowCommunication) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -419,7 +419,7 @@ TEST(ecal_service, Communication_SlowCommunication) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, CallbacksConnectDisconnect_ClientDisconnectsFirst) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -526,7 +526,7 @@ TEST(ecal_service, CallbacksConnectDisconnect_ClientDisconnectsFirst) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, CommunicationAndCallbacks_ClientsDisconnectFirst) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -719,7 +719,7 @@ TEST(ecal_service, CommunicationAndCallbacks_ClientsDisconnectFirst) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, CommunicationAndCallbacks_ServerDisconnectsFirst) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -867,7 +867,7 @@ TEST(ecal_service, CommunicationAndCallbacks_ServerDisconnectsFirst) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunication) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1022,7 +1022,7 @@ TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunication) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationNoParallelCalls) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1149,7 +1149,7 @@ TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationNoParallelCal } #endif -#if 0 +#if 1 TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationMassivePayload) // NOLINT { // This test does not work for Protocol version 0 and there is no way to fix that (which is the reason why we invented protocol version 1) @@ -1306,7 +1306,7 @@ TEST(ecal_service, CommunicationAndCallbacks_StressfulCommunicationMassivePayloa } #endif -#if 0 +#if 1 TEST(ecal_service, Callback_ServerAndClientManagers) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1441,7 +1441,7 @@ TEST(ecal_service, Callback_ServerAndClientManagers) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, Callback_ServiceCallFromCallback) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1511,7 +1511,7 @@ TEST(ecal_service, Callback_ServiceCallFromCallback) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, Callback_SerializedServiceCallbacks) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1605,7 +1605,7 @@ TEST(ecal_service, Callback_SerializedServiceCallbacks) // NOLINT } #endif -#if 0 +#if 1 // Call different eCAL Service API functions from within the callbacks TEST(ecal_service, Callback_ApiCallsFromCallbacks) // NOLINT { @@ -1750,7 +1750,7 @@ TEST(ecal_service, Callback_ApiCallsFromCallbacks) // NOLINT } #endif -#if 2 +#if 1 // Connect to a list of hosts of which the first one does not exist, so the next one is used and connected to TEST(ecal_service, BackupHost) { @@ -1846,7 +1846,7 @@ TEST(ecal_service, BackupHost) } #endif -#if 0 +#if 1 TEST(ecal_service, ErrorCallback_ErrorCallbackNoServer) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -1898,7 +1898,7 @@ TEST(ecal_service, ErrorCallback_ErrorCallbackNoServer) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, ErrorCallback_ErrorCallbackServerHasDisconnected) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2065,7 +2065,7 @@ TEST(ecal_service, ErrorCallback_ErrorCallbackServerHasDisconnected) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, ErrorCallback_ErrorCallbackClientDisconnects) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2164,7 +2164,7 @@ TEST(ecal_service, ErrorCallback_ErrorCallbackClientDisconnects) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThrough) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2347,7 +2347,7 @@ TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThrough) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThroughWithManagers) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2529,7 +2529,7 @@ TEST(ecal_service, ErrorCallback_StressfulErrorsHalfwayThroughWithManagers) // N } #endif -#if 0 +#if 1 TEST(ecal_service, BlockingCall_RegularBlockingCall) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2600,7 +2600,7 @@ TEST(ecal_service, BlockingCall_RegularBlockingCall) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, BlockingCall_BlockingCallWithErrorHalfwayThrough) // NOLINT { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) @@ -2734,7 +2734,7 @@ TEST(ecal_service, BlockingCall_BlockingCallWithErrorHalfwayThrough) // NOLINT } #endif -#if 0 +#if 1 TEST(ecal_service, BlockingCall_Stopped) // NOLINT // This test shows the proper way to stop everything. I should adapt all other tests, too { for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) From d6466b20cce73e27bdfe96a51ea9893716413fe5 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Mon, 6 May 2024 09:17:34 +0200 Subject: [PATCH 05/12] Fixed build issues --- ecal/service/CMakeLists.txt | 5 ++++- ecal/service/samples/sample_standalone/src/main.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ecal/service/CMakeLists.txt b/ecal/service/CMakeLists.txt index dd248b13ac..8743239f47 100644 --- a/ecal/service/CMakeLists.txt +++ b/ecal/service/CMakeLists.txt @@ -16,6 +16,9 @@ # # ========================= eCAL LICENSE ================================= +cmake_minimum_required(VERSION 3.16) +project(ecal_service) + # Main library add_subdirectory(ecal_service) @@ -29,4 +32,4 @@ endif() # Tests if(ECAL_CORE_BUILD_TESTS) add_subdirectory(test) -endif() \ No newline at end of file +endif() diff --git a/ecal/service/samples/sample_standalone/src/main.cpp b/ecal/service/samples/sample_standalone/src/main.cpp index be38fbca0f..751b20a331 100644 --- a/ecal/service/samples/sample_standalone/src/main.cpp +++ b/ecal/service/samples/sample_standalone/src/main.cpp @@ -69,7 +69,7 @@ int main(int /*argc*/, char** /*argv*/) // Create client // The client will connect to the server on the given port. - auto client = client_manager->create_client(1, "127.0.0.1", server->get_port(), client_event_callback); + auto client = client_manager->create_client(1, {{ "127.0.0.1", server->get_port() }}, client_event_callback); // Call the service non-blocking. The response will be passed to the callback. for (int i = 1; i <= 10; i++) From 72bbd526aacc043c1cb2cc0799e0b8e9907463e8 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Mon, 6 May 2024 09:20:18 +0200 Subject: [PATCH 06/12] Fixed logging name --- ecal/service/samples/sample_client/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecal/service/samples/sample_client/src/main.cpp b/ecal/service/samples/sample_client/src/main.cpp index ae71053a81..89f47ebddd 100644 --- a/ecal/service/samples/sample_client/src/main.cpp +++ b/ecal/service/samples/sample_client/src/main.cpp @@ -99,7 +99,7 @@ int main(int argc, char** argv) auto io_context = std::make_shared(); // Create a client manager - auto client_manager = eCAL::service::ClientManager::create(io_context, eCAL::service::default_logger("Server", eCAL::service::LogLevel::Debug)); + auto client_manager = eCAL::service::ClientManager::create(io_context, eCAL::service::default_logger("Client", eCAL::service::LogLevel::DebugVerbose)); // Create and start an io_context thread. // The io_context will be stopped, when the server_manager and client_manager are stopped. From 5f2b5f0ed5e08e6d49e803b60abf9936f5343f7b Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Mon, 6 May 2024 09:22:27 +0200 Subject: [PATCH 07/12] Switched LogLevel to DebugVerbose --- ecal/service/samples/sample_server/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecal/service/samples/sample_server/src/main.cpp b/ecal/service/samples/sample_server/src/main.cpp index 3ce2c9d7f8..09eccf8e0e 100644 --- a/ecal/service/samples/sample_server/src/main.cpp +++ b/ecal/service/samples/sample_server/src/main.cpp @@ -85,7 +85,7 @@ int main(int argc, char** argv) auto io_context = std::make_shared(); // Create a server manager - auto server_manager = eCAL::service::ServerManager::create(io_context, eCAL::service::default_logger("Server", eCAL::service::LogLevel::Info)); + auto server_manager = eCAL::service::ServerManager::create(io_context, eCAL::service::default_logger("Server", eCAL::service::LogLevel::DebugVerbose)); // Server Service callback // From bd1d18bd0dc637a0f8e8d86834d06dc38a324cc4 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Mon, 6 May 2024 16:03:37 +0200 Subject: [PATCH 08/12] Adapted eCAL to new service API and added hardcoded .local backup --- ecal/core/src/service/ecal_service_client_impl.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ecal/core/src/service/ecal_service_client_impl.cpp b/ecal/core/src/service/ecal_service_client_impl.cpp index 1124ee0f72..3a3bd20e10 100644 --- a/ecal/core/src/service/ecal_service_client_impl.cpp +++ b/ecal/core/src/service/ecal_service_client_impl.cpp @@ -751,7 +751,12 @@ namespace eCAL const auto port_to_use = (protocol_version == 0 ? iter.tcp_port_v0 : iter.tcp_port_v1); // Create the client and add it to the map - const auto new_client_session = client_manager->create_client(static_cast(protocol_version), iter.hname, port_to_use, event_callback); + const std::vector> endpoint_list + { + {iter.hname, port_to_use}, + {iter.hname + ".local", port_to_use}, // TODO: Make this configurable from the ecal.ini + }; + const auto new_client_session = client_manager->create_client(static_cast(protocol_version), endpoint_list, event_callback); if (new_client_session) m_client_map[iter.key] = new_client_session; } From 8cbe187989ebd864fa3a57cbe6c5b860dac869fb Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Mon, 6 May 2024 18:32:15 +0200 Subject: [PATCH 09/12] Fixed most clang-tidy warnings --- .../include/ecal/service/client_manager.h | 6 +++- .../include/ecal/service/client_session.h | 3 ++ .../include/ecal/service/server_manager.h | 8 +++-- .../ecal_service/src/client_manager.cpp | 10 ++++++- .../ecal_service/src/client_session.cpp | 12 +++++++- .../src/client_session_impl_base.h | 1 - .../src/client_session_impl_v0.cpp | 24 ++++++++++----- .../ecal_service/src/client_session_impl_v0.h | 8 +++-- .../src/client_session_impl_v1.cpp | 30 ++++++++++++++----- .../ecal_service/src/client_session_impl_v1.h | 4 +++ ecal/service/ecal_service/src/log_defs.h | 2 -- ecal/service/ecal_service/src/protocol_v0.cpp | 9 ++++++ ecal/service/ecal_service/src/protocol_v1.cpp | 8 +++++ ecal/service/ecal_service/src/server.cpp | 7 +++-- ecal/service/ecal_service/src/server_impl.cpp | 17 +++++++---- ecal/service/ecal_service/src/server_impl.h | 3 -- .../ecal_service/src/server_manager.cpp | 8 ++++- .../src/server_session_impl_base.h | 8 ++--- .../src/server_session_impl_v0.cpp | 21 ++++++++++--- .../ecal_service/src/server_session_impl_v0.h | 8 +++-- .../src/server_session_impl_v1.cpp | 22 +++++++++++--- .../ecal_service/src/server_session_impl_v1.h | 9 ++++-- .../samples/sample_client/src/main.cpp | 22 +++++++++++--- .../samples/sample_server/src/main.cpp | 18 +++++++---- .../samples/sample_standalone/src/main.cpp | 10 ++++++- 25 files changed, 212 insertions(+), 66 deletions(-) diff --git a/ecal/service/ecal_service/include/ecal/service/client_manager.h b/ecal/service/ecal_service/include/ecal/service/client_manager.h index 0c09ae9014..481003bd57 100644 --- a/ecal/service/ecal_service/include/ecal/service/client_manager.h +++ b/ecal/service/ecal_service/include/ecal/service/client_manager.h @@ -28,7 +28,11 @@ #include #include -#include +#include + +#include + +#include // IWYU pragma: export namespace eCAL { diff --git a/ecal/service/ecal_service/include/ecal/service/client_session.h b/ecal/service/ecal_service/include/ecal/service/client_session.h index c157baf23a..adfa4adceb 100644 --- a/ecal/service/ecal_service/include/ecal/service/client_session.h +++ b/ecal/service/ecal_service/include/ecal/service/client_session.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include #ifdef _MSC_VER #pragma warning(push) @@ -34,6 +36,7 @@ #endif #include +#include #include #include diff --git a/ecal/service/ecal_service/include/ecal/service/server_manager.h b/ecal/service/ecal_service/include/ecal/service/server_manager.h index fa93457a46..f54c4bb7fa 100644 --- a/ecal/service/ecal_service/include/ecal/service/server_manager.h +++ b/ecal/service/ecal_service/include/ecal/service/server_manager.h @@ -23,10 +23,14 @@ #include #include #include - -#include #include +#include + +#include + +#include // IWYU pragma: export + namespace eCAL { namespace service diff --git a/ecal/service/ecal_service/src/client_manager.cpp b/ecal/service/ecal_service/src/client_manager.cpp index eddde048c1..57431782a1 100644 --- a/ecal/service/ecal_service/src/client_manager.cpp +++ b/ecal/service/ecal_service/src/client_manager.cpp @@ -17,14 +17,22 @@ * ========================= eCAL LICENSE ================================= */ +#include + #include #include -#include #include #include #include #include +#include + +#include +#include +#include +#include + namespace eCAL { namespace service diff --git a/ecal/service/ecal_service/src/client_session.cpp b/ecal/service/ecal_service/src/client_session.cpp index 535895e678..e0b940895a 100644 --- a/ecal/service/ecal_service/src/client_session.cpp +++ b/ecal/service/ecal_service/src/client_session.cpp @@ -17,11 +17,21 @@ * ========================= eCAL LICENSE ================================= */ -#include +#include #include + +#include #include #include #include +#include +#include + +#include + +#include +#include +#include #include "client_session_impl_v1.h" #include "client_session_impl_v0.h" diff --git a/ecal/service/ecal_service/src/client_session_impl_base.h b/ecal/service/ecal_service/src/client_session_impl_base.h index df87e1eedb..9b4cf50da9 100644 --- a/ecal/service/ecal_service/src/client_session_impl_base.h +++ b/ecal/service/ecal_service/src/client_session_impl_base.h @@ -32,7 +32,6 @@ #pragma warning(pop) #endif -#include #include #include diff --git a/ecal/service/ecal_service/src/client_session_impl_v0.cpp b/ecal/service/ecal_service/src/client_session_impl_v0.cpp index cb51837167..f03ff20bb9 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v0.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v0.cpp @@ -18,20 +18,28 @@ */ #include "client_session_impl_v0.h" +#include "client_session_impl_base.h" #include "protocol_v0.h" +#include "protocol_layout.h" #include "log_helpers.h" #include "log_defs.h" +#include +#include +#include +#include + #include #include -#include #include #include #include #include #include +#include + namespace eCAL { namespace service @@ -191,7 +199,7 @@ namespace eCAL asio::error_code socket_option_ec; { const std::lock_guard socket_lock(me->socket_mutex_); - me->socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); + me->socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); // NOLINT(bugprone-unused-return-value) -> We already get the value from the ec parameter } if (socket_option_ec) { @@ -201,7 +209,7 @@ namespace eCAL { // Set the chosen endpoint - std::lock_guard chosen_endpoint_lock(me->chosen_endpoint_mutex_); + const std::lock_guard chosen_endpoint_lock(me->chosen_endpoint_mutex_); me->chosen_endpoint_ = me->server_list_[server_list_index]; } @@ -405,13 +413,13 @@ namespace eCAL std::string ClientSessionV0::get_address() const { - std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); + const std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); return chosen_endpoint_.first; } std::uint16_t ClientSessionV0::get_port() const { - std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); + const std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); return chosen_endpoint_.second; } @@ -531,7 +539,7 @@ namespace eCAL { // Set the stopped_by_user_ flag to true, so that the async operations stop enqueuing new service calls. - std::lock_guard service_state_lock(service_state_mutex_); + const std::lock_guard service_state_lock(service_state_mutex_); stopped_by_user_ = true; } @@ -551,12 +559,12 @@ namespace eCAL { { asio::error_code ec; - socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); // NOLINT(bugprone-unused-return-value) -> we already get the value by the ec parameter } { asio::error_code ec; - socket_.close(ec); + socket_.close(ec); // NOLINT(bugprone-unused-return-value) -> we already get the value by the ec parameter } } } diff --git a/ecal/service/ecal_service/src/client_session_impl_v0.h b/ecal/service/ecal_service/src/client_session_impl_v0.h index a6309da8a3..321893d0e3 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v0.h +++ b/ecal/service/ecal_service/src/client_session_impl_v0.h @@ -21,8 +21,7 @@ #include "client_session_impl_base.h" -#include - +#include #include #include #include @@ -31,6 +30,11 @@ #include #include +#include + +#include +#include + namespace eCAL { namespace service diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.cpp b/ecal/service/ecal_service/src/client_session_impl_v1.cpp index f60b51c186..c7985373bc 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v1.cpp @@ -18,20 +18,34 @@ */ #include "client_session_impl_v1.h" +#include "client_session_impl_base.h" #include "protocol_v1.h" +#include "protocol_layout.h" #include "log_helpers.h" #include "log_defs.h" #include #include -#include #include #include #include #include #include +#include + +#include +#include +#include +#include + +#ifdef WIN32 + #include +#else + #include +#endif + namespace eCAL { namespace service @@ -195,7 +209,7 @@ namespace eCAL asio::error_code socket_option_ec; { const std::lock_guard socket_lock(me->socket_mutex_); - me->socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); + me->socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); // NOLINT(bugprone-unused-return-value) -> We already get the value from the ec parameter } if (socket_option_ec) { @@ -204,7 +218,7 @@ namespace eCAL } { - std::lock_guard chosen_endpoint_lock(me->chosen_endpoint_mutex_); + const std::lock_guard chosen_endpoint_lock(me->chosen_endpoint_mutex_); me->chosen_endpoint_ = me->server_list_[server_list_index]; } @@ -526,13 +540,13 @@ namespace eCAL std::string ClientSessionV1::get_address() const { - std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); + const std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); return chosen_endpoint_.first; } std::uint16_t ClientSessionV1::get_port() const { - std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); + const std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); return chosen_endpoint_.second; } @@ -652,7 +666,7 @@ namespace eCAL { // Set the stopped_by_user_ flag to true, so that the async operations stop enqueuing new service calls. - std::lock_guard service_state_lock(service_state_mutex_); + const std::lock_guard service_state_lock(service_state_mutex_); stopped_by_user_ = true; } @@ -672,12 +686,12 @@ namespace eCAL { { asio::error_code ec; - socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); // NOLINT(bugprone-unused-return-value) -> we already get the return value from the ec parameter } { asio::error_code ec; - socket_.close(ec); + socket_.close(ec); // NOLINT(bugprone-unused-return-value) -> we already get the return value from the ec parameter } } } diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.h b/ecal/service/ecal_service/src/client_session_impl_v1.h index 412b33c778..d0c5572a6a 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.h +++ b/ecal/service/ecal_service/src/client_session_impl_v1.h @@ -22,6 +22,7 @@ #include "client_session_impl_base.h" #include +#include #include #include #include @@ -30,7 +31,10 @@ #include #include +#include + #include +#include namespace eCAL { diff --git a/ecal/service/ecal_service/src/log_defs.h b/ecal/service/ecal_service/src/log_defs.h index 1ca102e8c0..1b48d85905 100644 --- a/ecal/service/ecal_service/src/log_defs.h +++ b/ecal/service/ecal_service/src/log_defs.h @@ -20,8 +20,6 @@ #pragma once -#include - ///////////////////////////////////////////// // Enable / disable debug logging ///////////////////////////////////////////// diff --git a/ecal/service/ecal_service/src/protocol_v0.cpp b/ecal/service/ecal_service/src/protocol_v0.cpp index 54d231b4a4..c66d297e73 100644 --- a/ecal/service/ecal_service/src/protocol_v0.cpp +++ b/ecal/service/ecal_service/src/protocol_v0.cpp @@ -26,6 +26,15 @@ #include #include +#include +#include + +#ifdef WIN32 + #include +#else + #include +#endif + namespace eCAL { namespace service diff --git a/ecal/service/ecal_service/src/protocol_v1.cpp b/ecal/service/ecal_service/src/protocol_v1.cpp index 3ce1395957..7be282cbe7 100644 --- a/ecal/service/ecal_service/src/protocol_v1.cpp +++ b/ecal/service/ecal_service/src/protocol_v1.cpp @@ -27,6 +27,14 @@ #include #include +#include + +#ifdef WIN32 + #include +#else + #include +#endif + namespace eCAL { namespace service diff --git a/ecal/service/ecal_service/src/server.cpp b/ecal/service/ecal_service/src/server.cpp index c5c4002f15..7e6c726d3c 100644 --- a/ecal/service/ecal_service/src/server.cpp +++ b/ecal/service/ecal_service/src/server.cpp @@ -17,12 +17,15 @@ * ========================= eCAL LICENSE ================================= */ -#include #include -#include +#include #include +#include + +#include + #include "server_impl.h" namespace eCAL diff --git a/ecal/service/ecal_service/src/server_impl.cpp b/ecal/service/ecal_service/src/server_impl.cpp index 28da90c43e..f1f077baee 100644 --- a/ecal/service/ecal_service/src/server_impl.cpp +++ b/ecal/service/ecal_service/src/server_impl.cpp @@ -24,9 +24,16 @@ #include #include +#include + +#include "server_session_impl_base.h" #include "server_session_impl_v1.h" #include "server_session_impl_v0.h" +#include +#include +#include + #include "log_defs.h" namespace eCAL @@ -137,7 +144,7 @@ namespace eCAL { const std::lock_guard acceptor_lock(acceptor_mutex_); - acceptor_.open(endpoint.protocol(), ec); + acceptor_.open(endpoint.protocol(), ec); // NOLINT(bugprone-unused-return-value) -> We already get the return value rom the ec parameter } if (ec) { @@ -152,7 +159,7 @@ namespace eCAL { asio::error_code ec; { - acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true), ec); + acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true), ec); // NOLINT(bugprone-unused-return-value) -> We already get the value from the ec parameter const std::lock_guard acceptor_lock(acceptor_mutex_); } if (ec) @@ -168,7 +175,7 @@ namespace eCAL asio::error_code ec; { const std::lock_guard acceptor_lock(acceptor_mutex_); - acceptor_.bind(endpoint, ec); + acceptor_.bind(endpoint, ec); // NOLINT(bugprone-unused-return-value) -> We already get the return value rom the ec parameter } if (ec) { @@ -183,7 +190,7 @@ namespace eCAL asio::error_code ec; { const std::lock_guard acceptor_lock(acceptor_mutex_); - acceptor_.listen(asio::socket_base::max_listen_connections, ec); + acceptor_.listen(asio::socket_base::max_listen_connections, ec); // NOLINT(bugprone-unused-return-value) -> We already get the return value rom the ec parameter } if (ec) { @@ -296,7 +303,7 @@ namespace eCAL if (acceptor_.is_open()) { asio::error_code ec; - acceptor_.close(ec); + acceptor_.close(ec); // NOLINT(bugprone-unused-return-value) -> We already get the return value rom the ec parameter } } diff --git a/ecal/service/ecal_service/src/server_impl.h b/ecal/service/ecal_service/src/server_impl.h index 9b33edd76d..6ef2355d63 100644 --- a/ecal/service/ecal_service/src/server_impl.h +++ b/ecal/service/ecal_service/src/server_impl.h @@ -19,9 +19,7 @@ #pragma once -#include #include -#include #include #include #include @@ -32,7 +30,6 @@ #pragma warning(disable: 4834) #endif #include -#include #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/ecal/service/ecal_service/src/server_manager.cpp b/ecal/service/ecal_service/src/server_manager.cpp index 0b462412c9..2fad646d96 100644 --- a/ecal/service/ecal_service/src/server_manager.cpp +++ b/ecal/service/ecal_service/src/server_manager.cpp @@ -17,13 +17,19 @@ * ========================= eCAL LICENSE ================================= */ +#include + #include #include -#include #include #include #include +#include + +#include +#include + namespace eCAL { namespace service diff --git a/ecal/service/ecal_service/src/server_session_impl_base.h b/ecal/service/ecal_service/src/server_session_impl_base.h index 1d84acf6c2..ff663e28cf 100644 --- a/ecal/service/ecal_service/src/server_session_impl_base.h +++ b/ecal/service/ecal_service/src/server_session_impl_base.h @@ -23,18 +23,16 @@ #include #include -#include #ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable: 4834) +#pragma warning(push) +#pragma warning(disable : 4834) #endif #include -#include #ifdef _MSC_VER - #pragma warning(pop) +#pragma warning(pop) #endif #include diff --git a/ecal/service/ecal_service/src/server_session_impl_v0.cpp b/ecal/service/ecal_service/src/server_session_impl_v0.cpp index d06dfe050c..a96f6ec8ac 100644 --- a/ecal/service/ecal_service/src/server_session_impl_v0.cpp +++ b/ecal/service/ecal_service/src/server_session_impl_v0.cpp @@ -18,11 +18,12 @@ */ #include "server_session_impl_v0.h" +#include "server_session_impl_base.h" #include "log_helpers.h" #include "log_defs.h" - #include "protocol_layout.h" + #include #include #include @@ -30,6 +31,18 @@ #include #include +#include + +#include +#include +#include + +#ifdef WIN32 + #include +#else + #include +#endif + /////////////////////////////////////////////// // Create, Constructor, Destructor /////////////////////////////////////////////// @@ -104,7 +117,7 @@ namespace eCAL asio::error_code socket_option_ec; { const std::lock_guard socket_lock(socket_mutex_); - socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); + socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); // NOLINT(bugprone-unused-return-value) -> We already get the return value rom the ec parameter } if (socket_option_ec) { @@ -131,13 +144,13 @@ namespace eCAL { // Shutdown the socket asio::error_code ec; - socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); // NOLINT(bugprone-unused-return-value) -> We already get the return value rom the ec parameter } { // Close the socket asio::error_code ec; - socket_.close(ec); + socket_.close(ec); // NOLINT(bugprone-unused-return-value) -> We already get the return value rom the ec parameter } } } diff --git a/ecal/service/ecal_service/src/server_session_impl_v0.h b/ecal/service/ecal_service/src/server_session_impl_v0.h index 4c4f93dbe8..af3c33ea54 100644 --- a/ecal/service/ecal_service/src/server_session_impl_v0.h +++ b/ecal/service/ecal_service/src/server_session_impl_v0.h @@ -22,12 +22,14 @@ #include "server_session_impl_base.h" #include #include +#include +#include + +#include + #include #include - #include -#include -#include namespace eCAL { diff --git a/ecal/service/ecal_service/src/server_session_impl_v1.cpp b/ecal/service/ecal_service/src/server_session_impl_v1.cpp index 3633a2445c..568e524aef 100644 --- a/ecal/service/ecal_service/src/server_session_impl_v1.cpp +++ b/ecal/service/ecal_service/src/server_session_impl_v1.cpp @@ -18,11 +18,13 @@ */ #include "server_session_impl_v1.h" +#include "server_session_impl_base.h" #include "protocol_v1.h" - +#include "protocol_layout.h" #include "log_defs.h" #include "log_helpers.h" + #include #include #include @@ -30,6 +32,18 @@ #include #include +#include + +#include +#include +#include + +#ifdef WIN32 + #include +#else + #include +#endif + /////////////////////////////////////////////// // Create, Constructor, Destructor /////////////////////////////////////////////// @@ -88,7 +102,7 @@ namespace eCAL asio::error_code socket_option_ec; { const std::lock_guard socket_lock(socket_mutex_); - socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); + socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); // NOLINT(bugprone-unused-return-value) -> We already get the value from the ec parameter } if (socket_option_ec) { @@ -108,12 +122,12 @@ namespace eCAL { // Shutdown the socket asio::error_code ec; - socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); // NOLINT(bugprone-unused-return-value) -> We already get the value from the ec parameter } { // Close the socket asio::error_code ec; - socket_.close(ec); + socket_.close(ec); // NOLINT(bugprone-unused-return-value) -> We already get the value from the ec parameter } } } diff --git a/ecal/service/ecal_service/src/server_session_impl_v1.h b/ecal/service/ecal_service/src/server_session_impl_v1.h index d61e2ecc1e..4a233270bb 100644 --- a/ecal/service/ecal_service/src/server_session_impl_v1.h +++ b/ecal/service/ecal_service/src/server_session_impl_v1.h @@ -20,14 +20,17 @@ #pragma once #include "server_session_impl_base.h" + #include #include +#include +#include + +#include + #include #include - #include -#include -#include namespace eCAL { diff --git a/ecal/service/samples/sample_client/src/main.cpp b/ecal/service/samples/sample_client/src/main.cpp index 89f47ebddd..403f981104 100644 --- a/ecal/service/samples/sample_client/src/main.cpp +++ b/ecal/service/samples/sample_client/src/main.cpp @@ -17,11 +17,24 @@ * ========================= eCAL LICENSE ================================= */ -#include - +#include +#include +#include +#include #include +#include +#include +#include #include -#include +#include +#include + +#include + +#include +#include +#include +#include void print_usage(const std::string& arg0) { @@ -44,9 +57,10 @@ int main(int argc, char** argv) { // Convert command line arguments into vector std::vector args; + args.reserve(argc); for (int i = 0; i < argc; ++i) { - args.push_back(argv[i]); + args.emplace_back(argv[i]); } std::vector> server_list; diff --git a/ecal/service/samples/sample_server/src/main.cpp b/ecal/service/samples/sample_server/src/main.cpp index 09eccf8e0e..d1a7921d6b 100644 --- a/ecal/service/samples/sample_server/src/main.cpp +++ b/ecal/service/samples/sample_server/src/main.cpp @@ -17,11 +17,18 @@ * ========================= eCAL LICENSE ================================= */ -#include - +#include +#include #include -#include -#include +#include +#include +#include + +#include + +#include +#include +#include void print_usage(const std::string& arg0) { @@ -35,9 +42,10 @@ int main(int argc, char** argv) // convert command line arguments into vector std::vector args; + args.reserve(argc); for (int i = 0; i < argc; ++i) { - args.push_back(argv[i]); + args.emplace_back(argv[i]); } // parse command line arguments diff --git a/ecal/service/samples/sample_standalone/src/main.cpp b/ecal/service/samples/sample_standalone/src/main.cpp index 751b20a331..482aaf4bce 100644 --- a/ecal/service/samples/sample_standalone/src/main.cpp +++ b/ecal/service/samples/sample_standalone/src/main.cpp @@ -20,9 +20,17 @@ #include #include +#include +#include +#include + +#include #include +#include +#include #include -#include + +#include int main(int /*argc*/, char** /*argv*/) { From 2bed66b5bfc8e0ec3d551ee687bd8c825479c0d4 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Tue, 7 May 2024 12:55:41 +0200 Subject: [PATCH 10/12] Added get_remote_endpoint --- .../include/ecal/service/client_session.h | 5 ++++- ecal/service/ecal_service/src/client_session.cpp | 13 +++++++------ .../ecal_service/src/client_session_impl_base.h | 5 +++-- .../ecal_service/src/client_session_impl_v0.cpp | 15 ++++++++++++++- .../ecal_service/src/client_session_impl_v0.h | 6 ++++-- .../ecal_service/src/client_session_impl_v1.cpp | 15 ++++++++++++++- .../ecal_service/src/client_session_impl_v1.h | 6 ++++-- ecal/service/test/src/ecal_tcp_service_test.cpp | 11 ++++++++--- 8 files changed, 58 insertions(+), 18 deletions(-) diff --git a/ecal/service/ecal_service/include/ecal/service/client_session.h b/ecal/service/ecal_service/include/ecal/service/client_session.h index adfa4adceb..b75b83bf45 100644 --- a/ecal/service/ecal_service/include/ecal/service/client_session.h +++ b/ecal/service/ecal_service/include/ecal/service/client_session.h @@ -235,7 +235,7 @@ namespace eCAL * * @return The address that this client session has been created with. */ - std::string get_address() const; + std::string get_host() const; /**TODO: Revise documentation * @brief Get the port that this client session has been created with. @@ -248,6 +248,9 @@ namespace eCAL */ std::uint16_t get_port() const; + // TODO: Document + asio::ip::tcp::endpoint get_remote_endpoint() const; + /** * @brief Get the state of this client session. * diff --git a/ecal/service/ecal_service/src/client_session.cpp b/ecal/service/ecal_service/src/client_session.cpp index e0b940895a..9a84ddbc50 100644 --- a/ecal/service/ecal_service/src/client_session.cpp +++ b/ecal/service/ecal_service/src/client_session.cpp @@ -146,11 +146,12 @@ namespace eCAL } } - State ClientSession::get_state() const { return impl_->get_state(); } - std::uint8_t ClientSession::get_accepted_protocol_version() const { return impl_->get_accepted_protocol_version(); } - int ClientSession::get_queue_size() const { return impl_->get_queue_size(); } - std::string ClientSession::get_address() const { return impl_->get_address(); } - std::uint16_t ClientSession::get_port() const { return impl_->get_port(); } - void ClientSession::stop() { impl_->stop(); } + State ClientSession::get_state() const { return impl_->get_state(); } + std::uint8_t ClientSession::get_accepted_protocol_version() const { return impl_->get_accepted_protocol_version(); } + int ClientSession::get_queue_size() const { return impl_->get_queue_size(); } + std::string ClientSession::get_host() const { return impl_->get_host(); } + asio::ip::tcp::endpoint ClientSession::get_remote_endpoint() const { return impl_->get_remote_endpoint(); } + std::uint16_t ClientSession::get_port() const { return impl_->get_port(); } + void ClientSession::stop() { impl_->stop(); } } // namespace service } // namespace eCAL diff --git a/ecal/service/ecal_service/src/client_session_impl_base.h b/ecal/service/ecal_service/src/client_session_impl_base.h index 9b4cf50da9..535efe7587 100644 --- a/ecal/service/ecal_service/src/client_session_impl_base.h +++ b/ecal/service/ecal_service/src/client_session_impl_base.h @@ -73,8 +73,9 @@ namespace eCAL public: virtual bool async_call_service(const std::shared_ptr& request, const ResponseCallbackT& response_callback) = 0; - virtual std::string get_address() const = 0; - virtual std::uint16_t get_port() const = 0; + virtual std::string get_host() const = 0; + virtual std::uint16_t get_port() const = 0; + virtual asio::ip::tcp::endpoint get_remote_endpoint() const = 0; // TODO: Document virtual State get_state() const = 0; virtual std::uint8_t get_accepted_protocol_version() const = 0; diff --git a/ecal/service/ecal_service/src/client_session_impl_v0.cpp b/ecal/service/ecal_service/src/client_session_impl_v0.cpp index f03ff20bb9..bf6c96572f 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v0.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v0.cpp @@ -411,7 +411,7 @@ namespace eCAL // Status API ////////////////////////////////////// - std::string ClientSessionV0::get_address() const + std::string ClientSessionV0::get_host() const { const std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); return chosen_endpoint_.first; @@ -423,6 +423,19 @@ namespace eCAL return chosen_endpoint_.second; } + asio::ip::tcp::endpoint ClientSessionV0::get_remote_endpoint() const + { + // form remote endpoint string + { + asio::error_code ec; + const auto endpoint = socket_.remote_endpoint(ec); + if (!ec) + return endpoint; + else + return asio::ip::tcp::endpoint(); + } + } + State ClientSessionV0::get_state() const { const std::lock_guard lock(service_state_mutex_); diff --git a/ecal/service/ecal_service/src/client_session_impl_v0.h b/ecal/service/ecal_service/src/client_session_impl_v0.h index 321893d0e3..28fa738158 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v0.h +++ b/ecal/service/ecal_service/src/client_session_impl_v0.h @@ -99,8 +99,10 @@ namespace eCAL // Status API ////////////////////////////////////// public: - std::string get_address() const override; - std::uint16_t get_port() const override; + std::string get_host() const override; + std::uint16_t get_port() const override; + asio::ip::tcp::endpoint get_remote_endpoint() const override; // TODO: Document + State get_state() const override; std::uint8_t get_accepted_protocol_version() const override; int get_queue_size() const override; diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.cpp b/ecal/service/ecal_service/src/client_session_impl_v1.cpp index c7985373bc..558079bebb 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v1.cpp @@ -538,7 +538,7 @@ namespace eCAL // Status API ////////////////////////////////////// - std::string ClientSessionV1::get_address() const + std::string ClientSessionV1::get_host() const { const std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); return chosen_endpoint_.first; @@ -549,6 +549,19 @@ namespace eCAL const std::lock_guard chosen_endpoint_lock(chosen_endpoint_mutex_); return chosen_endpoint_.second; } + + asio::ip::tcp::endpoint ClientSessionV1::get_remote_endpoint() const + { + // form remote endpoint string + { + asio::error_code ec; + const auto endpoint = socket_.remote_endpoint(ec); + if (!ec) + return endpoint; + else + return asio::ip::tcp::endpoint(); + } + } State ClientSessionV1::get_state() const { diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.h b/ecal/service/ecal_service/src/client_session_impl_v1.h index d0c5572a6a..d7bf18dd8f 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.h +++ b/ecal/service/ecal_service/src/client_session_impl_v1.h @@ -103,8 +103,10 @@ namespace eCAL // Status API ////////////////////////////////////// public: - std::string get_address() const override; - std::uint16_t get_port() const override; + std::string get_host() const override; + std::uint16_t get_port() const override; + asio::ip::tcp::endpoint get_remote_endpoint() const override; // TODO: Document + State get_state() const override; std::uint8_t get_accepted_protocol_version() const override; int get_queue_size() const override; diff --git a/ecal/service/test/src/ecal_tcp_service_test.cpp b/ecal/service/test/src/ecal_tcp_service_test.cpp index a44f4ae963..88f0e67a3f 100644 --- a/ecal/service/test/src/ecal_tcp_service_test.cpp +++ b/ecal/service/test/src/ecal_tcp_service_test.cpp @@ -1670,8 +1670,9 @@ TEST(ecal_service, Callback_ApiCallsFromCallbacks) // NOLINT if(client) { // We just test if those functions can be called without crashing - auto address = client->get_address(); + auto address = client->get_host(); auto port = client->get_port(); + auto endpoint = client->get_remote_endpoint(); auto protocol_version = client->get_accepted_protocol_version(); auto queue_size = client->get_queue_size(); auto state = client->get_state(); @@ -1687,8 +1688,9 @@ TEST(ecal_service, Callback_ApiCallsFromCallbacks) // NOLINT if (client) { // We just test if those functions can be called without crashing - auto address = client->get_address(); + auto address = client->get_host(); auto port = client->get_port(); + auto endpoint = client->get_remote_endpoint(); auto protocol_version = client->get_accepted_protocol_version(); auto queue_size = client->get_queue_size(); auto state = client->get_state(); @@ -1813,10 +1815,13 @@ TEST(ecal_service, BackupHost) EXPECT_EQ(num_client_event_callback_called, 1); // Check what host the client is connected to - auto connected_host = client->get_address(); + auto connected_host = client->get_host(); auto connected_port = client->get_port(); + auto endpoint = client->get_remote_endpoint(); EXPECT_EQ(connected_host, "127.0.0.1"); EXPECT_EQ(connected_port, server->get_port()); + EXPECT_EQ(endpoint.port(), server->get_port()); + EXPECT_EQ(endpoint.address().to_string(), "127.0.0.1"); // Call service and wait for the response const eCAL::service::ClientSession::ResponseCallbackT response_callback From 8ebe6c823bc63984b7089798969925b3be57ba20 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Tue, 7 May 2024 13:44:56 +0200 Subject: [PATCH 11/12] Updated documentation and solved TODOs --- .../include/ecal/service/client_manager.h | 3 +- .../include/ecal/service/client_session.h | 41 +++++++++++-------- .../src/client_session_impl_base.h | 2 +- .../src/client_session_impl_v0.cpp | 9 +++- .../ecal_service/src/client_session_impl_v0.h | 2 +- .../src/client_session_impl_v1.cpp | 7 ++++ .../ecal_service/src/client_session_impl_v1.h | 2 +- .../test/src/ecal_tcp_service_test.cpp | 24 ++++++++++- 8 files changed, 66 insertions(+), 24 deletions(-) diff --git a/ecal/service/ecal_service/include/ecal/service/client_manager.h b/ecal/service/ecal_service/include/ecal/service/client_manager.h index 481003bd57..0bb903c947 100644 --- a/ecal/service/ecal_service/include/ecal/service/client_manager.h +++ b/ecal/service/ecal_service/include/ecal/service/client_manager.h @@ -147,8 +147,7 @@ namespace eCAL * stopped from this central place. * * @param protocol_version The protocol version to use for the client session. If 0, the legacy buggy protocol will be used. - * @param address The address of the server to connect to TODO: Update documentation - * @param port The port of the server to connect to + * @param server_list A list of endpoints to connect to. Must not be empty. The endpoints will be tried in the given order until a working endpoint is found. * @param event_callback The callback, that will be called, when the client has connected to the server or disconnected from it. The callback will be executed in the io_context thread. * * @return A shared_ptr to the newly created ClientSession instance diff --git a/ecal/service/ecal_service/include/ecal/service/client_session.h b/ecal/service/ecal_service/include/ecal/service/client_session.h index b75b83bf45..6b9b7f52de 100644 --- a/ecal/service/ecal_service/include/ecal/service/client_session.h +++ b/ecal/service/ecal_service/include/ecal/service/client_session.h @@ -136,8 +136,7 @@ namespace eCAL * * @param io_context The io_context to use for the session and all callbacks. * @param protocol_version The protocol version to use for the session. When this is 0, the legacy buggy protocol is used. - * @param address The address of the server to connect to. May be an IP or a Hostname, IPv6 is supported. TODO: Update documentation - * @param port The port of the server to connect to. + * @param server_list A list of endpoints to connect to. Must not be empty. The endpoints will be tried in the given order until a working endpoint is found. * @param event_callback The callback to be called when the session's state changes, i.e. when the session successfully connected to a server or disconnected from it. * @param logger The logger to use for logging. * @param delete_callback The callback to be called when the session is deleted. This is useful for the eCAL::service::ClientManager to keep track of the number of active sessions. @@ -225,30 +224,40 @@ namespace eCAL */ eCAL::service::Error call_service(const std::shared_ptr& request, std::shared_ptr& response); - /** TODO: Revise documentation - * @brief Get the address that this client session has been created with. + /** + * @brief Get the host that this client is connected to. + * + * Get the host that this client is connected to. + * If the client is not connected, this function will return an empty + * string. Otherwise, it will return the hostname from the list + * server_list that the client is connected to. * - * This function returns the address that this client session has been - * created with. It will not return the address of the server that this - * client session is connected to, which would actually be the same - * address, but probably resolved to an IP. + * The host is not resolved to an IP address. Use get_remote_endpoint() + * to get the actual IP address. * - * @return The address that this client session has been created with. + * @return The host that this client is connected to. */ std::string get_host() const; - /**TODO: Revise documentation - * @brief Get the port that this client session has been created with. + /** + * @brief Get the port that this client session is connected to. * - * This function returns the port that this client session has been - * created with. It is not said, that the connection has been established - * successfully. + * Get the port that this client session is connected to. If the client + * is not connected, this function will return 0. Otherwise, it will + * return the port from the list server_list that the client is connected + * to. * - * @return The port that this client session has been created with. + * @return The port that this client is connected to */ std::uint16_t get_port() const; - // TODO: Document + /** + * @brief Get the remote endpoint that this client session is connected to. + * + * Get the remote endpoint that this client session is connected to. Only + * valid, if the client session is actually connected to a server. If a + * hostname was given, this function will return the resolved IP address. + */ asio::ip::tcp::endpoint get_remote_endpoint() const; /** diff --git a/ecal/service/ecal_service/src/client_session_impl_base.h b/ecal/service/ecal_service/src/client_session_impl_base.h index 535efe7587..6db29e96c1 100644 --- a/ecal/service/ecal_service/src/client_session_impl_base.h +++ b/ecal/service/ecal_service/src/client_session_impl_base.h @@ -75,7 +75,7 @@ namespace eCAL virtual std::string get_host() const = 0; virtual std::uint16_t get_port() const = 0; - virtual asio::ip::tcp::endpoint get_remote_endpoint() const = 0; // TODO: Document + virtual asio::ip::tcp::endpoint get_remote_endpoint() const = 0; virtual State get_state() const = 0; virtual std::uint8_t get_accepted_protocol_version() const = 0; diff --git a/ecal/service/ecal_service/src/client_session_impl_v0.cpp b/ecal/service/ecal_service/src/client_session_impl_v0.cpp index bf6c96572f..10e98ce998 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v0.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v0.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -54,7 +55,13 @@ namespace eCAL { std::shared_ptr instance(new ClientSessionV0(io_context, server_list, event_callback, logger)); - instance->resolve_endpoint(0); // TODO: Write a test that checks what happens when the server_list is empty + // Throw exception, if the server list is empty + if (server_list.empty()) + { + throw std::invalid_argument("Server list must not be empty"); + } + + instance->resolve_endpoint(0); return instance; } diff --git a/ecal/service/ecal_service/src/client_session_impl_v0.h b/ecal/service/ecal_service/src/client_session_impl_v0.h index 28fa738158..326ad1b648 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v0.h +++ b/ecal/service/ecal_service/src/client_session_impl_v0.h @@ -101,7 +101,7 @@ namespace eCAL public: std::string get_host() const override; std::uint16_t get_port() const override; - asio::ip::tcp::endpoint get_remote_endpoint() const override; // TODO: Document + asio::ip::tcp::endpoint get_remote_endpoint() const override; State get_state() const override; std::uint8_t get_accepted_protocol_version() const override; diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.cpp b/ecal/service/ecal_service/src/client_session_impl_v1.cpp index 558079bebb..f6b44ae3b9 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v1.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,12 @@ namespace eCAL { std::shared_ptr instance(new ClientSessionV1(io_context, server_list, event_callback, logger)); + // Throw exception, if the server list is empty + if (server_list.empty()) + { + throw std::invalid_argument("Server list must not be empty"); + } + instance->resolve_endpoint(0); return instance; diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.h b/ecal/service/ecal_service/src/client_session_impl_v1.h index d7bf18dd8f..c95d360f1e 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.h +++ b/ecal/service/ecal_service/src/client_session_impl_v1.h @@ -105,7 +105,7 @@ namespace eCAL public: std::string get_host() const override; std::uint16_t get_port() const override; - asio::ip::tcp::endpoint get_remote_endpoint() const override; // TODO: Document + asio::ip::tcp::endpoint get_remote_endpoint() const override; State get_state() const override; std::uint8_t get_accepted_protocol_version() const override; diff --git a/ecal/service/test/src/ecal_tcp_service_test.cpp b/ecal/service/test/src/ecal_tcp_service_test.cpp index 88f0e67a3f..cd5abf2fcb 100644 --- a/ecal/service/test/src/ecal_tcp_service_test.cpp +++ b/ecal/service/test/src/ecal_tcp_service_test.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include // Should not be needed, when I use the server manager / client manager #include // Should not be needed, when I use the server manager / client manager @@ -59,8 +60,6 @@ eCAL::service::LoggerT critical_logger(const std::string& node_name) constexpr std::uint8_t min_protocol_version = 0; constexpr std::uint8_t max_protocol_version = 1; -// TODO: Add test for the new multi-host feature, where the second host is tried if the connection to the first one fails - #if 1 TEST(ecal_service, RAII_TcpServiceServer) // NOLINT { @@ -1851,6 +1850,27 @@ TEST(ecal_service, BackupHost) } #endif +#if 1 +// Test that the client create throws an exception when bein created with an empty server list +TEST(ecal_service, EmptyServerList) +{ + // Regular client + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + EXPECT_THROW(eCAL::service::ClientSession::create(io_context, protocol_version, {}, [](eCAL::service::ClientEventType, const std::string&) -> void {}), std::invalid_argument); + } + + // Client manager + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + auto client_manager = eCAL::service::ClientManager::create(io_context); + EXPECT_THROW(client_manager->create_client(protocol_version, {}, [](eCAL::service::ClientEventType, const std::string&) -> void {}), std::invalid_argument); + } +} +#endif + #if 1 TEST(ecal_service, ErrorCallback_ErrorCallbackNoServer) // NOLINT { From d67a2d22fd51159e24d42911502e19b2574ce34c Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Tue, 7 May 2024 14:17:04 +0200 Subject: [PATCH 12/12] Fixed clang-tidy warnings --- ecal/service/ecal_service/src/client_session_impl_v0.cpp | 2 +- ecal/service/ecal_service/src/client_session_impl_v1.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ecal/service/ecal_service/src/client_session_impl_v0.cpp b/ecal/service/ecal_service/src/client_session_impl_v0.cpp index 10e98ce998..de123b9e3f 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v0.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v0.cpp @@ -435,7 +435,7 @@ namespace eCAL // form remote endpoint string { asio::error_code ec; - const auto endpoint = socket_.remote_endpoint(ec); + auto endpoint = socket_.remote_endpoint(ec); if (!ec) return endpoint; else diff --git a/ecal/service/ecal_service/src/client_session_impl_v1.cpp b/ecal/service/ecal_service/src/client_session_impl_v1.cpp index f6b44ae3b9..576a3f3c5b 100644 --- a/ecal/service/ecal_service/src/client_session_impl_v1.cpp +++ b/ecal/service/ecal_service/src/client_session_impl_v1.cpp @@ -562,7 +562,7 @@ namespace eCAL // form remote endpoint string { asio::error_code ec; - const auto endpoint = socket_.remote_endpoint(ec); + auto endpoint = socket_.remote_endpoint(ec); if (!ec) return endpoint; else