12#include "../../stdafx.h"
13#include "../../thread.h"
16#include "../network_coordinator.h"
17#include "../network_internal.h"
19#include "../../safeguards.h"
29TCPConnecter::TCPConnecter(std::string_view connection_string, uint16_t default_port,
const NetworkAddress &bind_address,
int family) :
30 bind_address(bind_address),
29TCPConnecter::TCPConnecter(std::string_view connection_string, uint16_t default_port,
const NetworkAddress &bind_address,
int family) : {
…}
42 server_address(
ServerAddress::Parse(connection_string, default_port))
59TCPConnecter::~TCPConnecter()
65 for (
const auto &socket : this->
sockets) {
71 if (this->
ai !=
nullptr) freeaddrinfo(this->
ai);
90 SOCKET sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
91 if (sock == INVALID_SOCKET) {
101 if (bind(sock, (
const sockaddr *)this->
bind_address.
GetAddress(), this->bind_address.GetAddressLength()) != 0) {
118 int err = connect(sock, address->ai_addr, (
int)address->ai_addrlen);
150 std::deque<addrinfo *> addresses_ipv4, addresses_ipv6;
155 bool seen_ipv6 =
false;
157 for (addrinfo *runp =
ai; runp !=
nullptr; runp = runp->ai_next) {
158 if (runp->ai_family == AF_INET6) {
160 }
else if (!seen_ipv6) {
170 for (addrinfo *runp =
ai; runp !=
nullptr; runp = runp->ai_next) {
172 if (this->
family != AF_UNSPEC && this->
family != runp->ai_family)
continue;
175 if (runp->ai_family == AF_INET6) {
176 addresses_ipv6.emplace_back(runp);
178 addresses_ipv4.emplace_back(runp);
188 while (!addresses_ipv4.empty() || !addresses_ipv6.empty()) {
189 if (!addresses_ipv6.empty()) {
190 this->
addresses.push_back(addresses_ipv6.front());
191 addresses_ipv6.pop_front();
193 if (!addresses_ipv4.empty()) {
194 this->
addresses.push_back(addresses_ipv4.front());
195 addresses_ipv4.pop_front();
200 if (_debug_net_level >= 6) {
205 for (
const auto &address : this->
addresses) {
226 hints.ai_family = AF_UNSPEC;
227 hints.ai_flags = AI_ADDRCONFIG;
228 hints.ai_socktype = SOCK_STREAM;
230 std::string port_name = fmt::format(
"{}", address.
GetPort());
232 static bool getaddrinfo_timeout_error_shown =
false;
233 auto start = std::chrono::steady_clock::now();
236 int error = getaddrinfo(address.
GetHostname().c_str(), port_name.c_str(), &hints, &
ai);
238 auto end = std::chrono::steady_clock::now();
239 auto duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
240 if (!getaddrinfo_timeout_error_shown && duration >= std::chrono::seconds(5)) {
241 Debug(net, 0,
"getaddrinfo() for address \"{}\" took {} seconds", this->
connection_string, duration.count());
242 Debug(net, 0,
" This is likely an issue in the DNS name resolver's configuration causing it to time out");
243 getaddrinfo_timeout_error_shown =
true;
272 if (this->
killed)
return true;
317 for (
const auto &socket : this->
sockets) {
318 FD_SET(socket, &write_fd);
324 int n = select(FD_SETSIZE,
nullptr, &write_fd,
nullptr, &tv);
335 if (std::chrono::steady_clock::now() < this->
last_attempt + std::chrono::milliseconds(250))
return false;
341 if (std::chrono::steady_clock::now() < this->
last_attempt + std::chrono::milliseconds(3000))
return false;
347 for (
const auto &socket : this->
sockets) {
360 SOCKET connected_socket = INVALID_SOCKET;
361 for (
auto it = this->
sockets.begin(); it != this->
sockets.end(); ) {
372 if (connected_socket == INVALID_SOCKET && FD_ISSET(*it, &write_fd)) {
373 connected_socket = *it;
380 if (connected_socket == INVALID_SOCKET)
return false;
383 for (
auto it = this->
sockets.begin(); it != this->
sockets.end(); ) {
384 if (connected_socket != *it) {
392 if (_debug_net_level >= 5) {
407 if (this->
killed)
return true;
442 assert(sock != INVALID_SOCKET);
466 [](
auto &connecter) {
return connecter->CheckActivity(); }),
@ SERVER_ADDRESS_INVITE_CODE
Server-address is based on an invite code.
@ SERVER_ADDRESS_DIRECT
Server-address is based on an hostname:port.
void ConnectToServer(std::string_view invite_code, TCPServerConnecter *connecter)
Join a server based on an invite code.
Wrapper for (un)resolved network addresses; there's no reason to transform a numeric IP to a string a...
static std::string_view AddressFamilyAsString(int family)
Convert the address family into a string.
static const std::string GetPeerName(SOCKET sock)
Get the peer name of a socket in string format.
static std::string_view SocketTypeAsString(int socktype)
Convert the socket type into a string.
uint16_t GetPort() const
Get the port.
const sockaddr_storage * GetAddress()
Get the address in its internal representation.
const std::string & GetHostname()
Get the hostname; in case it wasn't given the IPv4 dotted representation is given.
std::string GetAddressAsString(bool with_family=true)
Get the address as a string, e.g.
Abstraction of a network error where all implementation details of the error codes are encapsulated i...
std::string_view AsString() const
Get the string representation of the error message.
bool HasError() const
Check whether an error was actually set.
static NetworkError GetLast()
Get the last network error.
Address to a game server.
std::string connection_string
The connection string for this ServerAddress.
ServerAddressType type
The type of this ServerAddress.
"Helper" class for creating TCP connections in a non-blocking manner
NetworkAddress bind_address
Address we're binding to, if any.
void Kill()
Kill this connecter.
std::chrono::steady_clock::time_point last_attempt
Time we last tried to connect.
std::atomic< Status > status
The current status of the connecter.
std::string connection_string
Current address we are connecting to (before resolving).
static std::vector< std::shared_ptr< TCPConnecter > > connecters
List of connections that are currently being created.
std::vector< SOCKET > sockets
Pending connect() attempts.
static void CheckCallbacks()
Check whether we need to call the callback, i.e.
size_t current_address
Current index in addresses we are trying.
void Resolve()
Start resolving the hostname.
virtual void OnFailure()
Callback for when the connection attempt failed.
void OnResolved(addrinfo *ai)
Callback when resolving is done.
virtual bool CheckActivity()
Check if there was activity for this connecter.
std::map< SOCKET, NetworkAddress > sock_to_address
Mapping of a socket to the real address it is connecting to. USed for DEBUG statements.
static void ResolveThunk(TCPConnecter *connecter)
Thunk to start Resolve() on the right instance.
std::vector< addrinfo * > addresses
Addresses we can connect to.
std::thread resolve_thread
Thread used during resolving.
static void KillAll()
Kill all connection attempts.
bool TryNextAddress()
Start the connect() for the next address in the list.
std::atomic< bool > killed
Whether this connecter is marked as killed.
@ Connected
The connection is established.
@ Resolving
The hostname is being resolved (threaded).
@ Init
TCPConnecter is created but resolving hasn't started.
@ Failure
Resolving failed.
@ Connecting
We are currently connecting.
virtual void OnConnect(SOCKET s)
Callback when the connection succeeded.
void Connect(addrinfo *address)
Start a connection to the indicated address.
addrinfo * ai
getaddrinfo() allocated linked-list of resolved addresses.
int family
Family we are using to connect with.
void SetConnected(SOCKET sock)
The connection was successfully established.
bool CheckActivity() override
Check if there was activity for this connecter.
ServerAddress server_address
Address we are connecting to.
void SetFailure()
The connection couldn't be established.
TCPServerConnecter(std::string_view connection_string, uint16_t default_port)
Create a new connecter for the server.
SOCKET socket
The socket when a connection is established.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
NetworkAddress ParseConnectionString(std::string_view connection_string, uint16_t default_port)
Convert a string containing either "hostname" or "hostname:ip" to a NetworkAddress.
std::string NormalizeConnectionString(std::string_view connection_string, uint16_t default_port)
Normalize a connection string.
ClientNetworkCoordinatorSocketHandler _network_coordinator_client
The connection to the Game Coordinator.
bool SetReusePort(SOCKET d)
Try to set the socket to reuse ports.
bool SetNoDelay(SOCKET d)
Try to set the socket to not delay sending.
bool SetNonBlocking(SOCKET d)
Try to set the socket into non-blocking mode.
NetworkError GetSocketError(SOCKET d)
Get the error from a socket, if any.
Basic functions to receive and send TCP packets.
bool StartNewThread(std::thread *thr, std::string_view name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.