Files
columnlynx/include/columnlynx/server/net/tcp/tcp_server.hpp

89 lines
3.4 KiB
C++

// tcp_server.hpp - TCP Server for ColumnLynx
// Copyright (C) 2025 DcruBro
// Distributed under the terms of the GNU General Public License, either version 2 only or version 3. See LICENSES/ for details.
#pragma once
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
#include <memory>
#include <unordered_set>
#include <new>
#include <asio.hpp>
#include <columnlynx/common/net/tcp/tcp_message_type.hpp>
#include <columnlynx/common/utils.hpp>
#include <columnlynx/server/net/tcp/tcp_connection.hpp>
#include <columnlynx/common/libsodium_wrapper.hpp>
#include <columnlynx/common/net/protocol_structs.hpp>
namespace ColumnLynx::Net::TCP {
class TCPServer {
public:
TCPServer(asio::io_context& ioContext,
uint16_t port,
std::shared_ptr<Utils::LibSodiumWrapper> sodiumWrapper,
std::shared_ptr<bool> hostRunning, bool ipv4Only = false)
: mIoContext(ioContext),
mAcceptor(ioContext),
mSodiumWrapper(sodiumWrapper),
mHostRunning(hostRunning)
{
// Preload the config map
mRawServerConfig = Utils::getConfigMap("server_config", {"NETWORK", "SUBNET_MASK"});
asio::error_code ec_open, ec_v6only, ec_bind;
if (!ipv4Only) {
// Try IPv6 (dual-stack if supported)
asio::ip::tcp::endpoint endpoint_v6(asio::ip::tcp::v6(), port);
mAcceptor.open(endpoint_v6.protocol(), ec_open);
if (!ec_open) {
// Try enabling dual-stack, but DO NOT treat failure as fatal
mAcceptor.set_option(asio::ip::v6_only(false), ec_v6only);
// Try binding IPv6
mAcceptor.bind(endpoint_v6, ec_bind);
}
}
// If IPv6 bind failed OR IPv6 open failed OR forced IPv4-only
if (ipv4Only || ec_open || ec_bind) {
if (!ipv4Only)
Utils::warn("TCP: IPv6 unavailable (open=" + ec_open.message() +
", bind=" + ec_bind.message() +
"), falling back to IPv4 only");
asio::ip::tcp::endpoint endpoint_v4(asio::ip::tcp::v4(), port);
mAcceptor.close(); // guarantee clean state
mAcceptor.open(endpoint_v4.protocol());
mAcceptor.bind(endpoint_v4);
}
// Start listening
mAcceptor.listen();
Utils::log("Started TCP server on port " + std::to_string(port));
mStartAccept();
}
// Stop the TCP Server
void stop();
private:
// Start accepting clients via TCP
void mStartAccept();
asio::io_context &mIoContext;
asio::ip::tcp::acceptor mAcceptor;
std::unordered_set<TCPConnection::pointer> mClients;
std::shared_ptr<Utils::LibSodiumWrapper> mSodiumWrapper;
std::shared_ptr<bool> mHostRunning;
std::unordered_map<std::string, std::string> mRawServerConfig;
};
}