diff --git a/src/net/clientstate.h b/src/net/clientstate.h index 1c9e6bb2..49615760 100644 --- a/src/net/clientstate.h +++ b/src/net/clientstate.h @@ -101,14 +101,32 @@ private: ResolverThread *m_resolver; }; -// State: Connecting to server. -class ClientStateConnect : public ClientState +// State: Initiate server connection. +class ClientStateStartConnect : public ClientState { public: // Access the state singleton. - static ClientStateConnect &Instance(); + static ClientStateStartConnect &Instance(); - virtual ~ClientStateConnect(); + virtual ~ClientStateStartConnect(); + + // Call connect. + virtual int Process(ClientThread &client); + +protected: + + // Protected constructor - this is a singleton. + ClientStateStartConnect(); +}; + +// State: Connecting to server. +class ClientStateConnecting : public ClientState +{ +public: + // Access the state singleton. + static ClientStateConnecting &Instance(); + + virtual ~ClientStateConnecting(); // "Poll" for the completion of the TCP/IP connect call. virtual int Process(ClientThread &client); @@ -116,7 +134,7 @@ public: protected: // Protected constructor - this is a singleton. - ClientStateConnect(); + ClientStateConnecting(); }; // State: Final (TODO). diff --git a/src/net/clientthread.h b/src/net/clientthread.h index 2d21ddd3..ea082f5b 100644 --- a/src/net/clientthread.h +++ b/src/net/clientthread.h @@ -61,7 +61,8 @@ private: friend class ClientStateInit; friend class ClientStateStartResolve; friend class ClientStateResolving; -friend class ClientStateConnect; +friend class ClientStateStartConnect; +friend class ClientStateConnecting; }; #endif diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index df05800b..a30acaf9 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -28,6 +28,8 @@ using namespace std; +#define CLIENT_WAIT_TIMEOUT_MSEC 100 + ClientState::~ClientState() { @@ -65,9 +67,9 @@ ClientStateInit::Process(ClientThread &client) if (!IS_VALID_SOCKET(data.sockfd)) throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); -// unsigned long mode = 1; -// if (IOCTLSOCKET(data.sockfd, FIONBIO, &mode) == SOCKET_ERROR) -// throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); + unsigned long mode = 1; + if (IOCTLSOCKET(data.sockfd, FIONBIO, &mode) == SOCKET_ERROR) + throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO()); client.SetState(ClientStateStartResolve::Instance()); @@ -112,7 +114,7 @@ ClientStateStartResolve::Process(ClientThread &client) throw ClientException(ERR_SOCK_SET_PORT_FAILED, 0); // No need to resolve - start connecting. - client.SetState(ClientStateConnect::Instance()); + client.SetState(ClientStateStartConnect::Instance()); retVal = MSG_SOCK_RESOLVE_DONE; } else @@ -167,7 +169,7 @@ ClientStateResolving::Process(ClientThread &client) if (!m_resolver) throw ClientException(ERR_SOCK_RESOLVE_FAILED, 0); - if (m_resolver->Join(100)) + if (m_resolver->Join(CLIENT_WAIT_TIMEOUT_MSEC)) { ClientData &data = client.GetData(); bool success = m_resolver->GetResult(data); @@ -176,7 +178,7 @@ ClientStateResolving::Process(ClientThread &client) if (!success) throw ClientException(ERR_SOCK_RESOLVE_FAILED, 0); - client.SetState(ClientStateConnect::Instance()); + client.SetState(ClientStateStartConnect::Instance()); retVal = MSG_SOCK_RESOLVE_DONE; } else @@ -201,32 +203,100 @@ ClientStateResolving::Cleanup() //----------------------------------------------------------------------------- -ClientStateConnect & -ClientStateConnect::Instance() +ClientStateStartConnect & +ClientStateStartConnect::Instance() { - static ClientStateConnect state; + static ClientStateStartConnect state; return state; } -ClientStateConnect::ClientStateConnect() +ClientStateStartConnect::ClientStateStartConnect() { } -ClientStateConnect::~ClientStateConnect() +ClientStateStartConnect::~ClientStateStartConnect() { } int -ClientStateConnect::Process(ClientThread &client) +ClientStateStartConnect::Process(ClientThread &client) { + int retVal; ClientData &data = client.GetData(); - if (!IS_VALID_CONNECT(connect(data.sockfd, (struct sockaddr *)&data.clientAddr, data.GetServerAddrSize()))) - throw ClientException(ERR_SOCK_CONNECT_FAILED, SOCKET_ERRNO()); + int connectResult = connect(data.sockfd, (struct sockaddr *)&data.clientAddr, data.GetServerAddrSize()); - client.SetState(ClientStateFinal::Instance()); + if (IS_VALID_CONNECT(connectResult)) + { + client.SetState(ClientStateFinal::Instance()); + retVal = MSG_SOCK_CONNECT_DONE; + } + else + { + int errCode = SOCKET_ERRNO(); + if (errCode == SOCKET_ERR_WOULDBLOCK) + { + client.SetState(ClientStateConnecting::Instance()); + retVal = MSG_SOCK_INTERNAL_PENDING; + } + else + throw ClientException(ERR_SOCK_CONNECT_FAILED, SOCKET_ERRNO()); + } - return MSG_SOCK_CONNECT_DONE; + return retVal; +} + +//----------------------------------------------------------------------------- + +ClientStateConnecting & +ClientStateConnecting::Instance() +{ + static ClientStateConnecting state; + return state; +} + +ClientStateConnecting::ClientStateConnecting() +{ +} + +ClientStateConnecting::~ClientStateConnecting() +{ +} + +int +ClientStateConnecting::Process(ClientThread &client) +{ + int retVal; + ClientData &data = client.GetData(); + + struct fd_set writeSet; + struct timeval timeout; + + FD_ZERO(&writeSet); + FD_SET(data.sockfd, &writeSet); + + timeout.tv_sec = 0; + timeout.tv_usec = CLIENT_WAIT_TIMEOUT_MSEC * 1000; + int selectResult = select(data.sockfd, NULL, &writeSet, NULL, &timeout); + + if (selectResult > 0) // success + { + // Check whether the connect call succeeded. + int connectResult = 0; + int tmpSize = sizeof(connectResult); + getsockopt(data.sockfd, SOL_SOCKET, SO_ERROR, (char *)&connectResult, &tmpSize); + if (connectResult != 0) + throw ClientException(ERR_SOCK_CONNECT_FAILED, connectResult); + client.SetState(ClientStateFinal::Instance()); + retVal = MSG_SOCK_CONNECT_DONE; + } + else if (selectResult == 0) // timeout + retVal = MSG_SOCK_INTERNAL_PENDING; + else + throw ClientException(ERR_SOCK_SELECT_FAILED, SOCKET_ERRNO()); + + + return retVal; } //----------------------------------------------------------------------------- diff --git a/src/net/common/resolverthread.cpp b/src/net/common/resolverthread.cpp index 257c1b4a..c1027938 100644 --- a/src/net/common/resolverthread.cpp +++ b/src/net/common/resolverthread.cpp @@ -43,7 +43,9 @@ ResolverThread::Init(const ClientData &data) if (IsRunning()) return; // TODO: throw exception - *m_data = data; + m_data->addrFamily = data.addrFamily; + m_data->serverAddr = data.serverAddr; + m_data->serverPort = data.serverPort; } bool diff --git a/src/net/socket_helper.h b/src/net/socket_helper.h index 16c07240..97cdd720 100644 --- a/src/net/socket_helper.h +++ b/src/net/socket_helper.h @@ -31,6 +31,7 @@ #define CLOSESOCKET closesocket #define IOCTLSOCKET ioctlsocket #define SOCKET_ERRNO() WSAGetLastError() +#define SOCKET_ERR_WOULDBLOCK WSAEWOULDBLOCK #else #define SOCKET int #define SOCKET_ERROR -1 @@ -38,6 +39,7 @@ #define CLOSESOCKET close #define SOCKET_ERRNO() errno #define IOCTLSOCKET ioctl +#define SOCKET_ERR_WOULDBLOCK EWOULDBLOCK #endif #define IS_VALID_SOCKET(_s) ((_s) != INVALID_SOCKET) diff --git a/src/net/socket_msg.h b/src/net/socket_msg.h index f94f28e9..6724452c 100644 --- a/src/net/socket_msg.h +++ b/src/net/socket_msg.h @@ -26,6 +26,7 @@ #define ERR_SOCK_SET_PORT_FAILED 4 #define ERR_SOCK_RESOLVE_FAILED 5 #define ERR_SOCK_CONNECT_FAILED 6 +#define ERR_SOCK_SELECT_FAILED 7 // This is an internal message which is not reported. #define MSG_SOCK_INTERNAL_PENDING 0