10#include "../../stdafx.h"
11#include "../../thread.h"
14#include "../network_coordinator.h"
15#include "../network_internal.h"
17#include "../../safeguards.h"
27TCPConnecter::TCPConnecter(std::string_view connection_string, uint16_t default_port,
const NetworkAddress &bind_address,
int family) :
28 bind_address(bind_address),
40 server_address(
ServerAddress::Parse(connection_string, default_port))
57TCPConnecter::~TCPConnecter()
63 for (
const auto &socket : this->
sockets) {
69 if (this->
ai !=
nullptr) freeaddrinfo(this->
ai);
88 SOCKET sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
89 if (sock == INVALID_SOCKET) {
99 if (bind(sock, (
const sockaddr *)this->
bind_address.
GetAddress(), this->bind_address.GetAddressLength()) != 0) {
116 int err = connect(sock, address->ai_addr, (
int)address->ai_addrlen);
148 std::deque<addrinfo *> addresses_ipv4, addresses_ipv6;
153 bool seen_ipv6 =
false;
155 for (addrinfo *runp =
ai; runp !=
nullptr; runp = runp->ai_next) {
156 if (runp->ai_family == AF_INET6) {
158 }
else if (!seen_ipv6) {
168 for (addrinfo *runp =
ai; runp !=
nullptr; runp = runp->ai_next) {
170 if (this->
family != AF_UNSPEC && this->
family != runp->ai_family)
continue;
173 if (runp->ai_family == AF_INET6) {
174 addresses_ipv6.emplace_back(runp);
176 addresses_ipv4.emplace_back(runp);
186 while (!addresses_ipv4.empty() || !addresses_ipv6.empty()) {
187 if (!addresses_ipv6.empty()) {
188 this->
addresses.push_back(addresses_ipv6.front());
189 addresses_ipv6.pop_front();
191 if (!addresses_ipv4.empty()) {
192 this->
addresses.push_back(addresses_ipv4.front());
193 addresses_ipv4.pop_front();
198 if (_debug_net_level >= 6) {
203 for (
const auto &address : this->
addresses) {
224 hints.ai_family = AF_UNSPEC;
225 hints.ai_flags = AI_ADDRCONFIG;
226 hints.ai_socktype = SOCK_STREAM;
228 std::string port_name = fmt::format(
"{}", address.
GetPort());
230 static bool getaddrinfo_timeout_error_shown =
false;
231 auto start = std::chrono::steady_clock::now();
234 int error = getaddrinfo(address.
GetHostname().c_str(), port_name.c_str(), &hints, &
ai);
236 auto end = std::chrono::steady_clock::now();
237 auto duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
238 if (!getaddrinfo_timeout_error_shown && duration >= std::chrono::seconds(5)) {
239 Debug(net, 0,
"getaddrinfo() for address \"{}\" took {} seconds", this->
connection_string, duration.count());
240 Debug(net, 0,
" This is likely an issue in the DNS name resolver's configuration causing it to time out");
241 getaddrinfo_timeout_error_shown =
true;
270 if (this->
killed)
return true;
315 for (
const auto &socket : this->
sockets) {
316 FD_SET(socket, &write_fd);
322 int n = select(FD_SETSIZE,
nullptr, &write_fd,
nullptr, &tv);
333 if (std::chrono::steady_clock::now() < this->
last_attempt + std::chrono::milliseconds(250))
return false;
339 if (std::chrono::steady_clock::now() < this->
last_attempt + std::chrono::milliseconds(3000))
return false;
345 for (
const auto &socket : this->
sockets) {
358 SOCKET connected_socket = INVALID_SOCKET;
359 for (
auto it = this->
sockets.begin(); it != this->
sockets.end(); ) {
370 if (connected_socket == INVALID_SOCKET && FD_ISSET(*it, &write_fd)) {
371 connected_socket = *it;
378 if (connected_socket == INVALID_SOCKET)
return false;
381 for (
auto it = this->
sockets.begin(); it != this->
sockets.end(); ) {
382 if (connected_socket != *it) {
390 if (_debug_net_level >= 5) {
405 if (this->
killed)
return true;
440 assert(sock != INVALID_SOCKET);
464 [](
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.