(svn r15163) -Change/Fix: use a non-blocking method to resolve the hostname and connect to game servers.
This commit is contained in:
30
src/network/core/address.cpp
Normal file
30
src/network/core/address.cpp
Normal file
@@ -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 */
|
103
src/network/core/address.h
Normal file
103
src/network/core/address.h
Normal file
@@ -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 */
|
@@ -201,5 +201,4 @@ bool NetworkTCPSocketHandler::IsPacketQueueEmpty()
|
||||
return this->packet_queue == NULL;
|
||||
}
|
||||
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
@@ -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 */
|
||||
|
99
src/network/core/tcp_connect.cpp
Normal file
99
src/network/core/tcp_connect.cpp
Normal file
@@ -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 */
|
Reference in New Issue
Block a user