3#if defined(_WIN32) || defined(_WIN64)
7#include <asio/read.hpp>
8#include <asio/write.hpp>
38 "TCP backend initialized");
52 "TCP backend started");
68 if (conn->socket.close(ec)) {
70 "Error closing TCP connection {}: {}",
id, ec.message());
73 if (conn->reconnect_timer) {
74 conn->reconnect_timer->cancel();
83 if (!listener->acceptor.close(ec)) {
85 "Error closing TCP listener {}: {}",
id, ec.message());
91 "TCP backend stopped");
111 "TCP backend shutdown");
124 auto listener = std::make_unique<ListenerState>(
m_context);
125 listener->info = info;
129 auto endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v4(), info.
local_port);
131 if (listener->acceptor.open(endpoint.protocol(), ec)) {
133 "Failed to open TCP acceptor: {}", ec.message());
137 if (listener->acceptor.set_option(asio::socket_base::reuse_address(
true), ec)) {
139 "Failed to set SO_REUSEADDR on TCP acceptor: {}", ec.message());
143 if (listener->acceptor.bind(endpoint, ec)) {
145 "Failed to bind TCP acceptor to port {}: {}", info.
local_port, ec.message());
149 if (listener->acceptor.listen(asio::socket_base::max_listen_connections, ec)) {
151 "Failed to listen on port {}: {}", info.
local_port, ec.message());
157 auto* raw = listener.get();
171 "TCP listener {} opened on port {}", info.
id, info.
local_port);
176 auto conn = std::make_unique<ConnectionState>(
m_context);
179 conn->is_outbound =
true;
181 auto* raw = conn.get();
204 if (!it->second->socket.close(ec)) {
206 "Error closing TCP connection {}: {}", endpoint_id, ec.message());
209 if (it->second->reconnect_timer) {
210 it->second->reconnect_timer->cancel();
215 "TCP connection {} closed", endpoint_id);
226 if (it->second->acceptor.close(ec)) {
228 "Error closing TCP listener {}: {}", endpoint_id, ec.message());
234 "TCP listener {} closed", endpoint_id);
245 return it->second->info.state;
253 return it->second->info.state;
262 std::vector<EndpointInfo> result;
268 result.push_back(conn->info);
275 result.push_back(listener->info);
294 auto& conn = *it->second;
299 auto frame = std::make_shared<std::vector<uint8_t>>(
sizeof(uint32_t) +
size);
300 uint32_t net_len = htonl(
static_cast<uint32_t
>(
size));
301 std::memcpy(frame->data(), &net_len,
sizeof(uint32_t));
302 std::memcpy(frame->data() +
sizeof(uint32_t), data,
size);
306 asio::buffer(*frame),
307 [frame, endpoint_id,
this](
const asio::error_code& ec,
size_t) {
309 MF_WARN(Journal::Component::Core, Journal::Context::NetworkBackend,
310 "TCP write failed on endpoint {}: {}", endpoint_id, ec.message());
312 std::shared_lock lk(m_connections_mutex);
313 auto cit = m_connections.find(endpoint_id);
314 if (cit != m_connections.end()) {
315 on_connection_error(*cit->second, ec);
323bool TCPBackend::send_to(uint64_t endpoint_id,
const uint8_t* data,
size_t size,
324 const std::string&, uint16_t)
326 return send(endpoint_id, data,
size);
335 m_receive_callback = std::move(
callback);
340 m_state_callback = std::move(
callback);
354 transition_state(conn.
info, EndpointState::ERROR);
360 conn.
socket.async_connect(
362 [
this, ep_id = conn.
info.
id](
const asio::error_code& connect_ec) {
363 std::shared_lock lock(m_connections_mutex);
364 auto it = m_connections.find(ep_id);
365 if (it == m_connections.end()) {
369 auto& c = *it->second;
373 "TCP connect failed for endpoint {}: {}",
374 ep_id, connect_ec.message());
376 if (m_config.auto_reconnect) {
377 transition_state(c.info, EndpointState::RECONNECTING);
378 schedule_reconnect(c);
380 transition_state(c.info, EndpointState::ERROR);
385 transition_state(c.info, EndpointState::OPEN);
386 start_receive_chain(c);
389 "TCP endpoint {} connected to {}:{}",
390 ep_id, c.info.remote_address, c.info.remote_port);
404 [
this, listener_id = listener.
info.
id](
const asio::error_code& ec) {
405 std::shared_lock lock(m_listeners_mutex);
406 auto it = m_listeners.find(listener_id);
407 if (it == m_listeners.end()) {
411 auto& lst = *it->second;
414 if (ec == asio::error::operation_aborted) {
418 "TCP accept error on listener {}: {}", listener_id, ec.message());
424 if (m_allocate_endpoint_id) {
425 new_id = m_allocate_endpoint_id();
428 auto peer_ep = lst.pending_socket.remote_endpoint();
429 std::string peer_addr = peer_ep.address().to_string();
430 uint16_t peer_port = peer_ep.port();
432 auto conn = std::make_unique<ConnectionState>(m_context);
433 conn->socket = std::move(lst.pending_socket);
434 conn->is_outbound =
false;
435 conn->info.id = new_id;
436 conn->info.transport = NetworkTransport::TCP;
437 conn->info.role = EndpointRole::BIDIRECTIONAL;
438 conn->info.state = EndpointState::OPEN;
439 conn->info.remote_address = peer_addr;
440 conn->info.remote_port = peer_port;
441 conn->info.local_port = lst.info.local_port;
443 auto* raw = conn.get();
446 std::unique_lock conn_lock(m_connections_mutex);
447 m_connections[new_id] = std::move(conn);
450 if (m_state_callback) {
451 m_state_callback(raw->info, EndpointState::CLOSED, EndpointState::OPEN);
454 start_receive_chain(*raw);
457 "TCP accepted connection {} from {}:{} on listener {}",
458 new_id, peer_addr, peer_port, listener_id);
473 [
this, ep_id = conn.
info.
id](
const asio::error_code& ec,
size_t bytes) {
474 std::shared_lock lock(m_connections_mutex);
475 auto it = m_connections.find(ep_id);
476 if (it == m_connections.end()) {
479 on_header_received(*it->second, ec, bytes);
484 const asio::error_code& ec,
size_t)
487 on_connection_error(conn, ec);
492 std::memcpy(&net_len, conn.
header_buf.data(),
sizeof(uint32_t));
493 uint32_t payload_size = ntohl(net_len);
495 if (payload_size == 0 || payload_size > 64 * 1024 * 1024) {
497 "TCP endpoint {} received invalid frame length: {}",
498 conn.
info.
id, payload_size);
499 on_connection_error(conn, asio::error::message_size);
508 [
this, ep_id = conn.
info.
id](
const asio::error_code& read_ec,
size_t bytes) {
509 std::shared_lock lock(m_connections_mutex);
510 auto it = m_connections.find(ep_id);
511 if (it == m_connections.end()) {
514 on_payload_received(*it->second, read_ec, bytes);
519 const asio::error_code& ec,
size_t )
522 on_connection_error(conn, ec);
526 if (m_receive_callback) {
529 m_receive_callback(conn.
info.
id,
534 start_receive_chain(conn);
541void TCPBackend::on_connection_error(
ConnectionState& conn,
const asio::error_code& ec)
543 if (ec == asio::error::operation_aborted) {
547 if (ec == asio::error::eof) {
549 "TCP endpoint {} peer disconnected", conn.
info.
id);
552 "TCP endpoint {} error: {}", conn.
info.
id, ec.message());
555 asio::error_code close_ec;
556 if (!conn.
socket.close(close_ec)) {
558 "Error closing TCP socket for endpoint {}: {}", conn.
info.
id, close_ec.message());
562 transition_state(conn.
info, EndpointState::RECONNECTING);
563 schedule_reconnect(conn);
565 transition_state(conn.
info, EndpointState::ERROR);
572 conn.
reconnect_timer = std::make_unique<asio::steady_timer>(m_context);
576 std::chrono::milliseconds(m_config.reconnect_interval_ms));
579 [
this, ep_id = conn.
info.
id](
const asio::error_code& ec) {
580 if (ec == asio::error::operation_aborted) {
584 std::shared_lock lock(m_connections_mutex);
585 auto it = m_connections.find(ep_id);
586 if (it == m_connections.end()) {
590 auto& c = *it->second;
592 c.socket = asio::ip::tcp::socket(m_context);
595 "TCP endpoint {} attempting reconnect to {}:{}",
596 ep_id, c.info.remote_address, c.info.remote_port);
605 info.
state = new_state;
607 if (m_state_callback && prev != new_state) {
608 m_state_callback(info, prev, new_state);
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
void close_endpoint(uint64_t endpoint_id) override
Close an endpoint and release its resources.
void start_connect(ConnectionState &conn)
Initiate async_connect for an outbound endpoint.
std::atomic< bool > m_running
std::unordered_map< uint64_t, std::unique_ptr< ConnectionState > > m_connections
std::shared_mutex m_listeners_mutex
void shutdown() override
Release all resources, close all endpoints.
std::unordered_map< uint64_t, std::unique_ptr< ListenerState > > m_listeners
bool initialize() override
Initialise backend resources (sockets, SHM segments, etc.)
bool send(uint64_t endpoint_id, const uint8_t *data, size_t size) override
Send data through an endpoint.
void start_accept(ListenerState &listener)
Initiate async_accept loop for a listener.
std::shared_mutex m_connections_mutex
EndpointState get_endpoint_state(uint64_t endpoint_id) const override
Query the current state of an endpoint.
std::vector< EndpointInfo > get_endpoints() const override
List all endpoints currently managed by this backend.
void stop() override
Stop receive threads without releasing resources.
uint64_t open_endpoint(const EndpointInfo &info) override
Open a new endpoint.
asio::io_context & m_context
TCPBackend(const TCPBackendInfo &config, asio::io_context &context)
Construct with config and a reference to the shared io_context.
EndpointStateCallback m_state_callback
std::atomic< bool > m_initialized
void start() override
Start receive threads and accept connections.
EndpointState
Observable connection state for an endpoint.
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.
@ Shutdown
Engine/subsystem shutdown and cleanup.
@ NetworkBackend
Network transport backend (UDP, TCP, SHM)
@ Init
Engine/subsystem initialization.
@ Core
Core engine, backend, subsystems.
std::string remote_address
Describes one logical send/receive endpoint managed by a backend.
Configuration for the TCP transport backend.
asio::ip::tcp::socket socket
std::array< uint8_t, 4 > header_buf
std::unique_ptr< asio::steady_timer > reconnect_timer
std::vector< uint8_t > payload_buf
State for a connected TCP peer (inbound or outbound)
asio::ip::tcp::acceptor acceptor
asio::ip::tcp::socket pending_socket
State for a listening acceptor.