Name resolution can be aborted. Fixed bug in socket_set_port.
This commit is contained in:
@@ -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
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
Reference in New Issue
Block a user