summaryrefslogtreecommitdiff
path: root/src/network/core
diff options
context:
space:
mode:
authorrubidium <rubidium@openttd.org>2009-01-20 11:28:18 +0000
committerrubidium <rubidium@openttd.org>2009-01-20 11:28:18 +0000
commit206841ba5f526270a37ab002ebbae6bd533dedad (patch)
tree6b344cc0bfeff2bffba2764265342a0ed65238d2 /src/network/core
parent12ff4cf083704088ef88ec178d81c37ca60327e0 (diff)
downloadopenttd-206841ba5f526270a37ab002ebbae6bd533dedad.tar.xz
(svn r15163) -Change/Fix: use a non-blocking method to resolve the hostname and connect to game servers.
Diffstat (limited to 'src/network/core')
-rw-r--r--src/network/core/address.cpp30
-rw-r--r--src/network/core/address.h103
-rw-r--r--src/network/core/tcp.cpp1
-rw-r--r--src/network/core/tcp.h57
-rw-r--r--src/network/core/tcp_connect.cpp99
5 files changed, 289 insertions, 1 deletions
diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp
new file mode 100644
index 000000000..637f0fe7b
--- /dev/null
+++ b/src/network/core/address.cpp
@@ -0,0 +1,30 @@
+/* $Id$ */
+
+/** @file core/address.cpp Implementation of the address. */
+
+#include "../../stdafx.h"
+
+#ifdef ENABLE_NETWORK
+
+#include "address.h"
+#include "host.h"
+
+const char *NetworkAddress::GetHostname() const
+{
+ if (this->hostname != NULL) return this->hostname;
+
+ in_addr addr;
+ addr.s_addr = this->ip;
+ return inet_ntoa(addr);
+}
+
+uint32 NetworkAddress::GetIP()
+{
+ if (!this->resolved) {
+ this->ip = NetworkResolveHost(this->hostname);
+ this->resolved = true;
+ }
+ return this->ip;
+}
+
+#endif /* ENABLE_NETWORK */
diff --git a/src/network/core/address.h b/src/network/core/address.h
new file mode 100644
index 000000000..62e79f3de
--- /dev/null
+++ b/src/network/core/address.h
@@ -0,0 +1,103 @@
+/* $Id$ */
+
+/** @file core/address.h Wrapper for network addresses. */
+
+#ifndef NETWORK_ADDRESS_H
+#define NETWORK_ADDRESS_H
+
+#ifdef ENABLE_NETWORK
+
+#include "os_abstraction.h"
+
+/**
+ * Wrapper for (un)resolved network addresses; there's no reason to transform
+ * a numeric IP to a string and then back again to pass it to functions. It
+ * furthermore allows easier delaying of the hostname lookup.
+ */
+class NetworkAddress {
+private:
+ bool resolved; ///< Has the IP address been resolved
+ char *hostname; ///< The hostname, NULL if there isn't one
+ uint32 ip; ///< The resolved IP address
+ uint16 port; ///< The port associated with the address
+
+public:
+ /**
+ * Create a network address based on a resolved IP and port
+ * @param ip the resolved ip
+ * @param port the port
+ */
+ NetworkAddress(in_addr_t ip, uint16 port) :
+ resolved(true),
+ hostname(NULL),
+ ip(ip),
+ port(port)
+ {
+ }
+
+ /**
+ * Create a network address based on a unresolved host and port
+ * @param ip the unresolved hostname
+ * @param port the port
+ */
+ NetworkAddress(const char *hostname, uint16 port) :
+ resolved(false),
+ hostname(strdup(hostname)),
+ ip(0),
+ port(port)
+ {
+ }
+
+ /**
+ * Make a clone of another address
+ * @param address the address to clone
+ */
+ NetworkAddress(const NetworkAddress &address) :
+ resolved(address.resolved),
+ hostname(address.hostname == NULL ? NULL : strdup(address.hostname)),
+ ip(address.ip),
+ port(address.port)
+ {
+ }
+
+ /** Clean up our mess */
+ ~NetworkAddress()
+ {
+ free(hostname);
+ }
+
+ /**
+ * Get the hostname; in case it wasn't given the
+ * IPv4 dotted representation is given.
+ * @return the hostname
+ */
+ const char *GetHostname() const;
+
+ /**
+ * Get the IP address. If the IP has not been resolved yet this will resolve
+ * it possibly blocking this function for a while
+ * @return the IP address
+ */
+ uint32 GetIP();
+
+ /**
+ * Get the port
+ * @return the port
+ */
+ uint16 GetPort() const
+ {
+ return this->port;
+ }
+
+ /**
+ * Check whether the IP address has been resolved already
+ * @return true iff the port has been resolved
+ */
+ bool IsResolved() const
+ {
+ return this->resolved;
+ }
+};
+
+#endif /* ENABLE_NETWORK */
+#endif /* NETWORK_ADDRESS_H */
diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp
index 08609ff20..9c58af2c0 100644
--- a/src/network/core/tcp.cpp
+++ b/src/network/core/tcp.cpp
@@ -201,5 +201,4 @@ bool NetworkTCPSocketHandler::IsPacketQueueEmpty()
return this->packet_queue == NULL;
}
-
#endif /* ENABLE_NETWORK */
diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h
index da3a55adf..962ea45e5 100644
--- a/src/network/core/tcp.h
+++ b/src/network/core/tcp.h
@@ -10,6 +10,7 @@
#ifdef ENABLE_NETWORK
#include "os_abstraction.h"
+#include "address.h"
#include "core.h"
#include "packet.h"
@@ -31,6 +32,62 @@ public:
~NetworkTCPSocketHandler();
};
+/**
+ * "Helper" class for creating TCP connections in a non-blocking manner
+ */
+class TCPConnecter {
+private:
+ class ThreadObject *thread; ///< Thread used to create the TCP connection
+ bool connected; ///< Whether we succeeded in making the connection
+ bool aborted; ///< Whether we bailed out (i.e. connection making failed)
+ bool killed; ///< Whether we got killed
+ SOCKET sock; ///< The socket we're connecting with
+
+ /** The actual connection function */
+ void Connect();
+
+ /**
+ * Entry point for the new threads.
+ * @param param the TCPConnecter instance to call Connect on.
+ */
+ static void ThreadEntry(void *param);
+
+protected:
+ /** Address we're connecting to */
+ NetworkAddress address;
+
+public:
+ /**
+ * Create a new connecter for the given address
+ * @param address the (un)resolved address to connect to
+ */
+ TCPConnecter(const NetworkAddress &address);
+ /** Silence the warnings */
+ virtual ~TCPConnecter() {}
+
+ /**
+ * Callback when the connection succeeded.
+ * @param s the socket that we opened
+ */
+ virtual void OnConnect(SOCKET s) {}
+
+ /**
+ * Callback for when the connection attempt failed.
+ */
+ virtual void OnFailure() {}
+
+ /**
+ * Check whether we need to call the callback, i.e. whether we
+ * have connected or aborted and call the appropriate callback
+ * for that. It's done this way to ease on the locking that
+ * would otherwise be needed everywhere.
+ */
+ static void CheckCallbacks();
+
+ /** Kill all connection attempts. */
+ static void KillAll();
+};
+
#endif /* ENABLE_NETWORK */
#endif /* NETWORK_CORE_TCP_H */
diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp
new file mode 100644
index 000000000..88501b260
--- /dev/null
+++ b/src/network/core/tcp_connect.cpp
@@ -0,0 +1,99 @@
+/* $Id$ */
+
+/**
+ * @file tcp_connect.cpp Basic functions to create connections without blocking.
+ */
+
+#ifdef ENABLE_NETWORK
+
+#include "../../stdafx.h"
+#include "../../debug.h"
+#include "../../core/smallvec_type.hpp"
+#include "../../thread.h"
+
+#include "tcp.h"
+
+/** List of connections that are currently being created */
+static SmallVector<TCPConnecter *, 1> _tcp_connecters;
+
+TCPConnecter::TCPConnecter(const NetworkAddress &address) :
+ connected(false),
+ aborted(false),
+ killed(false),
+ sock(INVALID_SOCKET),
+ address(address)
+{
+ *_tcp_connecters.Append() = this;
+ if (!ThreadObject::New(TCPConnecter::ThreadEntry, this, &this->thread)) {
+ this->Connect();
+ }
+}
+
+void TCPConnecter::Connect()
+{
+ DEBUG(net, 1, "Connecting to %s %d", address.GetHostname(), address.GetPort());
+
+ this->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (this->sock == INVALID_SOCKET) {
+ this->aborted = true;
+ return;
+ }
+
+ if (!SetNoDelay(this->sock)) DEBUG(net, 1, "Setting TCP_NODELAY failed");
+
+ struct sockaddr_in sin;
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = address.GetIP();
+ sin.sin_port = htons(address.GetPort());
+
+ /* We failed to connect for which reason what so ever */
+ if (connect(this->sock, (struct sockaddr*) &sin, sizeof(sin)) != 0) {
+ closesocket(this->sock);
+ this->sock = INVALID_SOCKET;
+ this->aborted = true;
+ return;
+ }
+
+ if (!SetNonBlocking(this->sock)) DEBUG(net, 0, "Setting non-blocking mode failed");
+
+ this->connected = true;
+}
+
+
+/* static */ void TCPConnecter::ThreadEntry(void *param)
+{
+ static_cast<TCPConnecter*>(param)->Connect();
+}
+
+/* static */ void TCPConnecter::CheckCallbacks()
+{
+ for (TCPConnecter **iter = _tcp_connecters.Begin(); iter < _tcp_connecters.End(); /* nothing */) {
+ TCPConnecter *cur = *iter;
+ if ((cur->connected || cur->aborted) && cur->killed) {
+ _tcp_connecters.Erase(iter);
+ if (cur->sock != INVALID_SOCKET) closesocket(cur->sock);
+ delete cur;
+ continue;
+ }
+ if (cur->connected) {
+ _tcp_connecters.Erase(iter);
+ cur->OnConnect(cur->sock);
+ delete cur;
+ continue;
+ }
+ if (cur->aborted) {
+ _tcp_connecters.Erase(iter);
+ cur->OnFailure();
+ delete cur;
+ continue;
+ }
+ iter++;
+ }
+}
+
+/* static */ void TCPConnecter::KillAll()
+{
+ for (TCPConnecter **iter = _tcp_connecters.Begin(); iter != _tcp_connecters.End(); iter++) (*iter)->killed = true;
+}
+
+#endif /* ENABLE_NETWORK */