feat(tcp): ListenerTCP binds the address it is given, at port 0 if asked
A new constructor takes a numeric bind address (127.0.0.1, ::1, 0.0.0.0, ::) and accepts port 0, and Port() reports the port the listener got. Until now every listener bound 0.0.0.0 at a port the caller chose, so a helper meant for this machine's own processes was reachable from the network, and the caller had to guess a free port. A name such as `localhost` is refused instead of bound to whichever address the resolver ranks first. The port-only constructor still listens on every IPv4 interface. `running` is atomic now: Stop() runs on another thread than the accept loop that reads it. The accept loops take a sockaddr_storage, so an IPv6 peer's address is no longer cut short. New: ShouldBindLoopbackEphemeral. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
78853033e8
commit
d6f8f4edcb
5 changed files with 235 additions and 40 deletions
|
|
@ -1,28 +1,40 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
export module Crafter.Network:ListenerTCP;
|
||||
import std;
|
||||
import :ClientTCP;
|
||||
|
||||
#ifndef CRAFTER_NETWORK_BROWSER
|
||||
namespace Crafter {
|
||||
export class ListenerTCP {
|
||||
public:
|
||||
bool running = true;
|
||||
std::function<void(ClientTCP*)> connectCallback;
|
||||
std::uint32_t concurrentClientLimit;
|
||||
std::uint32_t totalClientLimit;
|
||||
ListenerTCP(std::uint16_t port, std::function<void(ClientTCP*)> connectCallback, std::uint32_t concurrentClientLimit = std::numeric_limits<std::uint32_t>::max(), std::uint32_t totalClientLimit = std::numeric_limits<std::uint32_t>::max());
|
||||
~ListenerTCP();
|
||||
void Stop();
|
||||
void ListenSyncSync();
|
||||
void ListenSyncAsync();
|
||||
void ListenAsyncSync();
|
||||
void ListenAsyncAsync();
|
||||
private:
|
||||
std::uint32_t totalClientCounter = 0;
|
||||
int s;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
export module Crafter.Network:ListenerTCP;
|
||||
import std;
|
||||
import :ClientTCP;
|
||||
|
||||
#ifndef CRAFTER_NETWORK_BROWSER
|
||||
namespace Crafter {
|
||||
export class ListenerTCP {
|
||||
public:
|
||||
// Atomic: Stop() is called from another thread than the one running
|
||||
// an accept loop, which reads this on every turn.
|
||||
std::atomic<bool> running = true;
|
||||
std::function<void(ClientTCP*)> connectCallback;
|
||||
std::uint32_t concurrentClientLimit;
|
||||
std::uint32_t totalClientLimit;
|
||||
// Every IPv4 interface (0.0.0.0) at `port`.
|
||||
ListenerTCP(std::uint16_t port, std::function<void(ClientTCP*)> connectCallback, std::uint32_t concurrentClientLimit = std::numeric_limits<std::uint32_t>::max(), std::uint32_t totalClientLimit = std::numeric_limits<std::uint32_t>::max());
|
||||
// `bindAddress` is a numeric IPv4 or IPv6 address — 127.0.0.1 or ::1
|
||||
// for this machine alone, 0.0.0.0 or :: for every interface — and a
|
||||
// name such as `localhost` is refused: the address decides who can
|
||||
// reach the listener, so it is spelled out rather than looked up. A
|
||||
// `port` of 0 lets the kernel pick a free one; Port() says which.
|
||||
ListenerTCP(std::string_view bindAddress, std::uint16_t port, std::function<void(ClientTCP*)> connectCallback, std::uint32_t concurrentClientLimit = std::numeric_limits<std::uint32_t>::max(), std::uint32_t totalClientLimit = std::numeric_limits<std::uint32_t>::max());
|
||||
~ListenerTCP();
|
||||
// The port the listener is bound to: the one asked for, or the
|
||||
// kernel's pick when that was 0.
|
||||
std::uint16_t Port() const;
|
||||
void Stop();
|
||||
void ListenSyncSync();
|
||||
void ListenSyncAsync();
|
||||
void ListenAsyncSync();
|
||||
void ListenAsyncAsync();
|
||||
private:
|
||||
std::uint32_t totalClientCounter = 0;
|
||||
int s;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue