12 #include "../../stdafx.h"
13 #include "../../thread.h"
16 #include "../network_coordinator.h"
17 #include "../network_internal.h"
19 #include "../../safeguards.h"
29 TCPConnecter::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))
59 TCPConnecter::~TCPConnecter()
65 for (
const auto &socket : this->
sockets) {
68 this->sockets.clear();
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);
127 this->sockets.push_back(sock);
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;
307 if (this->sockets.empty()) {
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) {
351 this->sockets.clear();
361 SOCKET connected_socket = INVALID_SOCKET;
362 for (
auto it = this->sockets.begin(); it != this->sockets.end(); ) {
368 it = this->sockets.erase(it);
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) {
389 it = this->sockets.erase(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.
virtual void OnConnect([[maybe_unused]] SOCKET s)
Callback when the connection succeeded.
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.
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 SetNonBlocking([[maybe_unused]] SOCKET d)
Try to set the socket into non-blocking mode.
NetworkError GetSocketError(SOCKET d)
Get the error from a socket, if any.
bool SetNoDelay([[maybe_unused]] SOCKET d)
Try to set the socket to not delay sending.
Basic functions to receive and send TCP packets.
bool StartNewThread(std::thread *thr, const char *name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.