MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TCPBackend.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/tcp.hpp>
11#include <asio/steady_timer.hpp>
12
13#if defined(_WIN32) || defined(_WIN64)
14#ifdef ERROR
15#undef ERROR
16#endif
17#endif
18
19namespace MayaFlux::Core {
20
21/**
22 * @class TCPBackend
23 * @brief Connection-oriented reliable stream transport over TCP via standalone Asio
24 *
25 * Two endpoint modes:
26 * - Outbound (remote_address set): async_connect to a remote host.
27 * - Listener (local_port set, no remote_address): async_accept on a port.
28 * Accepted connections become new endpoints, announced via state callback.
29 *
30 * Framing: every message on the wire is [uint32_t network-order length][payload].
31 * The backend handles framing transparently. Callers send and receive complete
32 * messages, never partial streams.
33 *
34 * Reconnection: if auto_reconnect is enabled and a connection drops, the
35 * backend transitions to RECONNECTING and schedules async_connect retries
36 * via asio::steady_timer.
37 *
38 * All I/O is async on the shared io_context. No dedicated threads per
39 * connection. No mutexes on the data path. One io_context thread handles
40 * all sockets: accept, connect, read, write, reconnect timers.
41 *
42 * Receive chain per connection:
43 * async_read(4 bytes header) -> parse length
44 * -> async_read(length bytes payload) -> fire callback -> resubmit header read
45 *
46 * Send:
47 * async_write(4 bytes header + payload). Serialised per-connection via
48 * Asio strand (implicit: all operations on one io_context thread).
49 *
50 * @code
51 * asio::io_context ctx;
52 * TCPBackend tcp(config, ctx);
53 * tcp.initialize();
54 * tcp.set_receive_callback([](uint64_t id, const uint8_t* d, size_t s, std::string_view a) {
55 * // handle complete framed message
56 * });
57 * tcp.set_state_callback([](const EndpointInfo& ep, EndpointState prev, EndpointState cur) {
58 * // track connection/disconnection
59 * });
60 * tcp.start();
61 *
62 * // Outbound connection
63 * EndpointInfo client_ep;
64 * client_ep.role = EndpointRole::BIDIRECTIONAL;
65 * client_ep.remote_address = "192.168.1.50";
66 * client_ep.remote_port = 7000;
67 * auto ep_id = tcp.open_endpoint(client_ep);
68 *
69 * // Listener
70 * EndpointInfo server_ep;
71 * server_ep.role = EndpointRole::RECEIVE;
72 * server_ep.local_port = 7000;
73 * auto listen_id = tcp.open_endpoint(server_ep);
74 *
75 * ctx.run();
76 * @endcode
77 */
78class MAYAFLUX_API TCPBackend : public INetworkBackend {
79public:
80 /**
81 * @brief Construct with config and a reference to the shared io_context
82 * @param config TCP-specific configuration.
83 * @param context The io_context owned by NetworkSubsystem.
84 */
85 TCPBackend(const TCPBackendInfo& config, asio::io_context& context);
86 ~TCPBackend() override;
87
88 TCPBackend(const TCPBackend&) = delete;
89 TCPBackend& operator=(const TCPBackend&) = delete;
92
93 // ─── INetworkBackend lifecycle ──────────────────────────────────────────
94
95 bool initialize() override;
96 void start() override;
97 void stop() override;
98 void shutdown() override;
99
100 [[nodiscard]] bool is_initialized() const override { return m_initialized.load(); }
101 [[nodiscard]] bool is_running() const override { return m_running.load(); }
102
103 [[nodiscard]] NetworkTransport get_transport() const override { return NetworkTransport::TCP; }
104 [[nodiscard]] std::string get_name() const override { return "TCP (Asio)"; }
105 [[nodiscard]] std::string get_version() const override { return "1.0"; }
106
107 // ─── INetworkBackend endpoints ──────────────────────────────────────────
108
109 uint64_t open_endpoint(const EndpointInfo& info) override;
110 void close_endpoint(uint64_t endpoint_id) override;
111 [[nodiscard]] EndpointState get_endpoint_state(uint64_t endpoint_id) const override;
112 [[nodiscard]] std::vector<EndpointInfo> get_endpoints() const override;
113
114 // ─── INetworkBackend data ───────────────────────────────────────────────
115
116 bool send(uint64_t endpoint_id, const uint8_t* data, size_t size) override;
117 bool send_to(uint64_t endpoint_id, const uint8_t* data, size_t size,
118 const std::string& address, uint16_t port) override;
119
120 // ─── INetworkBackend callbacks ──────────────────────────────────────────
121
122 void set_receive_callback(NetworkReceiveCallback callback) override;
123 void set_state_callback(EndpointStateCallback callback) override;
124
125private:
126 /**
127 * @brief State for a connected TCP peer (inbound or outbound)
128 */
131 asio::ip::tcp::socket socket;
132 std::array<uint8_t, 4> header_buf {};
133 std::vector<uint8_t> payload_buf;
134 std::unique_ptr<asio::steady_timer> reconnect_timer;
135 bool is_outbound {};
136
137 explicit ConnectionState(asio::io_context& ctx)
138 : socket(ctx)
139 {
140 }
141 };
142
143 /**
144 * @brief State for a listening acceptor
145 */
148 asio::ip::tcp::acceptor acceptor;
149 asio::ip::tcp::socket pending_socket;
150
151 explicit ListenerState(asio::io_context& ctx)
152 : acceptor(ctx)
153 , pending_socket(ctx)
154 {
155 }
156 };
157
159 asio::io_context& m_context;
160
161 std::atomic<bool> m_initialized { false };
162 std::atomic<bool> m_running { false };
163
164 mutable std::shared_mutex m_connections_mutex;
165 std::unordered_map<uint64_t, std::unique_ptr<ConnectionState>> m_connections;
166
167 mutable std::shared_mutex m_listeners_mutex;
168 std::unordered_map<uint64_t, std::unique_ptr<ListenerState>> m_listeners;
169
172
173 /**
174 * @brief Pointer to subsystem's endpoint id allocator.
175 *
176 * When a listener accepts an inbound connection, the new connection
177 * needs a globally unique endpoint id. The subsystem provides this
178 * via a callback set during add_backend().
179 */
180 std::function<uint64_t()> m_allocate_endpoint_id;
181
182 // ─── Async operation chains ─────────────────────────────────────────────
183
184 /**
185 * @brief Initiate async_connect for an outbound endpoint.
186 *
187 * On success: transitions to OPEN, starts receive chain.
188 * On failure: transitions to ERROR or RECONNECTING.
189 */
190 void start_connect(ConnectionState& conn);
191
192 /**
193 * @brief Initiate async_accept loop for a listener.
194 *
195 * On accept: creates ConnectionState for the new peer, assigns
196 * endpoint id via m_allocate_endpoint_id, starts receive chain,
197 * fires state callback. Then resubmits async_accept.
198 */
199 void start_accept(ListenerState& listener);
200
201 /**
202 * @brief Start the framed message receive chain on a connection.
203 *
204 * Posts async_read for 4-byte header. Completion parses length,
205 * posts async_read for payload. Completion fires receive callback,
206 * then resubmits header read.
207 */
208 void start_receive_chain(ConnectionState& conn);
209
210 /**
211 * @brief Header read completion handler.
212 */
213 void on_header_received(ConnectionState& conn,
214 const asio::error_code& ec, size_t bytes);
215
216 /**
217 * @brief Payload read completion handler.
218 */
219 void on_payload_received(ConnectionState& conn,
220 const asio::error_code& ec, size_t bytes);
221
222 /**
223 * @brief Handle a connection error (read/write failure).
224 *
225 * Transitions to ERROR. If auto_reconnect and outbound, schedules
226 * reconnect via steady_timer.
227 */
228 void on_connection_error(ConnectionState& conn, const asio::error_code& ec);
229
230 /**
231 * @brief Schedule a reconnect attempt after the configured interval.
232 */
233 void schedule_reconnect(ConnectionState& conn);
234
235 void transition_state(EndpointInfo& info, EndpointState new_state);
236
237public:
238 /**
239 * @brief Set the endpoint id allocator (called by NetworkSubsystem)
240 *
241 * Required for listener-accepted connections that need globally
242 * unique ids. Set before start().
243 */
244 void set_endpoint_id_allocator(std::function<uint64_t()> allocator)
245 {
246 m_allocate_endpoint_id = std::move(allocator);
247 }
248};
249
250} // namespace MayaFlux::Core
bool size
PickCallback callback
Definition Picker.cpp:22
Abstract interface for network transport backends.
std::string get_name() const override
void set_endpoint_id_allocator(std::function< uint64_t()> allocator)
Set the endpoint id allocator (called by NetworkSubsystem)
std::function< uint64_t()> m_allocate_endpoint_id
Pointer to subsystem's endpoint id allocator.
NetworkReceiveCallback m_receive_callback
TCPBackend & operator=(const TCPBackend &)=delete
std::unordered_map< uint64_t, std::unique_ptr< ConnectionState > > m_connections
std::shared_mutex m_listeners_mutex
TCPBackend(TCPBackend &&)=delete
std::unordered_map< uint64_t, std::unique_ptr< ListenerState > > m_listeners
bool is_initialized() const override
TCPBackend(const TCPBackend &)=delete
std::shared_mutex m_connections_mutex
std::string get_version() const override
bool is_running() const override
TCPBackend & operator=(TCPBackend &&)=delete
asio::io_context & m_context
NetworkTransport get_transport() const override
EndpointStateCallback m_state_callback
Connection-oriented reliable stream transport over TCP 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 TCP transport backend.
std::unique_ptr< asio::steady_timer > reconnect_timer
State for a connected TCP peer (inbound or outbound)
State for a listening acceptor.