diff --git a/pokerth.pro b/pokerth.pro index 6486bfe6..9dcee609 100755 --- a/pokerth.pro +++ b/pokerth.pro @@ -105,6 +105,9 @@ SOURCES += pokerth.cpp \ clientstate.cpp \ clientthread.cpp \ resolverthread.cpp \ + netpacket.cpp \ + senderthread.cpp \ + serverthread.cpp \ clientdata.cpp \ clientcallback.cpp \ socket_helper_cmn.cpp \ diff --git a/src/gui/qt/connecttoserverdialog/connecttoserverdialogimpl.cpp b/src/gui/qt/connecttoserverdialog/connecttoserverdialogimpl.cpp index a8323b09..4b9ed56c 100644 --- a/src/gui/qt/connecttoserverdialog/connecttoserverdialogimpl.cpp +++ b/src/gui/qt/connecttoserverdialog/connecttoserverdialogimpl.cpp @@ -36,9 +36,12 @@ void connectToServerDialogImpl::refresh(int actionID) { break; case MSG_SOCK_RESOLVE_DONE: { label_actionMessage->setText("Connecting to server..."); } break; - case MSG_SOCK_CONNECT_DONE: { label_actionMessage->setText("Connection established."); } + case MSG_SOCK_CONNECT_DONE: { label_actionMessage->setText("Starting session..."); } break; - default: { label_actionMessage->setText("ERROR"); } + case MSG_SOCK_SESSION_DONE: { label_actionMessage->setText("Connection established!"); } + break; + + default: { label_actionMessage->setText("Please wait..."); } } progressBar->setValue(actionID*(100/MSG_SOCK_LAST)); @@ -81,6 +84,21 @@ void connectToServerDialogImpl::error(int errorID, int osErrorID) { tr("Could not connect to the server."), QMessageBox::Close); } break; + case ERR_SOCK_SELECT_FAILED: + { QMessageBox::warning(this, tr("Network Error"), + tr("Internal network error: \"select\" failed."), + QMessageBox::Close); } + break; + case ERR_SOCK_RECV_FAILED: + { QMessageBox::warning(this, tr("Network Error"), + tr("Internal network error: \"recv\" failed."), + QMessageBox::Close); } + break; + { QMessageBox::warning(this, tr("Network Error"), + tr("Internal network error: \"send\" failed."), + QMessageBox::Close); } + case ERR_SOCK_SEND_FAILED: + break; default: { QMessageBox::warning(this, tr("Network Error"), tr("DEFAULT ERROR"), QMessageBox::Close); } diff --git a/src/gui/qt/mainwindow/mainwindowimpl.cpp b/src/gui/qt/mainwindow/mainwindowimpl.cpp index 9f520868..7215ec3e 100755 --- a/src/gui/qt/mainwindow/mainwindowimpl.cpp +++ b/src/gui/qt/mainwindow/mainwindowimpl.cpp @@ -579,6 +579,8 @@ void mainWindowImpl::callCreateNetworkGameDialog() { myCreateNetworkGameDialog->exec(); // if (myCreateNetworkGameDialog->result() == QDialog::Accepted ) { + mySession->terminateNetworkServer(); + mySession->startNetworkServer(); // // mySession->terminateNetworkClient(); // diff --git a/src/net/clientstate.h b/src/net/clientstate.h index 49615760..4c84809a 100644 --- a/src/net/clientstate.h +++ b/src/net/clientstate.h @@ -137,6 +137,42 @@ protected: ClientStateConnecting(); }; +// State: Session init. +class ClientStateStartSession : public ClientState +{ +public: + // Access the state singleton. + static ClientStateStartSession &Instance(); + + virtual ~ClientStateStartSession(); + + // sleep. + virtual int Process(ClientThread &client); + +protected: + + // Protected constructor - this is a singleton. + ClientStateStartSession(); +}; + +// State: Wait for Session ACK. +class ClientStateWaitSession : public ClientState +{ +public: + // Access the state singleton. + static ClientStateWaitSession &Instance(); + + virtual ~ClientStateWaitSession(); + + // sleep. + virtual int Process(ClientThread &client); + +protected: + + // Protected constructor - this is a singleton. + ClientStateWaitSession(); +}; + // State: Final (TODO). class ClientStateFinal : public ClientState { diff --git a/src/net/clientthread.h b/src/net/clientthread.h index ea082f5b..7080ef7d 100644 --- a/src/net/clientthread.h +++ b/src/net/clientthread.h @@ -28,6 +28,7 @@ class ClientData; class ClientState; class ClientCallback; +class SenderThread; class ClientThread : public Thread { @@ -51,18 +52,22 @@ protected: ClientState &GetState(); void SetState(ClientState &newState); + SenderThread &GetSender(); + private: std::auto_ptr m_data; ClientState *m_curState; ClientCallback &m_callback; - + std::auto_ptr m_sender; friend class ClientStateInit; friend class ClientStateStartResolve; friend class ClientStateResolving; friend class ClientStateStartConnect; friend class ClientStateConnecting; +friend class ClientStateStartSession; +friend class ClientStateWaitSession; }; #endif diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index 3d8a7311..828556c9 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -229,7 +231,7 @@ ClientStateStartConnect::Process(ClientThread &client) if (IS_VALID_CONNECT(connectResult)) { - client.SetState(ClientStateFinal::Instance()); + client.SetState(ClientStateStartSession::Instance()); retVal = MSG_SOCK_CONNECT_DONE; } else @@ -288,7 +290,7 @@ ClientStateConnecting::Process(ClientThread &client) getsockopt(data.sockfd, SOL_SOCKET, SO_ERROR, (char *)&connectResult, &tmpSize); if (connectResult != 0) throw ClientException(ERR_SOCK_CONNECT_FAILED, connectResult); - client.SetState(ClientStateFinal::Instance()); + client.SetState(ClientStateStartSession::Instance()); retVal = MSG_SOCK_CONNECT_DONE; } else if (selectResult == 0) // timeout @@ -302,6 +304,87 @@ ClientStateConnecting::Process(ClientThread &client) //----------------------------------------------------------------------------- +ClientStateStartSession & +ClientStateStartSession::Instance() +{ + static ClientStateStartSession state; + return state; +} + +ClientStateStartSession::ClientStateStartSession() +{ +} + +ClientStateStartSession::~ClientStateStartSession() +{ +} + +int +ClientStateStartSession::Process(ClientThread &client) +{ + client.GetSender().Init(client.GetData().sockfd); + client.GetSender().Run(); + + boost::shared_ptr packet(new TestNetPacket(10)); + client.GetSender().Send(packet); + + client.SetState(ClientStateWaitSession::Instance()); + + return MSG_SOCK_INTERNAL_PENDING; +} + +//----------------------------------------------------------------------------- + +ClientStateWaitSession & +ClientStateWaitSession::Instance() +{ + static ClientStateWaitSession state; + return state; +} + +ClientStateWaitSession::ClientStateWaitSession() +{ +} + +ClientStateWaitSession::~ClientStateWaitSession() +{ +} + +int +ClientStateWaitSession::Process(ClientThread &client) +{ + int retVal; + ClientData &data = client.GetData(); + + // TODO: use receiver thread. + fd_set readSet; + struct timeval timeout; + + FD_ZERO(&readSet); + FD_SET(data.sockfd, &readSet); + + timeout.tv_sec = 0; + timeout.tv_usec = CLIENT_WAIT_TIMEOUT_MSEC * 1000; + int selectResult = select(data.sockfd + 1, &readSet, NULL, NULL, &timeout); + if (selectResult > 0) // recv is possible + { + char buf[128]; + if (recv(data.sockfd, buf, sizeof(buf), 0) > 0) + { + client.SetState(ClientStateFinal::Instance()); + retVal = MSG_SOCK_SESSION_DONE; + } + else + throw ClientException(ERR_SOCK_RECV_FAILED, 0); + } + else + retVal = MSG_SOCK_INTERNAL_PENDING; + + return retVal; +} + +//----------------------------------------------------------------------------- + ClientStateFinal & ClientStateFinal::Instance() { diff --git a/src/net/common/clientthread.cpp b/src/net/common/clientthread.cpp index 7e7fd833..6c3adc4f 100644 --- a/src/net/common/clientthread.cpp +++ b/src/net/common/clientthread.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ ClientThread::Init(const string &serverAddress, unsigned serverPort, bool ipv6, void ClientThread::Main() { + m_sender.reset(new SenderThread); SetState(CLIENT_INITIAL_STATE::Instance()); try { while (!ShouldTerminate()) @@ -69,6 +71,8 @@ ClientThread::Main() { m_callback.SignalNetClientError(e.GetErrorId(), e.GetOsErrorCode()); } + GetSender().SignalTermination(); + GetSender().Join(100); } const ClientData & @@ -97,3 +101,11 @@ ClientThread::SetState(ClientState &newState) { m_curState = &newState; } + +SenderThread & +ClientThread::GetSender() +{ + assert(m_sender.get()); + return *m_sender; +} + diff --git a/src/net/common/netpacket.cpp b/src/net/common/netpacket.cpp new file mode 100644 index 00000000..a6767f48 --- /dev/null +++ b/src/net/common/netpacket.cpp @@ -0,0 +1,44 @@ +/*************************************************************************** + * 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 + +NetPacket::~NetPacket() +{ +} + +//----------------------------------------------------------------------------- + +TestNetPacket::TestNetPacket(u_int32_t value) +{ + m_data.head.type = 0; + m_data.head.length = sizeof(m_data); + m_data.test = value; +} + +TestNetPacket::~TestNetPacket() +{ +} + +NetPacketHeader * +TestNetPacket::GetData() +{ + return (NetPacketHeader *)&m_data; +} + diff --git a/src/net/common/senderthread.cpp b/src/net/common/senderthread.cpp new file mode 100644 index 00000000..5d70c16a --- /dev/null +++ b/src/net/common/senderthread.cpp @@ -0,0 +1,85 @@ +/*************************************************************************** + * 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 +#include + +#define SEND_TIMEOUT_MSEC 50 + +SenderThread::SenderThread() +{ +} + +SenderThread::~SenderThread() +{ +} + +void +SenderThread::Init(SOCKET socket) +{ + if (!IS_VALID_SOCKET(socket) || IsRunning()) + return; // TODO: throw exception + + m_socket = socket; +} + +void +SenderThread::Send(boost::shared_ptr packet) +{ + boost::mutex::scoped_lock lock(m_outBufMutex); + m_outBuf.push_back(packet); +} + +void +SenderThread::Main() +{ + boost::shared_ptr tmpPacket; + while (!ShouldTerminate()) + { + if (!tmpPacket.get()) + { + boost::mutex::scoped_lock lock(m_outBufMutex); + if (!m_outBuf.empty()) + { + tmpPacket = m_outBuf.front(); + m_outBuf.pop_front(); + } + } + if (tmpPacket.get()) + { + fd_set writeSet; + struct timeval timeout; + + FD_ZERO(&writeSet); + FD_SET(m_socket, &writeSet); + + timeout.tv_sec = 0; + timeout.tv_usec = SEND_TIMEOUT_MSEC * 1000; + int selectResult = select(m_socket + 1, NULL, &writeSet, NULL, &timeout); + if (selectResult > 0) // send is possible + { + send(m_socket, (const char *)tmpPacket->GetData(), tmpPacket->GetData()->length, 0); + tmpPacket.reset(); + } + } + else + Msleep(SEND_TIMEOUT_MSEC); + } +} + diff --git a/src/net/common/serverthread.cpp b/src/net/common/serverthread.cpp new file mode 100644 index 00000000..39e6ebde --- /dev/null +++ b/src/net/common/serverthread.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** + * 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 +#include + + +ServerThread::ServerThread() +{ +} + +ServerThread::~ServerThread() +{ +} + +void +ServerThread::Init() +{ + if (IsRunning()) + return; // TODO: throw exception +} + +void +ServerThread::Main() +{ + while (!ShouldTerminate()) + { + // Simple hacked server for testing. + SOCKET sockfd; + char buf[1024]; + struct sockaddr_storage servaddr, clientaddr; + int sockaddr_size = sizeof(struct sockaddr_in); + int addrFamily = AF_INET; + int addrSize; + + sockfd = socket(addrFamily, SOCK_STREAM, 0); + bzero(&servaddr, sizeof(servaddr)); + servaddr.ss_family = addrFamily; + + socket_string_to_addr("0.0.0.0", addrFamily, (struct sockaddr *)&servaddr, sockaddr_size); + socket_set_port(7234, addrFamily, (struct sockaddr *)&servaddr, sockaddr_size); + + bind(sockfd, (const struct sockaddr *)&servaddr, sockaddr_size); + listen(sockfd, 1); + + bzero(&clientaddr, sizeof(clientaddr)); + addrSize = sockaddr_size; + SOCKET conn = accept(sockfd, (struct sockaddr *)&clientaddr, &addrSize); + CLOSESOCKET(sockfd); + int ret = recv(conn, buf, sizeof(buf), 0); + + send(conn, buf, ret, 0); + + CLOSESOCKET(conn); + } +} + diff --git a/src/net/netpacket.h b/src/net/netpacket.h new file mode 100644 index 00000000..8ec75f88 --- /dev/null +++ b/src/net/netpacket.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +/* PokerTH network packet. */ + +#ifndef _NETPACKET_H_ +#define _NETPACKET_H_ + +#include +#include + +#ifdef _MSC_VER + #pragma pack(push, 2) +#else + #pragma align 2 +#endif + +struct NetPacketHeader +{ + u_int16_t type; + u_int16_t length; +}; + +struct NetPacketInit +{ + NetPacketHeader head; + u_int32_t test; +}; + +#ifdef _MSC_VER + #pragma pack(pop) +#else + #pragma align 0 +#endif + + +class NetPacket +{ +public: + virtual ~NetPacket(); + + virtual NetPacketHeader *GetData() = 0; +}; + +class TestNetPacket : public NetPacket +{ +public: + TestNetPacket(u_int32_t value); + virtual ~TestNetPacket(); + + virtual NetPacketHeader *GetData(); + +protected: + NetPacketInit m_data; +}; + +#endif + diff --git a/src/net/senderthread.h b/src/net/senderthread.h new file mode 100644 index 00000000..01e4e0b8 --- /dev/null +++ b/src/net/senderthread.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +/* Network sender thread. */ + +#ifndef _SENDERTHREAD_H_ +#define _SENDERTHREAD_H_ + +#include +#include + +#include +#include + +class ClientData; +class NetPacket; + +class SenderThread : public Thread +{ +public: + SenderThread(); + virtual ~SenderThread(); + + // Set the socket from which to receive data. + // TODO: Add error callback. + void Init(SOCKET socket); + + void Send(boost::shared_ptr packet); + +protected: + + // Main function of the thread. + virtual void Main(); + +private: + + SOCKET m_socket; + + std::deque > m_outBuf; + mutable boost::mutex m_outBufMutex; +}; + +#endif + diff --git a/src/net/serverthread.h b/src/net/serverthread.h new file mode 100644 index 00000000..5f3935ec --- /dev/null +++ b/src/net/serverthread.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ +/* Network server thread. */ + +#ifndef _SERVERTHREAD_H_ +#define _SERVERTHREAD_H_ + +#include +#include +#include + + +class ServerThread : public Thread +{ +public: + ServerThread(/*ServerCallback &gui*/); + virtual ~ServerThread(); + + // Set the parameters. TODO + void Init(); + +protected: + + // Main function of the thread. + virtual void Main(); + +private: + +}; + +#endif diff --git a/src/net/socket_msg.h b/src/net/socket_msg.h index 6724452c..c10fe189 100644 --- a/src/net/socket_msg.h +++ b/src/net/socket_msg.h @@ -27,6 +27,8 @@ #define ERR_SOCK_RESOLVE_FAILED 5 #define ERR_SOCK_CONNECT_FAILED 6 #define ERR_SOCK_SELECT_FAILED 7 +#define ERR_SOCK_RECV_FAILED 8 +#define ERR_SOCK_SEND_FAILED 9 // This is an internal message which is not reported. #define MSG_SOCK_INTERNAL_PENDING 0 @@ -35,8 +37,9 @@ #define MSG_SOCK_INIT_DONE 1 #define MSG_SOCK_RESOLVE_DONE 2 #define MSG_SOCK_CONNECT_DONE 3 +#define MSG_SOCK_SESSION_DONE 4 -#define MSG_SOCK_LAST MSG_SOCK_CONNECT_DONE +#define MSG_SOCK_LAST MSG_SOCK_SESSION_DONE #endif diff --git a/src/pokerth.cpp b/src/pokerth.cpp index 92b5e73c..c34b775b 100755 --- a/src/pokerth.cpp +++ b/src/pokerth.cpp @@ -55,7 +55,7 @@ class GuiWrapper; int main( int argc, char **argv ) { - ENABLE_LEAK_CHECK(); + //ENABLE_LEAK_CHECK(); //_CrtSetBreakAlloc(49937); diff --git a/src/session.cpp b/src/session.cpp index 1938a07b..75c5b806 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -22,13 +22,15 @@ #include "guiinterface.h" #include "configfile.h" #include +#include #define NET_CLIENT_TERMINATE_TIMEOUT_MSEC 1000 +#define NET_SERVER_TERMINATE_TIMEOUT_MSEC 1000 using namespace std; Session::Session(GuiInterface *g) -: actualGameID(0), myNetClient(0), actualGame(0), myGui(g) +: actualGameID(0), myNetClient(0), myNetServer(0), actualGame(0), myGui(g) { myConfig = new ConfigFile; } @@ -78,3 +80,27 @@ void Session::terminateNetworkClient() // If termination fails, leave a memory leak to prevent a crash. myNetClient = 0; } + +void Session::startNetworkServer() +{ + if (myNetServer) + return; // TODO: throw exception + myNetServer = new ServerThread(); + myNetServer->Init(); + myNetServer->Run(); +} + +void Session::terminateNetworkServer() +{ + if (!myNetServer) + return; // already terminated + myNetServer->SignalTermination(); + // Give the thread some time to terminate. + if (myNetServer->Join(NET_SERVER_TERMINATE_TIMEOUT_MSEC)) + { + delete myNetServer; + } + // If termination fails, leave a memory leak to prevent a crash. + myNetServer = 0; +} + diff --git a/src/session.h b/src/session.h index df148df1..37557a8d 100755 --- a/src/session.h +++ b/src/session.h @@ -25,6 +25,7 @@ class GuiInterface; class Game; class ConfigFile; class ClientThread; +class ServerThread; class Session{ public: @@ -39,6 +40,9 @@ public: void startNetworkClient(const std::string &serverAddress, unsigned serverPort, bool ipv6, const std::string &pwd); void terminateNetworkClient(); + void startNetworkServer(); + void terminateNetworkServer(); + void setActualGameID(const int& theValue) { actualGameID = theValue; } int getActualGameID() const { return actualGameID; } @@ -49,6 +53,7 @@ private: int actualGameID; ClientThread *myNetClient; + ServerThread *myNetServer; Game *actualGame; GuiInterface *myGui;