From 9ec3e020dd8a6e0d2aaf71099747745e1bb1ddb1 Mon Sep 17 00:00:00 2001 From: lotodore Date: Sun, 18 Feb 2007 11:26:57 +0000 Subject: [PATCH] Cleaned up the code. Added client exception for error handling. --- src/core/thread.cpp | 31 ++++++++--- src/core/thread.h | 3 ++ src/net/clientdata.h | 4 +- src/net/clientexception.h | 40 +++++++++++++++ src/net/clientstate.h | 10 ++-- src/net/common/clientdata.cpp | 33 ++++++++++++ src/net/common/clientstate.cpp | 91 +++++++++++++++------------------ src/net/common/clientthread.cpp | 28 +++++++--- src/net/socket_msg.h | 4 ++ 9 files changed, 173 insertions(+), 71 deletions(-) create mode 100644 src/net/clientexception.h create mode 100644 src/net/common/clientdata.cpp diff --git a/src/core/thread.cpp b/src/core/thread.cpp index d3631b21..db259b67 100644 --- a/src/core/thread.cpp +++ b/src/core/thread.cpp @@ -19,6 +19,19 @@ #include "thread.h" +// This is ugly, but I can't help it. +#define ADD_MSEC_TO_XTIME(__xt, __msec) \ + { \ + __xt.sec += __msec / 1000; \ + __xt.nsec += (__msec % 1000) * 1000; \ + if (__xt.nsec > NANOSECONDS_PER_SECOND) \ + { \ + __xt.sec++; \ + __xt.nsec -= NANOSECONDS_PER_SECOND; \ + } \ + } + + // Helper class for thread creation. class ThreadStarter { @@ -68,13 +81,7 @@ Thread::Join(unsigned msecTimeout) // Calculate time after timeout boost::xtime t; boost::xtime_get(&t, boost::TIME_UTC); - t.sec += msecTimeout / 1000; - t.nsec += (msecTimeout % 1000) * 1000; - if (t.nsec > NANOSECONDS_PER_SECOND) - { - t.sec++; - t.nsec -= NANOSECONDS_PER_SECOND; - } + ADD_MSEC_TO_XTIME(t, msecTimeout); // Wait for the termination of the application code. boost::timed_mutex::scoped_timed_lock lock(m_isTerminatedMutex, t); @@ -94,6 +101,16 @@ Thread::Join(unsigned msecTimeout) return tmpIsTerminated; } +void +Thread::Msleep(unsigned msecs) +{ + boost::xtime t; + boost::xtime_get(&t, boost::TIME_UTC); + ADD_MSEC_TO_XTIME(t, msecs); + + boost::thread::sleep(t); +} + void Thread::MainWrapper() { diff --git a/src/core/thread.h b/src/core/thread.h index bb18690c..a2d2473c 100644 --- a/src/core/thread.h +++ b/src/core/thread.h @@ -46,6 +46,9 @@ public: // You SHOULD always call join for a thread. bool Join(unsigned msecTimeout); + // Sleep the currently active thread. + static void Msleep(unsigned msecs); + protected: // Startup function. diff --git a/src/net/clientdata.h b/src/net/clientdata.h index 8e4afb6c..50593142 100644 --- a/src/net/clientdata.h +++ b/src/net/clientdata.h @@ -28,8 +28,8 @@ class ClientData { public: - ClientData() - : sockfd(INVALID_SOCKET), addrFamily(AF_INET), serverPort(0) {} + ClientData(); + ~ClientData(); int GetServerAddrSize() const { return addrFamily == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in); diff --git a/src/net/clientexception.h b/src/net/clientexception.h new file mode 100644 index 00000000..de1c45bc --- /dev/null +++ b/src/net/clientexception.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +/* Exception class for client errors. */ + +#ifndef _CLIENTEXCEPTION_H_ +#define _CLIENTEXCEPTION_H_ + + +class ClientException +{ +public: + + ClientException(int errorId, int osErrorCode) + : m_errorId(errorId), m_osErrorCode(osErrorCode) {} + + int GetErrorId() const {return m_errorId;} + int GetOsErrorCode() const {return m_osErrorCode;} + +private: + int m_errorId; + int m_osErrorCode; +}; + +#endif diff --git a/src/net/clientstate.h b/src/net/clientstate.h index bd780547..b0cf87c1 100644 --- a/src/net/clientstate.h +++ b/src/net/clientstate.h @@ -35,7 +35,7 @@ public: virtual ~ClientState(); // Main processing function of the current state. - virtual void Process(ClientThread &client, ClientCallback &gui) = 0; + virtual int Process(ClientThread &client) = 0; }; // State: Initialization. @@ -48,7 +48,7 @@ public: virtual ~ClientStateInit(); // Some basic initialization (socket creation, basic checks). - virtual void Process(ClientThread &client, ClientCallback &gui); + virtual int Process(ClientThread &client); protected: @@ -66,7 +66,7 @@ public: virtual ~ClientStateResolve(); // "Poll" for the completion of the name resolution. - virtual void Process(ClientThread &client, ClientCallback &gui); + virtual int Process(ClientThread &client); protected: @@ -84,7 +84,7 @@ public: virtual ~ClientStateConnect(); // "Poll" for the completion of the TCP/IP connect call. - virtual void Process(ClientThread &client, ClientCallback &gui); + virtual int Process(ClientThread &client); protected: @@ -102,7 +102,7 @@ public: virtual ~ClientStateFinal(); // sleep. - virtual void Process(ClientThread &client, ClientCallback &gui); + virtual int Process(ClientThread &client); protected: diff --git a/src/net/common/clientdata.cpp b/src/net/common/clientdata.cpp new file mode 100644 index 00000000..3c90044f --- /dev/null +++ b/src/net/common/clientdata.cpp @@ -0,0 +1,33 @@ +/*************************************************************************** + * 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 + +ClientData::ClientData() +: sockfd(INVALID_SOCKET), addrFamily(AF_INET), serverPort(0) +{ + bzero(clientAddr, sizeof(clientAddr)); +} + +ClientCallback::~ClientCallback() +{ + if (sockfd != INVALID_SOCKET) + CLOSESOCKET(sockfd); +} + diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index 99876768..f532345a 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -20,8 +20,8 @@ #include #include #include +#include #include -#include #include #include @@ -50,41 +50,30 @@ ClientStateInit::~ClientStateInit() { } -void -ClientStateInit::Process(ClientThread &client, ClientCallback &cb) +int +ClientStateInit::Process(ClientThread &client) { ClientData &data = client.GetData(); if (data.serverAddr.empty()) - { - cb.SignalNetClientError(ERR_SOCK_SERVERADDR_NOT_SET, 0); - throw runtime_error("ClientStateInit"); // TODO: own exception - } + throw ClientException(ERR_SOCK_SERVERADDR_NOT_SET, 0); if (data.serverPort < 1024) - { - cb.SignalNetClientError(ERR_SOCK_INVALID_PORT, 0); - throw runtime_error("ClientStateInit"); - } + throw ClientException(ERR_SOCK_INVALID_PORT, 0); data.sockfd = socket(data.addrFamily, SOCK_STREAM, 0); if (!IS_VALID_SOCKET(data.sockfd)) - { - cb.SignalNetClientError(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); - throw runtime_error("ClientStateInit"); - } + throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); #if 0 unsigned long mode = 1; if (IOCTLSOCKET(data.sockfd, FIONBIO, &mode) == SOCKET_ERROR) - { - cb.SignalNetClientError(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); - throw runtime_error("ClientStateInit"); - } + throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); #endif - cb.SignalNetClientSuccess(MSG_SOCK_INIT_DONE); client.SetState(ClientStateResolve::Instance()); + + return MSG_SOCK_INIT_DONE; } //----------------------------------------------------------------------------- @@ -104,36 +93,45 @@ ClientStateResolve::~ClientStateResolve() { } -void -ClientStateResolve::Process(ClientThread &client, ClientCallback &cb) +int +ClientStateResolve::Process(ClientThread &client) { ClientData &data = client.GetData(); data.clientAddr.ss_family = data.addrFamily; // Treat the server address as numbers first. - if (socket_string_to_addr(data.serverAddr.c_str(), data.addrFamily, (struct sockaddr *)&data.clientAddr, data.GetServerAddrSize())) + if (socket_string_to_addr( + data.serverAddr.c_str(), + data.addrFamily, + (struct sockaddr *)&data.clientAddr, + data.GetServerAddrSize())) { - // 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())) - { - cb.SignalNetClientError(ERR_SOCK_SET_PORT_FAILED, 0); - throw runtime_error("ClientStateResolve"); - } + throw ClientException(ERR_SOCK_SET_PORT_FAILED, 0); } else { // This did not work out - try name resolution. ostringstream tmpStr; tmpStr << data.serverPort; - if (!socket_resolve(data.serverAddr.c_str(), tmpStr.str().c_str(), data.addrFamily, SOCK_STREAM, 0, (struct sockaddr *)&data.clientAddr, data.GetServerAddrSize())) + // 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())) { - cb.SignalNetClientError(ERR_SOCK_RESOLVE_FAILED, 0); // TODO: use errno value - throw runtime_error("ClientStateResolve"); + throw ClientException(ERR_SOCK_RESOLVE_FAILED, 0); } } - cb.SignalNetClientSuccess(MSG_SOCK_RESOLVE_DONE); client.SetState(ClientStateConnect::Instance()); + + return MSG_SOCK_RESOLVE_DONE; } //----------------------------------------------------------------------------- @@ -153,18 +151,17 @@ ClientStateConnect::~ClientStateConnect() { } -void -ClientStateConnect::Process(ClientThread &client, ClientCallback &cb) +int +ClientStateConnect::Process(ClientThread &client) { ClientData &data = client.GetData(); if (!IS_VALID_CONNECT(connect(data.sockfd, (struct sockaddr *)&data.clientAddr, data.GetServerAddrSize()))) - { - cb.SignalNetClientError(ERR_SOCK_CONNECT_FAILED, SOCKET_ERRNO()); - throw runtime_error("ClientStateResolve"); - } - cb.SignalNetClientSuccess(MSG_SOCK_RESOLVE_DONE); + throw ClientException(ERR_SOCK_CONNECT_FAILED, SOCKET_ERRNO()); + client.SetState(ClientStateFinal::Instance()); + + return MSG_SOCK_RESOLVE_DONE; } //----------------------------------------------------------------------------- @@ -184,16 +181,10 @@ ClientStateFinal::~ClientStateFinal() { } -void -ClientStateFinal::Process(ClientThread &client, ClientCallback &cb) +int +ClientStateFinal::Process(ClientThread &client) { - boost::xtime t; - boost::xtime_get(&t, boost::TIME_UTC); - t.nsec += 10000; - if (t.nsec > NANOSECONDS_PER_SECOND) - { - t.sec++; - t.nsec -= NANOSECONDS_PER_SECOND; - } - boost::thread::sleep(t); + Thread::Msleep(10); + + return MSG_SOCK_INTERNAL_PENDING; } diff --git a/src/net/common/clientthread.cpp b/src/net/common/clientthread.cpp index 8ac8d2f2..7e7fd833 100644 --- a/src/net/common/clientthread.cpp +++ b/src/net/common/clientthread.cpp @@ -20,6 +20,10 @@ #include #include #include +#include +#include +#include + #include @@ -30,7 +34,6 @@ ClientThread::ClientThread(ClientCallback &cb) : m_curState(NULL), m_callback(cb) { m_data.reset(new ClientData); - m_curState = &CLIENT_INITIAL_STATE::Instance(); } ClientThread::~ClientThread() @@ -42,18 +45,29 @@ ClientThread::Init(const string &serverAddress, unsigned serverPort, bool ipv6, { if (IsRunning()) return; // TODO: throw exception - m_data->addrFamily = ipv6 ? AF_INET6 : AF_INET; - m_data->serverAddr = serverAddress; - m_data->serverPort = serverPort; - m_data->password = pwd; + + ClientData &data = GetData(); + + data.addrFamily = ipv6 ? AF_INET6 : AF_INET; + data.serverAddr = serverAddress; + data.serverPort = serverPort; + data.password = pwd; } void ClientThread::Main() { - while (!ShouldTerminate()) + SetState(CLIENT_INITIAL_STATE::Instance()); + try { + while (!ShouldTerminate()) + { + int msg = GetState().Process(*this); + if (msg != MSG_SOCK_INTERNAL_PENDING) + m_callback.SignalNetClientSuccess(msg); + } + } catch (const ClientException &e) { - GetState().Process(*this, m_callback); + m_callback.SignalNetClientError(e.GetErrorId(), e.GetOsErrorCode()); } } diff --git a/src/net/socket_msg.h b/src/net/socket_msg.h index e05ab552..4571a57d 100644 --- a/src/net/socket_msg.h +++ b/src/net/socket_msg.h @@ -27,6 +27,10 @@ #define ERR_SOCK_RESOLVE_FAILED 12 #define ERR_SOCK_CONNECT_FAILED 13 +// This is an internal message which is not reported. +#define MSG_SOCK_INTERNAL_PENDING 0 + +// The following messages are reported. #define MSG_SOCK_INIT_DONE 1 #define MSG_SOCK_RESOLVE_DONE 2 #define MSG_SOCK_CONNECT_DONE 3