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
+33 -6
View File
@@ -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.
+2 -1
View File
@@ -59,7 +59,8 @@ private:
friend class ClientStateInit;
friend class ClientStateResolve;
friend class ClientStateStartResolve;
friend class ClientStateResolving;
friend class ClientStateConnect;
};
+96 -31
View File
@@ -20,11 +20,11 @@
#include <net/clientstate.h>
#include <net/clientthread.h>
#include <net/clientdata.h>
#include <net/resolverthread.h>
#include <net/clientexception.h>
#include <net/socket_msg.h>
#include <stdexcept>
#include <sstream>
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<ResolverThread> 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;
}
}
//-----------------------------------------------------------------------------
+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))
{
((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;
}
}
+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