12#include "../../stdafx.h"
13#include "../../thread.h"
16#include "../network_coordinator.h"
17#include "../network_internal.h"
19#include "../../safeguards.h"
29TCPConnecter::TCPConnecter(
const std::string &connection_string, uint16_t default_port,
const NetworkAddress &bind_address,
int family) :
30 bind_address(bind_address),
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 memset(&hints, 0,
sizeof(hints));
227 hints.ai_family = AF_UNSPEC;
228 hints.ai_flags = AI_ADDRCONFIG;
229 hints.ai_socktype = SOCK_STREAM;
231 std::string port_name = std::to_string(address.
GetPort());
233 static bool getaddrinfo_timeout_error_shown =
false;
234 auto start = std::chrono::steady_clock::now();
237 int error = getaddrinfo(address.
GetHostname().c_str(), port_name.c_str(), &hints, &
ai);
239 auto end = std::chrono::steady_clock::now();
240 auto duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
241 if (!getaddrinfo_timeout_error_shown && duration >= std::chrono::seconds(5)) {
242 Debug(net, 0,
"getaddrinfo() for address \"{}\" took {} seconds", this->
connection_string, duration.count());
243 Debug(net, 0,
" This is likely an issue in the DNS name resolver's configuration causing it to time out");
244 getaddrinfo_timeout_error_shown =
true;
273 if (this->
killed)
return true;
318 for (
const auto &socket : this->
sockets) {
319 FD_SET(socket, &write_fd);
325 int n = select(FD_SETSIZE,
nullptr, &write_fd,
nullptr, &tv);
336 if (std::chrono::steady_clock::now() < this->
last_attempt + std::chrono::milliseconds(250))
return false;
342 if (std::chrono::steady_clock::now() < this->
last_attempt + std::chrono::milliseconds(3000))
return false;
348 for (
const auto &socket : this->
sockets) {
361 SOCKET connected_socket = INVALID_SOCKET;
362 for (
auto it = this->
sockets.begin(); it != this->
sockets.end(); ) {
373 if (connected_socket == INVALID_SOCKET && FD_ISSET(*it, &write_fd)) {
374 connected_socket = *it;
381 if (connected_socket == INVALID_SOCKET)
return false;
384 for (
auto it = this->
sockets.begin(); it != this->
sockets.end(); ) {
385 if (connected_socket != *it) {
393 if (_debug_net_level >= 5) {
408 if (this->
killed)
return true;
443 assert(sock != INVALID_SOCKET);
467 [](
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(const std::string &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 const char * AddressFamilyAsString(int family)
Convert the address family into a string.
static const char * SocketTypeAsString(int socktype)
Convert the socket type into a string.
static const std::string GetPeerName(SOCKET sock)
Get the peer name of a socket in string format.
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...
bool HasError() const
Check whether an error was actually set.
const std::string & AsString() const
Get the string representation of the error message.
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.
@ 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.
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.
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.
TCPServerConnecter(const std::string &connection_string, uint16_t default_port)
Create a new connecter for the server.
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.
SOCKET socket
The socket when a connection is established.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
std::string NormalizeConnectionString(const std::string &connection_string, uint16_t default_port)
Normalize a connection string.
NetworkAddress ParseConnectionString(const std::string &connection_string, uint16_t default_port)
Convert a string containing either "hostname" or "hostname:ip" to a NetworkAddress.
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, const char *name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.