diff --git a/pokerth.pro b/pokerth.pro index f323d98d..6486bfe6 100755 --- a/pokerth.pro +++ b/pokerth.pro @@ -104,6 +104,7 @@ SOURCES += pokerth.cpp \ socket_startup.cpp \ clientstate.cpp \ clientthread.cpp \ + resolverthread.cpp \ clientdata.cpp \ clientcallback.cpp \ socket_helper_cmn.cpp \ diff --git a/src/net/clientstate.h b/src/net/clientstate.h index b0cf87c1..1c9e6bb2 100644 --- a/src/net/clientstate.h +++ b/src/net/clientstate.h @@ -28,6 +28,7 @@ class ClientThread; class ClientCallback; +class ResolverThread; class ClientState { @@ -56,22 +57,48 @@ protected: ClientStateInit(); }; -// State: Resolving name. -class ClientStateResolve : public ClientState +// State: Starting name resolution. +class ClientStateStartResolve : public ClientState { public: // Access the state singleton. - static ClientStateResolve &Instance(); + static ClientStateStartResolve &Instance(); - virtual ~ClientStateResolve(); + virtual ~ClientStateStartResolve(); - // "Poll" for the completion of the name resolution. + // Initiate the name resolution. virtual int Process(ClientThread &client); protected: // Protected constructor - this is a singleton. - ClientStateResolve(); + ClientStateStartResolve(); +}; + +// State: Name resolution. +class ClientStateResolving : public ClientState +{ +public: + // Access the state singleton. + static ClientStateResolving &Instance(); + + virtual ~ClientStateResolving(); + + void SetResolver(ResolverThread *resolver); + + // Poll for the completion of the name resolution. + virtual int Process(ClientThread &client); + +protected: + + // Protected constructor - this is a singleton. + ClientStateResolving(); + + void Cleanup(); + +private: + + ResolverThread *m_resolver; }; // State: Connecting to server. diff --git a/src/net/clientthread.h b/src/net/clientthread.h index cf4c1037..2d21ddd3 100644 --- a/src/net/clientthread.h +++ b/src/net/clientthread.h @@ -59,7 +59,8 @@ private: friend class ClientStateInit; -friend class ClientStateResolve; +friend class ClientStateStartResolve; +friend class ClientStateResolving; friend class ClientStateConnect; }; diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index 60ac83cd..df05800b 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -20,11 +20,11 @@ #include #include #include +#include #include #include #include -#include using namespace std; @@ -65,37 +65,37 @@ ClientStateInit::Process(ClientThread &client) if (!IS_VALID_SOCKET(data.sockfd)) throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); -#if 0 - unsigned long mode = 1; - if (IOCTLSOCKET(data.sockfd, FIONBIO, &mode) == SOCKET_ERROR) - throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); -#endif +// unsigned long mode = 1; +// if (IOCTLSOCKET(data.sockfd, FIONBIO, &mode) == SOCKET_ERROR) +// throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); - client.SetState(ClientStateResolve::Instance()); + client.SetState(ClientStateStartResolve::Instance()); return MSG_SOCK_INIT_DONE; } //----------------------------------------------------------------------------- -ClientStateResolve & -ClientStateResolve::Instance() +ClientStateStartResolve & +ClientStateStartResolve::Instance() { - static ClientStateResolve state; + static ClientStateStartResolve state; return state; } -ClientStateResolve::ClientStateResolve() +ClientStateStartResolve::ClientStateStartResolve() { } -ClientStateResolve::~ClientStateResolve() +ClientStateStartResolve::~ClientStateStartResolve() { } int -ClientStateResolve::Process(ClientThread &client) +ClientStateStartResolve::Process(ClientThread &client) { + int retVal; + ClientData &data = client.GetData(); data.clientAddr.ss_family = data.addrFamily; @@ -110,28 +110,93 @@ ClientStateResolve::Process(ClientThread &client) // Success - but we still need to set the port. if (!socket_set_port(data.serverPort, data.addrFamily, (struct sockaddr *)&data.clientAddr, data.GetServerAddrSize())) throw ClientException(ERR_SOCK_SET_PORT_FAILED, 0); + + // No need to resolve - start connecting. + client.SetState(ClientStateConnect::Instance()); + retVal = MSG_SOCK_RESOLVE_DONE; } else { - // This did not work out - try name resolution. - ostringstream tmpStr; - tmpStr << data.serverPort; - // TODO: start in a separate thread, since it is blocking. - if (!socket_resolve( - data.serverAddr.c_str(), - tmpStr.str().c_str(), - data.addrFamily, - SOCK_STREAM, - 0, - (struct sockaddr *)&data.clientAddr, - data.GetServerAddrSize())) - { - throw ClientException(ERR_SOCK_RESOLVE_FAILED, 0); - } - } - client.SetState(ClientStateConnect::Instance()); + // Start name resolution in a separate thread, since it is blocking + // for up to about 30 seconds. + std::auto_ptr resolver(new ResolverThread); + resolver->Init(data); + resolver->Run(); - return MSG_SOCK_RESOLVE_DONE; + ClientStateResolving::Instance().SetResolver(resolver.release()); + client.SetState(ClientStateResolving::Instance()); + + retVal = MSG_SOCK_INTERNAL_PENDING; + } + + return retVal; +} + +//----------------------------------------------------------------------------- + +ClientStateResolving & +ClientStateResolving::Instance() +{ + static ClientStateResolving state; + return state; +} + +ClientStateResolving::ClientStateResolving() +: m_resolver(NULL) +{ +} + +ClientStateResolving::~ClientStateResolving() +{ + Cleanup(); +} + +void +ClientStateResolving::SetResolver(ResolverThread *resolver) +{ + Cleanup(); + + m_resolver = resolver; +} + +int +ClientStateResolving::Process(ClientThread &client) +{ + int retVal; + + if (!m_resolver) + throw ClientException(ERR_SOCK_RESOLVE_FAILED, 0); + + if (m_resolver->Join(100)) + { + ClientData &data = client.GetData(); + bool success = m_resolver->GetResult(data); + Cleanup(); // Not required, but better keep things clean. + + if (!success) + throw ClientException(ERR_SOCK_RESOLVE_FAILED, 0); + + client.SetState(ClientStateConnect::Instance()); + retVal = MSG_SOCK_RESOLVE_DONE; + } + else + retVal = MSG_SOCK_INTERNAL_PENDING; + + return retVal; +} + + +void +ClientStateResolving::Cleanup() +{ + if (m_resolver) + { + if (m_resolver->Join(500)) + delete m_resolver; + // If the resolver does not terminate fast enough, leave it + // as memory leak. + m_resolver = NULL; + } } //----------------------------------------------------------------------------- diff --git a/src/net/common/resolverthread.cpp b/src/net/common/resolverthread.cpp new file mode 100644 index 00000000..257c1b4a --- /dev/null +++ b/src/net/common/resolverthread.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2007 by Lothar May * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include + +#include +#include + +using namespace std; + + +ResolverThread::ResolverThread() +: m_retVal(false) +{ + m_data.reset(new ClientData); +} + +ResolverThread::~ResolverThread() +{ +} + +void +ResolverThread::Init(const ClientData &data) +{ + if (IsRunning()) + return; // TODO: throw exception + + *m_data = data; +} + +bool +ResolverThread::GetResult(ClientData &data) +{ + if (IsRunning()) + return false; // TODO: throw exception + + if (m_retVal) + memcpy(&data.clientAddr, &GetData().clientAddr, GetData().GetServerAddrSize()); + + return m_retVal; +} + +void +ResolverThread::Main() +{ + const ClientData &data = GetData(); + + // Convert the port to a string. + ostringstream tmpStr; + tmpStr << data.serverPort; + + // Start the name resolution. + m_retVal = socket_resolve( + data.serverAddr.c_str(), + tmpStr.str().c_str(), + data.addrFamily, + SOCK_STREAM, + 0, + (struct sockaddr *)&data.clientAddr, + data.GetServerAddrSize()); +} + +const ClientData & +ResolverThread::GetData() const +{ + assert(m_data.get()); + return *m_data; +} + +ClientData & +ResolverThread::GetData() +{ + assert(m_data.get()); + return *m_data; +} + diff --git a/src/net/common/socket_helper_cmn.cpp b/src/net/common/socket_helper_cmn.cpp index 3d068e52..33afb0e6 100644 --- a/src/net/common/socket_helper_cmn.cpp +++ b/src/net/common/socket_helper_cmn.cpp @@ -31,12 +31,12 @@ socket_set_port(unsigned port, int addrFamily, struct sockaddr *addr, int addrLe { if (addrFamily == AF_INET && addrLen >= (int)sizeof(sockaddr_in)) { - ((sockaddr_in *)&addr)->sin_port = htons(port); + ((sockaddr_in *)addr)->sin_port = htons(port); retVal = true; } else if (addrFamily == AF_INET6 && addrLen >= (int)sizeof(sockaddr_in6)) { - ((sockaddr_in6 *)&addr)->sin6_port = htons(port); + ((sockaddr_in6 *)addr)->sin6_port = htons(port); retVal = true; } } diff --git a/src/net/resolverthread.h b/src/net/resolverthread.h new file mode 100644 index 00000000..b0923127 --- /dev/null +++ b/src/net/resolverthread.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2007 by Lothar May * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +/* Name resolution thread. */ + +#ifndef _RESOLVERTHREAD_H_ +#define _RESOLVERTHREAD_H_ + +#include +#include +#include + +class ClientData; + +class ResolverThread : public Thread +{ +public: + ResolverThread(); + virtual ~ResolverThread(); + + // Set the parameters. Does not do any error checking. + // To prevent access faults if this thread cannot be + // terminated, the data is not modified. + void Init(const ClientData &data); + + // Retrieve the result of the name resolution. + // ONLY CALL THIS FUNCTION AFTER THE THREAD TERMINATED. + // You have been warned... + bool GetResult(ClientData &data); + +protected: + + // Main function of the thread. + virtual void Main(); + + const ClientData &GetData() const; + ClientData &GetData(); + +private: + + std::auto_ptr m_data; + bool m_retVal; +}; + +#endif +