-
Notifications
You must be signed in to change notification settings - Fork 12
feat(transport): event-driven transport adapters (#172) #178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
f9e3ca4
758d08f
b77786e
c715a48
e539a78
8f6da44
021a480
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| /******************************************************************************** | ||
| * Copyright (c) 2025 Vinicius Tadeu Zein | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Apache License Version 2.0 which is available at | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| ********************************************************************************/ | ||
|
|
||
| #ifndef SOMEIP_TRANSPORT_EVENT_DRIVEN_TCP_TRANSPORT_H | ||
| #define SOMEIP_TRANSPORT_EVENT_DRIVEN_TCP_TRANSPORT_H | ||
|
|
||
| #include "transport/transport.h" | ||
| #include "transport/tcp_socket_adapter.h" | ||
| #include "platform/thread.h" | ||
| #include <atomic> | ||
| #include <queue> | ||
| #include <vector> | ||
|
|
||
| namespace someip { | ||
| namespace transport { | ||
|
|
||
| /** | ||
| * @brief Configuration for event-driven TCP transport (stream reassembly limits). | ||
| */ | ||
| struct EventDrivenTcpTransportConfig { | ||
| size_t max_receive_buffer{65536}; | ||
| }; | ||
|
|
||
| /** | ||
| * @brief ITransport implementation driven by an ITcpSocketAdapter. | ||
| */ | ||
| class EventDrivenTcpTransport : public ITransport { | ||
| public: | ||
| explicit EventDrivenTcpTransport(ITcpSocketAdapter& adapter, | ||
| const EventDrivenTcpTransportConfig& config = EventDrivenTcpTransportConfig()); | ||
|
|
||
| ~EventDrivenTcpTransport() override; | ||
|
|
||
| EventDrivenTcpTransport(const EventDrivenTcpTransport&) = delete; | ||
| EventDrivenTcpTransport& operator=(const EventDrivenTcpTransport&) = delete; | ||
|
|
||
| [[nodiscard]] Result initialize(const Endpoint& local_endpoint); | ||
|
|
||
| [[nodiscard]] Result enable_server_mode(int backlog = 5); | ||
|
|
||
| /** | ||
| * @brief Non-blocking accept attempt (server mode). Adapter should invoke | ||
| * connected/disconnected callbacks when the connection is ready or lost. | ||
| */ | ||
| [[nodiscard]] Result try_accept_connection(Endpoint& remote_out); | ||
|
|
||
| [[nodiscard]] Result send_message(const Message& message, const Endpoint& endpoint) override; | ||
| MessagePtr receive_message() override; | ||
| Result connect(const Endpoint& endpoint) override; | ||
| Result disconnect() override; | ||
| bool is_connected() const override; | ||
| Endpoint get_local_endpoint() const override; | ||
| void set_listener(ITransportListener* listener) override; | ||
| Result start() override; | ||
| Result stop() override; | ||
| bool is_running() const override; | ||
|
|
||
| private: | ||
| void on_adapter_receive(const std::vector<uint8_t>& data); | ||
| void on_adapter_connected(const Endpoint& remote); | ||
| void on_adapter_disconnected(); | ||
| bool parse_message_from_buffer(std::vector<uint8_t>& buffer, MessagePtr& message); | ||
|
|
||
| ITcpSocketAdapter& adapter_; | ||
| EventDrivenTcpTransportConfig config_; | ||
| Endpoint local_endpoint_; | ||
| Endpoint connection_remote_; | ||
| ITransportListener* listener_{nullptr}; | ||
|
|
||
| std::atomic<bool> running_{false}; | ||
| std::atomic<bool> initialized_{false}; | ||
| bool server_mode_{false}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🤖 Prompt for AI Agents |
||
|
|
||
| std::vector<uint8_t> receive_buffer_; | ||
| std::queue<std::pair<MessagePtr, Endpoint>> message_queue_; | ||
| platform::Mutex queue_mutex_; | ||
|
|
||
| static const size_t SOMEIP_HEADER_SIZE; | ||
| static const size_t MAX_MESSAGE_SIZE; | ||
| }; | ||
|
|
||
| } // namespace transport | ||
| } // namespace someip | ||
|
|
||
| #endif // SOMEIP_TRANSPORT_EVENT_DRIVEN_TCP_TRANSPORT_H | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| /******************************************************************************** | ||
| * Copyright (c) 2025 Vinicius Tadeu Zein | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Apache License Version 2.0 which is available at | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| ********************************************************************************/ | ||
|
|
||
| #ifndef SOMEIP_TRANSPORT_EVENT_DRIVEN_UDP_TRANSPORT_H | ||
| #define SOMEIP_TRANSPORT_EVENT_DRIVEN_UDP_TRANSPORT_H | ||
|
|
||
| #include "transport/transport.h" | ||
| #include "transport/udp_socket_adapter.h" | ||
| #include "platform/thread.h" | ||
| #include <atomic> | ||
| #include <queue> | ||
| #include <string> | ||
|
|
||
| namespace someip { | ||
| namespace transport { | ||
|
|
||
| /** | ||
| * @brief Configuration for event-driven UDP transport. | ||
| */ | ||
| struct EventDrivenUdpTransportConfig { | ||
| std::string multicast_interface{}; | ||
| size_t max_message_size{1400}; | ||
| }; | ||
|
|
||
| /** | ||
| * @brief ITransport implementation driven by an IUdpSocketAdapter. | ||
| */ | ||
| class EventDrivenUdpTransport : public ITransport { | ||
| public: | ||
| explicit EventDrivenUdpTransport(IUdpSocketAdapter& adapter, | ||
| const Endpoint& local_endpoint, | ||
| const EventDrivenUdpTransportConfig& config = EventDrivenUdpTransportConfig()); | ||
|
|
||
| ~EventDrivenUdpTransport() override; | ||
|
|
||
| EventDrivenUdpTransport(const EventDrivenUdpTransport&) = delete; | ||
| EventDrivenUdpTransport& operator=(const EventDrivenUdpTransport&) = delete; | ||
|
|
||
| [[nodiscard]] Result send_message(const Message& message, const Endpoint& endpoint) override; | ||
| MessagePtr receive_message() override; | ||
| Result connect(const Endpoint& endpoint) override; | ||
| Result disconnect() override; | ||
| bool is_connected() const override; | ||
| Endpoint get_local_endpoint() const override; | ||
| void set_listener(ITransportListener* listener) override; | ||
| Result start() override; | ||
| Result stop() override; | ||
| bool is_running() const override; | ||
|
|
||
| Result join_multicast_group(const std::string& multicast_address); | ||
| Result leave_multicast_group(const std::string& multicast_address); | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| private: | ||
| void on_adapter_receive(const std::vector<uint8_t>& data, const Endpoint& sender); | ||
| static bool is_multicast_ipv4(const std::string& address); | ||
|
|
||
| IUdpSocketAdapter& adapter_; | ||
| Endpoint local_endpoint_; | ||
| EventDrivenUdpTransportConfig config_; | ||
| std::atomic<bool> running_{false}; | ||
| std::atomic<bool> opened_{false}; | ||
| ITransportListener* listener_{nullptr}; | ||
|
|
||
| std::queue<MessagePtr> receive_queue_; | ||
| platform::Mutex queue_mutex_; | ||
|
|
||
| static constexpr size_t MAX_UDP_PAYLOAD = 65507; | ||
| }; | ||
|
|
||
| } // namespace transport | ||
| } // namespace someip | ||
|
|
||
| #endif // SOMEIP_TRANSPORT_EVENT_DRIVEN_UDP_TRANSPORT_H | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| /******************************************************************************** | ||
| * Copyright (c) 2025 Vinicius Tadeu Zein | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Apache License Version 2.0 which is available at | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| ********************************************************************************/ | ||
|
|
||
| #ifndef SOMEIP_TRANSPORT_TCP_SOCKET_ADAPTER_H | ||
| #define SOMEIP_TRANSPORT_TCP_SOCKET_ADAPTER_H | ||
|
|
||
| #include "transport/endpoint.h" | ||
| #include "common/result.h" | ||
| #include <functional> | ||
| #include <vector> | ||
|
|
||
| namespace someip { | ||
| namespace transport { | ||
|
|
||
| /** | ||
| * @brief Invoked when payload bytes arrive on the established connection. | ||
| */ | ||
| using TcpReceiveCallback = std::function<void(const std::vector<uint8_t>& data)>; | ||
|
|
||
| /** | ||
| * @brief Invoked when the connection is ready (outgoing connect or accepted peer). | ||
| */ | ||
| using TcpConnectedCallback = std::function<void(const Endpoint& remote)>; | ||
|
|
||
| /** | ||
| * @brief Invoked when the connection is closed or reset. | ||
| */ | ||
| using TcpDisconnectedCallback = std::function<void()>; | ||
|
|
||
| /** | ||
| * @brief TCP socket abstraction for event-driven SOME/IP transport. | ||
| * | ||
| * Implemented by integrators using non-BSD stacks. The adapter owns the | ||
| * semantics of connect/accept; it must invoke callbacks from its event context. | ||
| */ | ||
| class ITcpSocketAdapter { | ||
| public: | ||
| virtual ~ITcpSocketAdapter() = default; | ||
|
|
||
| /** | ||
| * @brief Create socket bound to the local endpoint (listening or pre-connect). | ||
| */ | ||
| [[nodiscard]] virtual Result open(const Endpoint& local_endpoint) = 0; | ||
|
|
||
| virtual void close() = 0; | ||
|
|
||
| /** | ||
| * @brief Start listening after open (server mode). No-op or NOT_IMPLEMENTED for client-only adapters. | ||
| */ | ||
| [[nodiscard]] virtual Result listen(int backlog) = 0; | ||
|
|
||
| /** | ||
| * @brief Connect to a remote endpoint (client mode). | ||
| */ | ||
| [[nodiscard]] virtual Result connect(const Endpoint& remote_endpoint) = 0; | ||
|
|
||
| /** | ||
| * @brief Accept one pending connection if available (non-blocking from SOME/IP's perspective). | ||
| * @param remote_out Peer endpoint when Result::SUCCESS | ||
| */ | ||
| [[nodiscard]] virtual Result accept(Endpoint& remote_out) = 0; | ||
|
|
||
| /** | ||
| * @brief Send bytes on the active connection. | ||
| */ | ||
| [[nodiscard]] virtual Result send(const std::vector<uint8_t>& data) = 0; | ||
|
|
||
| virtual void set_receive_callback(TcpReceiveCallback callback) = 0; | ||
| virtual void set_connected_callback(TcpConnectedCallback callback) = 0; | ||
| virtual void set_disconnected_callback(TcpDisconnectedCallback callback) = 0; | ||
|
|
||
| [[nodiscard]] virtual Endpoint get_local_endpoint() const = 0; | ||
| [[nodiscard]] virtual bool is_connected() const = 0; | ||
| }; | ||
|
|
||
| } // namespace transport | ||
| } // namespace someip | ||
|
|
||
| #endif // SOMEIP_TRANSPORT_TCP_SOCKET_ADAPTER_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| /******************************************************************************** | ||
| * Copyright (c) 2025 Vinicius Tadeu Zein | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Apache License Version 2.0 which is available at | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| ********************************************************************************/ | ||
|
|
||
| #ifndef SOMEIP_TRANSPORT_UDP_SOCKET_ADAPTER_H | ||
| #define SOMEIP_TRANSPORT_UDP_SOCKET_ADAPTER_H | ||
|
|
||
| #include "transport/endpoint.h" | ||
| #include "common/result.h" | ||
| #include <functional> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| namespace someip { | ||
| namespace transport { | ||
|
|
||
| /** | ||
| * @brief Callback invoked by the adapter when a datagram is received. | ||
| * | ||
| * Integrators call this from their I/O event path after a packet is available. | ||
| * The payload is the raw UDP payload (one datagram). | ||
| */ | ||
| using UdpReceiveCallback = std::function<void(const std::vector<uint8_t>& data, const Endpoint& sender)>; | ||
|
|
||
| /** | ||
| * @brief UDP socket abstraction for event-driven SOME/IP transport. | ||
| * | ||
| * Implemented by integrators using non-BSD stacks (e.g. custom datagram sockets). | ||
| * Must not depend on platform socket headers. | ||
| */ | ||
| class IUdpSocketAdapter { | ||
| public: | ||
| virtual ~IUdpSocketAdapter() = default; | ||
|
|
||
| /** | ||
| * @brief Open and bind the local endpoint (port 0 selects an ephemeral port). | ||
| */ | ||
| [[nodiscard]] virtual Result open(const Endpoint& local_endpoint) = 0; | ||
|
|
||
| /** | ||
| * @brief Close the socket and release resources. | ||
| */ | ||
| virtual void close() = 0; | ||
|
|
||
| /** | ||
| * @brief Send one datagram to the destination. | ||
| */ | ||
| [[nodiscard]] virtual Result send(const std::vector<uint8_t>& data, const Endpoint& destination) = 0; | ||
|
|
||
| /** | ||
| * @brief Join an IPv4 multicast group. | ||
| * @param multicast_address Group address (e.g. 224.0.0.1) | ||
| * @param interface_address Outgoing interface address; empty uses stack default | ||
| */ | ||
| [[nodiscard]] virtual Result join_multicast(const std::string& multicast_address, | ||
| const std::string& interface_address = {}) = 0; | ||
|
|
||
| /** | ||
| * @brief Leave a multicast group previously joined. | ||
| */ | ||
| [[nodiscard]] virtual Result leave_multicast(const std::string& multicast_address, | ||
| const std::string& interface_address = {}) = 0; | ||
|
|
||
| /** | ||
| * @brief Register the receive callback (nullptr clears). | ||
| * | ||
| * The adapter must invoke the callback for each received datagram from the | ||
| * integrator's event loop or I/O thread. | ||
| */ | ||
| virtual void set_receive_callback(UdpReceiveCallback callback) = 0; | ||
|
|
||
| /** | ||
| * @brief Effective local endpoint after open (required after bind with port 0). | ||
| */ | ||
| [[nodiscard]] virtual Endpoint get_local_endpoint() const = 0; | ||
| }; | ||
|
|
||
| } // namespace transport | ||
| } // namespace someip | ||
|
|
||
| #endif // SOMEIP_TRANSPORT_UDP_SOCKET_ADAPTER_H |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Non-polymorphic initialization methods may cause usage errors.
initialize(),enable_server_mode(), andtry_accept_connection()are not part of theITransportinterface. Code usingITransport*polymorphically cannot call these required setup methods, potentially leading toNOT_INITIALIZEDerrors at runtime.Consider either:
local_endpointin the constructor (matchingEventDrivenUdpTransport)🤖 Prompt for AI Agents