OpenTTD Source
12.0-beta2
|
Go to the documentation of this file.
12 #include "../../stdafx.h"
13 #include "../../thread.h"
16 #include "../network_coordinator.h"
17 #include "../network_internal.h"
21 #include "../../safeguards.h"
32 TCPConnecter::TCPConnecter(
const std::string &connection_string, uint16 default_port,
NetworkAddress bind_address,
int family) :
33 bind_address(bind_address),
47 server_address(
ServerAddress::Parse(connection_string, default_port))
66 TCPConnecter::~TCPConnecter()
72 for (
const auto &socket : this->
sockets) {
75 this->sockets.clear();
78 if (this->
ai !=
nullptr) freeaddrinfo(this->
ai);
97 SOCKET sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
98 if (sock == INVALID_SOCKET) {
108 if (bind(sock, (
const sockaddr *)this->
bind_address.
GetAddress(), this->bind_address.GetAddressLength()) != 0) {
125 int err = connect(sock, address->ai_addr, (
int)address->ai_addrlen);
134 this->sockets.push_back(sock);
157 std::deque<addrinfo *> addresses_ipv4, addresses_ipv6;
162 bool seen_ipv6 =
false;
164 for (addrinfo *runp =
ai; runp !=
nullptr; runp = runp->ai_next) {
165 if (runp->ai_family == AF_INET6) {
167 }
else if (!seen_ipv6) {
177 for (addrinfo *runp =
ai; runp !=
nullptr; runp = runp->ai_next) {
179 if (this->
family != AF_UNSPEC && this->
family != runp->ai_family)
continue;
182 if (runp->ai_family == AF_INET6) {
183 addresses_ipv6.emplace_back(runp);
185 addresses_ipv4.emplace_back(runp);
195 while (!addresses_ipv4.empty() || !addresses_ipv6.empty()) {
196 if (!addresses_ipv6.empty()) {
197 this->
addresses.push_back(addresses_ipv6.front());
198 addresses_ipv6.pop_front();
200 if (!addresses_ipv4.empty()) {
201 this->
addresses.push_back(addresses_ipv4.front());
202 addresses_ipv4.pop_front();
207 if (_debug_net_level >= 6) {
209 for (
const auto &address : this->
addresses) {
229 memset(&hints, 0,
sizeof(hints));
230 hints.ai_family = AF_UNSPEC;
231 hints.ai_flags = AI_ADDRCONFIG;
232 hints.ai_socktype = SOCK_STREAM;
237 static bool getaddrinfo_timeout_error_shown =
false;
238 auto start = std::chrono::steady_clock::now();
243 auto end = std::chrono::steady_clock::now();
244 auto duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
245 if (!getaddrinfo_timeout_error_shown && duration >= std::chrono::seconds(5)) {
246 Debug(net, 0,
"getaddrinfo() for address \"{}\" took {} seconds", this->
connection_string, duration.count());
247 Debug(net, 0,
" This is likely an issue in the DNS name resolver's configuration causing it to time out");
248 getaddrinfo_timeout_error_shown =
true;
277 if (this->
killed)
return true;
311 if (this->sockets.empty()) {
322 for (
const auto &socket : this->sockets) {
323 FD_SET(socket, &write_fd);
329 int n = select(FD_SETSIZE,
nullptr, &write_fd,
nullptr, &tv);
340 if (std::chrono::steady_clock::now() < this->
last_attempt + std::chrono::milliseconds(250))
return false;
346 if (std::chrono::steady_clock::now() < this->
last_attempt + std::chrono::milliseconds(3000))
return false;
352 for (
const auto &socket : this->sockets) {
355 this->sockets.clear();
363 for (
auto it = this->sockets.begin(); it != this->sockets.end(); ) {
369 it = this->sockets.erase(it);
376 if (this->sockets.empty()) {
387 SOCKET connected_socket = INVALID_SOCKET;
388 for (
auto it = this->sockets.begin(); it != this->sockets.end(); ) {
389 if (connected_socket == INVALID_SOCKET && FD_ISSET(*it, &write_fd)) {
390 connected_socket = *it;
395 it = this->sockets.erase(it);
397 assert(connected_socket != INVALID_SOCKET);
400 if (_debug_net_level >= 5) {
415 if (this->
killed)
return true;
bool CheckActivity() override
Check if there was activity for this connecter.
static void CheckCallbacks()
Check whether we need to call the callback, i.e.
virtual void OnConnect(SOCKET s)
Callback when the connection succeeded.
std::vector< SOCKET > sockets
Pending connect() attempts.
std::string connection_string
Current address we are connecting to (before resolving).
@ CONNECTED
The connection is established.
const sockaddr_storage * GetAddress()
Get the address in its internal representation.
bool SetReusePort(SOCKET d)
Try to set the socket to reuse ports.
"Helper" class for creating TCP connections in a non-blocking manner
std::chrono::steady_clock::time_point last_attempt
Time we last tried to connect.
NetworkAddress ParseConnectionString(const std::string &connection_string, uint16 default_port)
Convert a string containing either "hostname" or "hostname:ip" to a NetworkAddress.
void Kill()
Kill this connecter.
int family
Family we are using to connect with.
bool HasError() const
Check whether an error was actually set.
addrinfo * ai
getaddrinfo() allocated linked-list of resolved addresses.
void SetFailure()
The connection couldn't be established.
ServerAddressType type
The type of this ServerAddress.
static const std::string GetPeerName(SOCKET sock)
Get the peer name of a socket in string format.
void Resolve()
Start resolving the hostname.
void ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter)
Join a server based on an invite code.
static std::vector< TCPConnecter * > _tcp_connecters
List of connections that are currently being created.
std::string NormalizeConnectionString(const std::string &connection_string, uint16 default_port)
Normalize a connection string.
void Connect(addrinfo *address)
Start a connection to the indicated address.
std::atomic< bool > killed
Whether this connecter is marked as killed.
static NetworkError GetLast()
Get the last network error.
@ SERVER_ADDRESS_DIRECT
Server-address is based on an hostname:port.
@ RESOLVING
The hostname is being resolved (threaded).
bool StartNewThread(std::thread *thr, const char *name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
uint16 GetPort() const
Get the port.
std::string GetAddressAsString(bool with_family=true)
Get the address as a string, e.g.
static void KillAll()
Kill all connection attempts.
NetworkError GetSocketError(SOCKET d)
Get the error from a socket, if any.
std::vector< addrinfo * > addresses
Addresses we can connect to.
ServerAddress server_address
Address we are connecting to.
virtual bool CheckActivity()
Check if there was activity for this connecter.
Wrapper for (un)resolved network addresses; there's no reason to transform a numeric IP to a string a...
void SetConnected(SOCKET sock)
The connection was successfully established.
size_t current_address
Current index in addresses we are trying.
ClientNetworkCoordinatorSocketHandler _network_coordinator_client
The connection to the Game Coordinator.
std::map< SOCKET, NetworkAddress > sock_to_address
Mapping of a socket to the real address it is connecting to. USed for DEBUG statements.
const std::string & GetHostname()
Get the hostname; in case it wasn't given the IPv4 dotted representation is given.
const std::string & AsString() const
Get the string representation of the error message.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Abstraction of a network error where all implementation details of the error codes are encapsulated i...
virtual void OnFailure()
Callback for when the connection attempt failed.
static const char * SocketTypeAsString(int socktype)
Convert the socket type into a string.
@ SERVER_ADDRESS_INVITE_CODE
Server-address is based on an invite code.
void CDECL error(const char *s,...)
Error handling for fatal non-user errors.
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
bool SetNonBlocking(SOCKET d)
Try to set the socket into non-blocking mode.
void OnResolved(addrinfo *ai)
Callback when resolving is done.
SOCKET socket
The socket when a connection is established.
@ FAILURE
Resolving failed.
static const char * AddressFamilyAsString(int family)
Convert the address family into a string.
std::string connection_string
The connection string for this ServerAddress.
#define lastof(x)
Get the last element of an fixed size array.
NetworkAddress bind_address
Address we're binding to, if any.
bool SetNoDelay(SOCKET d)
Try to set the socket to not delay sending.
@ CONNECTING
We are currently connecting.
Address to a game server.
TCPServerConnecter(const std::string &connection_string, uint16 default_port)
Create a new connecter for the server.
std::atomic< Status > status
The current status of the connecter.
static void ResolveThunk(TCPConnecter *connecter)
Thunk to start Resolve() on the right instance.
@ INIT
TCPConnecter is created but resolving hasn't started.
std::thread resolve_thread
Thread used during resolving.
bool TryNextAddress()
Start the connect() for the next address in the list.