Name resolution can be aborted. Fixed bug in socket_set_port.

This commit is contained in:
lotodore
2007-02-25 17:08:58 +00:00
parent 954f997bcf
commit d63588f54b
7 changed files with 289 additions and 40 deletions
+1
View File
@@ -104,6 +104,7 @@ SOURCES += pokerth.cpp \
socket_startup.cpp \ socket_startup.cpp \
clientstate.cpp \ clientstate.cpp \
clientthread.cpp \ clientthread.cpp \
resolverthread.cpp \
clientdata.cpp \ clientdata.cpp \
clientcallback.cpp \ clientcallback.cpp \
socket_helper_cmn.cpp \ socket_helper_cmn.cpp \
+33 -6
View File
@@ -28,6 +28,7 @@
class ClientThread; class ClientThread;
class ClientCallback; class ClientCallback;
class ResolverThread;
class ClientState class ClientState
{ {
@@ -56,22 +57,48 @@ protected:
ClientStateInit(); ClientStateInit();
}; };
// State: Resolving name. // State: Starting name resolution.
class ClientStateResolve : public ClientState class ClientStateStartResolve : public ClientState
{ {
public: public:
// Access the state singleton. // 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); virtual int Process(ClientThread &client);
protected: protected:
// Protected constructor - this is a singleton. // 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. // State: Connecting to server.
+2 -1
View File
@@ -59,7 +59,8 @@ private:
friend class ClientStateInit; friend class ClientStateInit;
friend class ClientStateResolve; friend class ClientStateStartResolve;
friend class ClientStateResolving;
friend class ClientStateConnect; friend class ClientStateConnect;
}; };
+96 -31
View File
@@ -20,11 +20,11 @@
#include <net/clientstate.h> #include <net/clientstate.h>
#include <net/clientthread.h> #include <net/clientthread.h>
#include <net/clientdata.h> #include <net/clientdata.h>
#include <net/resolverthread.h>
#include <net/clientexception.h> #include <net/clientexception.h>
#include <net/socket_msg.h> #include <net/socket_msg.h>
#include <stdexcept> #include <stdexcept>
#include <sstream>
using namespace std; using namespace std;
@@ -65,37 +65,37 @@ ClientStateInit::Process(ClientThread &client)
if (!IS_VALID_SOCKET(data.sockfd)) if (!IS_VALID_SOCKET(data.sockfd))
throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO());
#if 0 // unsigned long mode = 1;
unsigned long mode = 1; // if (IOCTLSOCKET(data.sockfd, FIONBIO, &mode) == SOCKET_ERROR)
if (IOCTLSOCKET(data.sockfd, FIONBIO, &mode) == SOCKET_ERROR) // throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO());
throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO());
#endif
client.SetState(ClientStateResolve::Instance()); client.SetState(ClientStateStartResolve::Instance());
return MSG_SOCK_INIT_DONE; return MSG_SOCK_INIT_DONE;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
ClientStateResolve & ClientStateStartResolve &
ClientStateResolve::Instance() ClientStateStartResolve::Instance()
{ {
static ClientStateResolve state; static ClientStateStartResolve state;
return state; return state;
} }
ClientStateResolve::ClientStateResolve() ClientStateStartResolve::ClientStateStartResolve()
{ {
} }
ClientStateResolve::~ClientStateResolve() ClientStateStartResolve::~ClientStateStartResolve()
{ {
} }
int int
ClientStateResolve::Process(ClientThread &client) ClientStateStartResolve::Process(ClientThread &client)
{ {
int retVal;
ClientData &data = client.GetData(); ClientData &data = client.GetData();
data.clientAddr.ss_family = data.addrFamily; data.clientAddr.ss_family = data.addrFamily;
@@ -110,28 +110,93 @@ ClientStateResolve::Process(ClientThread &client)
// Success - but we still need to set the port. // Success - but we still need to set the port.
if (!socket_set_port(data.serverPort, data.addrFamily, (struct sockaddr *)&data.clientAddr, data.GetServerAddrSize())) if (!socket_set_port(data.serverPort, data.addrFamily, (struct sockaddr *)&data.clientAddr, data.GetServerAddrSize()))
throw ClientException(ERR_SOCK_SET_PORT_FAILED, 0); throw ClientException(ERR_SOCK_SET_PORT_FAILED, 0);
// No need to resolve - start connecting.
client.SetState(ClientStateConnect::Instance());
retVal = MSG_SOCK_RESOLVE_DONE;
} }
else else
{ {
// This did not work out - try name resolution. // Start name resolution in a separate thread, since it is blocking
ostringstream tmpStr; // for up to about 30 seconds.
tmpStr << data.serverPort; std::auto_ptr<ResolverThread> resolver(new ResolverThread);
// TODO: start in a separate thread, since it is blocking. resolver->Init(data);
if (!socket_resolve( resolver->Run();
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());
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;
}
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
+94
View File
@@ -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 <net/resolverthread.h>
#include <net/clientdata.h>
#include <net/clientexception.h>
#include <sstream>
#include <cassert>
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;
}
+2 -2
View File
@@ -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)) 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; retVal = true;
} }
else if (addrFamily == AF_INET6 && addrLen >= (int)sizeof(sockaddr_in6)) 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; retVal = true;
} }
} }
+61
View File
@@ -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 <core/thread.h>
#include <string>
#include <memory>
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<ClientData> m_data;
bool m_retVal;
};
#endif