Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions include/transport/event_driven_tcp_transport.h
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);
Comment on lines +53 to +62
Copy link
Copy Markdown
Contributor

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(), and try_accept_connection() are not part of the ITransport interface. Code using ITransport* polymorphically cannot call these required setup methods, potentially leading to NOT_INITIALIZED errors at runtime.

Consider either:

  1. Accepting local_endpoint in the constructor (matching EventDrivenUdpTransport)
  2. Documenting that this transport requires concrete type usage for setup
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/transport/event_driven_tcp_transport.h` around lines 47 - 55, The
three non-polymorphic setup methods on EventDrivenTcpTransport (initialize,
enable_server_mode, try_accept_connection) prevent correct usage via
ITransport*; change EventDrivenTcpTransport to accept the required
local_endpoint in its constructor (mirror EventDrivenUdpTransport's pattern),
remove/privatize the standalone initialize() to avoid misuse, and keep
enable_server_mode/try_accept_connection as concrete-type-only APIs but add a
clear comment on EventDrivenTcpTransport explaining they are not part of
ITransport and must be invoked only when using the concrete class; update all
callers to construct with the Endpoint and to call server-mode methods on the
concrete type.


[[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};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

server_mode_ should be atomic or protected.

server_mode_ is a plain bool that can be read in connect() and try_accept_connection() while potentially being written in enable_server_mode() or stop() from different threads. Consider using std::atomic<bool> for consistency with running_ and initialized_.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/transport/event_driven_tcp_transport.h` at line 82, The field
server_mode_ is a plain bool accessed concurrently (read in connect() and
try_accept_connection(), written in enable_server_mode() and stop()) and should
be made thread-safe like running_ and initialized_; change server_mode_ to
std::atomic<bool> (or protect all accesses with the same mutex used for
running_/initialized_), update any direct reads/writes in connect(),
try_accept_connection(), enable_server_mode(), and stop() to use atomic
load/store (or guarded access), and ensure header includes <atomic> so there are
no data races.


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
83 changes: 83 additions & 0 deletions include/transport/event_driven_udp_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);
Comment thread
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
89 changes: 89 additions & 0 deletions include/transport/tcp_socket_adapter.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
90 changes: 90 additions & 0 deletions include/transport/udp_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
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ set(OPENSOMEIP_SOURCES
transport/endpoint.cpp
transport/udp_transport.cpp
transport/tcp_transport.cpp
transport/event_driven_udp_transport.cpp
transport/event_driven_tcp_transport.cpp
e2e/e2e_protection.cpp
e2e/e2e_header.cpp
e2e/e2e_crc.cpp
Expand Down
Loading
Loading