MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
UDPBackend.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "NetworkBackend.hpp"
4
5#if defined(_WIN32) || defined(_WIN64)
6#include <windows.h>
7#endif
8
9#include <asio/io_context.hpp>
10#include <asio/ip/udp.hpp>
11
12#if defined(_WIN32) || defined(_WIN64)
13#ifdef ERROR
14#undef ERROR
15#endif
16#endif
17
18namespace MayaFlux::Core {
19
20/**
21 * @class UDPBackend
22 * @brief Connectionless datagram transport over UDP via standalone Asio
23 *
24 * Each endpoint maps to a (socket, optional default remote) pair.
25 * Endpoints that share a local port share the underlying socket.
26 *
27 * All I/O is async on the io_context provided by NetworkSubsystem.
28 * No dedicated threads, no mutexes on the data path.
29 *
30 * async_receive_from -> completion fires receive callback -> resubmit
31 * async_send_to -> fire-and-forget (UDP is unreliable anyway)
32 *
33 * OSC, Art-Net, or any other datagram protocol is a serialisation
34 * concern above this layer. This backend knows nothing about message
35 * formats: bytes in, bytes out.
36 *
37 * @code
38 * // Created and started by NetworkSubsystem. Direct use for testing:
39 * asio::io_context ctx;
40 * UDPBackend udp(config, ctx);
41 * udp.initialize();
42 * udp.set_receive_callback([](uint64_t id, const uint8_t* d, size_t s, std::string_view a) {
43 * // handle datagram
44 * });
45 * udp.start();
46 *
47 * EndpointInfo ep;
48 * ep.role = EndpointRole::BIDIRECTIONAL;
49 * ep.local_port = 8000;
50 * ep.remote_address = "127.0.0.1";
51 * ep.remote_port = 9000;
52 * auto id = udp.open_endpoint(ep);
53 *
54 * udp.send(id, payload.data(), payload.size());
55 *
56 * ctx.run(); // normally driven by NetworkSubsystem's jthread
57 * @endcode
58 */
59class MAYAFLUX_API UDPBackend : public INetworkBackend {
60public:
61 /**
62 * @brief Construct with config and a reference to the shared io_context
63 * @param config UDP-specific configuration.
64 * @param context The io_context owned by NetworkSubsystem. All async
65 * operations are posted here.
66 */
67 UDPBackend(const UDPBackendInfo& config, asio::io_context& context);
68 ~UDPBackend() override;
69
70 UDPBackend(const UDPBackend&) = delete;
71 UDPBackend& operator=(const UDPBackend&) = delete;
74
75 // ─── INetworkBackend lifecycle ──────────────────────────────────────────
76
77 bool initialize() override;
78 void start() override;
79 void stop() override;
80 void shutdown() override;
81
82 [[nodiscard]] bool is_initialized() const override { return m_initialized.load(); }
83 [[nodiscard]] bool is_running() const override { return m_running.load(); }
84
85 [[nodiscard]] NetworkTransport get_transport() const override { return NetworkTransport::UDP; }
86 [[nodiscard]] std::string get_name() const override { return "UDP (Asio)"; }
87 [[nodiscard]] std::string get_version() const override { return "1.0"; }
88
89 // ─── INetworkBackend endpoints ──────────────────────────────────────────
90
91 uint64_t open_endpoint(const EndpointInfo& info) override;
92 void close_endpoint(uint64_t endpoint_id) override;
93 [[nodiscard]] EndpointState get_endpoint_state(uint64_t endpoint_id) const override;
94 [[nodiscard]] std::vector<EndpointInfo> get_endpoints() const override;
95
96 // ─── INetworkBackend data ───────────────────────────────────────────────
97
98 bool send(uint64_t endpoint_id, const uint8_t* data, size_t size) override;
99 bool send_to(uint64_t endpoint_id, const uint8_t* data, size_t size,
100 const std::string& address, uint16_t port) override;
101
102 // ─── INetworkBackend callbacks ──────────────────────────────────────────
103
104 void set_receive_callback(NetworkReceiveCallback callback) override;
105 void set_state_callback(EndpointStateCallback callback) override;
106
107private:
108 /**
109 * @brief Per-bound-port socket state
110 *
111 * Multiple endpoints can share a socket (same local port, different
112 * remote targets). The socket owns the async_receive_from loop.
113 */
114 struct SocketState {
115 asio::ip::udp::socket socket;
116 std::array<uint8_t, 65536> recv_buffer {};
117 asio::ip::udp::endpoint sender_endpoint;
118 uint16_t local_port {};
119 uint32_t ref_count {};
120
121 explicit SocketState(asio::io_context& ctx)
122 : socket(ctx)
123 {
124 }
125 };
126
127 /**
128 * @brief Per-endpoint state
129 *
130 * Maps an endpoint id to its socket and optional default remote target.
131 */
134 SocketState* socket_state {};
135 asio::ip::udp::endpoint default_remote;
136 bool has_default_remote {};
137 };
138
140 asio::io_context& m_context;
141
142 std::atomic<bool> m_initialized { false };
143 std::atomic<bool> m_running { false };
144
145 mutable std::shared_mutex m_endpoints_mutex;
146 std::unordered_map<uint64_t, EndpointRecord> m_endpoints;
147
148 mutable std::shared_mutex m_sockets_mutex;
149 std::unordered_map<uint16_t, std::unique_ptr<SocketState>> m_sockets;
150
153
154 /**
155 * @brief Get or create a socket bound to local_port.
156 *
157 * If a socket for this port already exists, increments ref_count
158 * and returns it. Otherwise binds a new socket and starts the
159 * async receive loop.
160 */
161 SocketState* acquire_socket(uint16_t local_port);
162
163 /**
164 * @brief Decrement ref_count for a socket. Closes if zero.
165 */
166 void release_socket(uint16_t local_port);
167
168 /**
169 * @brief Post the first async_receive_from for a newly bound socket.
170 */
171 void start_receive_loop(SocketState& state);
172
173 /**
174 * @brief Completion handler for async_receive_from.
175 *
176 * Fires the receive callback with the endpoint id resolved from
177 * the sender address, then resubmits async_receive_from.
178 */
179 void on_receive(SocketState& state, const asio::error_code& ec, size_t bytes_received);
180
181 /**
182 * @brief Resolve which endpoint id a received datagram belongs to.
183 *
184 * For a given socket (local port), checks if any endpoint has a
185 * default_remote matching the sender. If none match, falls back to
186 * the first RECEIVE or BIDIRECTIONAL endpoint on that port.
187 */
188 uint64_t resolve_endpoint_for_sender(
189 uint16_t local_port, const asio::ip::udp::endpoint& sender) const;
190
191 void transition_state(EndpointRecord& record, EndpointState new_state);
192};
193
194} // namespace MayaFlux::Core
bool size
PickCallback callback
Definition Picker.cpp:22
Abstract interface for network transport backends.
std::unordered_map< uint16_t, std::unique_ptr< SocketState > > m_sockets
NetworkReceiveCallback m_receive_callback
UDPBackend & operator=(const UDPBackend &)=delete
UDPBackend(UDPBackend &&)=delete
NetworkTransport get_transport() const override
bool is_initialized() const override
std::unordered_map< uint64_t, EndpointRecord > m_endpoints
std::string get_name() const override
UDPBackend(const UDPBackend &)=delete
EndpointStateCallback m_state_callback
bool is_running() const override
asio::io_context & m_context
UDPBackend & operator=(UDPBackend &&)=delete
std::shared_mutex m_sockets_mutex
std::string get_version() const override
std::shared_mutex m_endpoints_mutex
Connectionless datagram transport over UDP via standalone Asio.
void initialize()
Definition main.cpp:10
EndpointState
Observable connection state for an endpoint.
NetworkTransport
Identifies the transport protocol a backend implements.
std::function< void(uint64_t endpoint_id, const uint8_t *data, size_t size, std::string_view sender_addr)> NetworkReceiveCallback
Callback signature for inbound data on an endpoint.
std::function< void(const EndpointInfo &info, EndpointState previous, EndpointState current)> EndpointStateCallback
Callback signature for endpoint state changes.
Describes one logical send/receive endpoint managed by a backend.
Configuration for the UDP transport backend.
asio::ip::udp::endpoint sender_endpoint
Per-bound-port socket state.