Started to implement server state engine.

This commit is contained in:
lotodore
2007-03-13 23:27:17 +00:00
parent 6f33202406
commit ca354dce7f
33 changed files with 1130 additions and 193 deletions
+52
View File
@@ -0,0 +1,52 @@
/***************************************************************************
* 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/clientcontext.h>
ClientContext::ClientContext()
: m_sockfd(INVALID_SOCKET), m_addrFamily(AF_INET), m_serverPort(0)
{
bzero(&m_clientSockaddr, sizeof(m_clientSockaddr));
}
ClientContext::~ClientContext()
{
if (m_sockfd != INVALID_SOCKET)
CLOSESOCKET(m_sockfd);
}
SOCKET
ClientContext::GetSocket() const
{
return m_sockfd;
}
u_int32_t
ClientContext::GetId() const
{
// Id is unused for clients.
return 0;
}
void
ClientContext::SetSocket(SOCKET sockfd)
{
m_sockfd = sockfd;
}
+27 -33
View File
@@ -19,7 +19,7 @@
#include <net/clientstate.h>
#include <net/clientthread.h>
#include <net/clientdata.h>
#include <net/clientcontext.h>
#include <net/senderthread.h>
#include <net/receiverhelper.h>
#include <net/netpacket.h>
@@ -28,11 +28,9 @@
#include <net/socket_helper.h>
#include <net/socket_msg.h>
#include <stdexcept>
using namespace std;
#define CLIENT_WAIT_TIMEOUT_MSEC 100
#define CLIENT_WAIT_TIMEOUT_MSEC 50
ClientState::~ClientState()
@@ -59,20 +57,20 @@ ClientStateInit::~ClientStateInit()
int
ClientStateInit::Process(ClientThread &client)
{
ClientData &data = client.GetData();
ClientContext &context = client.GetContext();
if (data.serverAddr.empty())
if (context.GetServerAddr().empty())
throw ClientException(ERR_SOCK_SERVERADDR_NOT_SET, 0);
// if (data.serverPort < 1024)
// if (context.GetServerPort() < 1024)
// throw ClientException(ERR_SOCK_INVALID_PORT, 0);
data.sockfd = socket(data.addrFamily, SOCK_STREAM, 0);
if (!IS_VALID_SOCKET(data.sockfd))
context.SetSocket(socket(context.GetAddrFamily(), SOCK_STREAM, 0));
if (!IS_VALID_SOCKET(context.GetSocket()))
throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO());
unsigned long mode = 1;
if (IOCTLSOCKET(data.sockfd, FIONBIO, &mode) == SOCKET_ERROR)
if (IOCTLSOCKET(context.GetSocket(), FIONBIO, &mode) == SOCKET_ERROR)
throw ClientException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO());
client.SetState(ClientStateStartResolve::Instance());
@@ -102,19 +100,19 @@ ClientStateStartResolve::Process(ClientThread &client)
{
int retVal;
ClientData &data = client.GetData();
ClientContext &context = client.GetContext();
data.clientAddr.ss_family = data.addrFamily;
context.GetClientSockaddr()->ss_family = context.GetAddrFamily();
// Treat the server address as numbers first.
if (socket_string_to_addr(
data.serverAddr.c_str(),
data.addrFamily,
(struct sockaddr *)&data.clientAddr,
data.GetServerAddrSize()))
context.GetServerAddr().c_str(),
context.GetAddrFamily(),
(struct sockaddr *)context.GetClientSockaddr(),
context.GetClientSockaddrSize()))
{
// 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(context.GetServerPort(), context.GetAddrFamily(), (struct sockaddr *)context.GetClientSockaddr(), context.GetClientSockaddrSize()))
throw ClientException(ERR_SOCK_SET_PORT_FAILED, 0);
// No need to resolve - start connecting.
@@ -126,7 +124,7 @@ ClientStateStartResolve::Process(ClientThread &client)
// 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->Init(context);
resolver->Run();
ClientStateResolving::Instance().SetResolver(resolver.release());
@@ -175,8 +173,8 @@ ClientStateResolving::Process(ClientThread &client)
if (m_resolver->Join(CLIENT_WAIT_TIMEOUT_MSEC))
{
ClientData &data = client.GetData();
bool success = m_resolver->GetResult(data);
ClientContext &context = client.GetContext();
bool success = m_resolver->GetResult(context);
Cleanup(); // Not required, but better keep things clean.
if (!success)
@@ -226,9 +224,9 @@ int
ClientStateStartConnect::Process(ClientThread &client)
{
int retVal;
ClientData &data = client.GetData();
ClientContext &context = client.GetContext();
int connectResult = connect(data.sockfd, (struct sockaddr *)&data.clientAddr, data.GetServerAddrSize());
int connectResult = connect(context.GetSocket(), (struct sockaddr *)context.GetClientSockaddr(), context.GetClientSockaddrSize());
if (IS_VALID_CONNECT(connectResult))
{
@@ -271,24 +269,24 @@ int
ClientStateConnecting::Process(ClientThread &client)
{
int retVal;
ClientData &data = client.GetData();
ClientContext &context = client.GetContext();
fd_set writeSet;
struct timeval timeout;
FD_ZERO(&writeSet);
FD_SET(data.sockfd, &writeSet);
FD_SET(context.GetSocket(), &writeSet);
timeout.tv_sec = 0;
timeout.tv_usec = CLIENT_WAIT_TIMEOUT_MSEC * 1000;
int selectResult = select(data.sockfd + 1, NULL, &writeSet, NULL, &timeout);
int selectResult = select(context.GetSocket() + 1, NULL, &writeSet, NULL, &timeout);
if (selectResult > 0) // success
{
// Check whether the connect call succeeded.
int connectResult = 0;
socklen_t tmpSize = sizeof(connectResult);
getsockopt(data.sockfd, SOL_SOCKET, SO_ERROR, (char *)&connectResult, &tmpSize);
getsockopt(context.GetSocket(), SOL_SOCKET, SO_ERROR, (char *)&connectResult, &tmpSize);
if (connectResult != 0)
throw ClientException(ERR_SOCK_CONNECT_FAILED, connectResult);
client.SetState(ClientStateStartSession::Instance());
@@ -323,12 +321,8 @@ ClientStateStartSession::~ClientStateStartSession()
int
ClientStateStartSession::Process(ClientThread &client)
{
client.GetReceiver().Init(client.GetData().sockfd);
client.GetSender().Init(client.GetData().sockfd);
client.GetSender().Run();
boost::shared_ptr<NetPacket> packet(new TestNetPacket(10));
client.GetSender().Send(packet);
client.GetSender().Send(packet, client.GetContext().GetSocket());
client.SetState(ClientStateWaitSession::Instance());
@@ -356,11 +350,11 @@ int
ClientStateWaitSession::Process(ClientThread &client)
{
int retVal;
ClientData &data = client.GetData();
ClientContext &context = client.GetContext();
// delegate to receiver helper class
boost::shared_ptr<NetPacket> tmpPacket = client.GetReceiver().Recv();
boost::shared_ptr<NetPacket> tmpPacket = client.GetReceiver().Recv(context.GetSocket());
if (tmpPacket.get())
{
+58 -21
View File
@@ -19,7 +19,7 @@
#include <net/clientthread.h>
#include <net/clientstate.h>
#include <net/clientdata.h>
#include <net/clientcontext.h>
#include <net/senderthread.h>
#include <net/receiverhelper.h>
#include <net/clientcallback.h>
@@ -32,10 +32,30 @@
using namespace std;
class ClientSenderCallback : public SenderCallback
{
public:
ClientSenderCallback(ClientThread &client) : m_client(client) {}
virtual ~ClientSenderCallback() {}
virtual void SignalNetError(SOCKET sock, int errorID, int osErrorID)
{
// For now, we ignore the socket.
// Just signal the error.
// We assume that the client thread will be terminated.
m_client.GetCallback().SignalNetError(errorID, osErrorID);
}
private:
ClientThread &m_client;
};
ClientThread::ClientThread(ClientCallback &cb)
: m_curState(NULL), m_callback(cb)
{
m_data.reset(new ClientData);
m_context.reset(new ClientContext);
m_senderCallback.reset(new ClientSenderCallback(*this));
}
ClientThread::~ClientThread()
@@ -48,47 +68,57 @@ ClientThread::Init(const string &serverAddress, unsigned serverPort, bool ipv6,
if (IsRunning())
return; // TODO: throw exception
ClientData &data = GetData();
ClientContext &context = GetContext();
data.addrFamily = ipv6 ? AF_INET6 : AF_INET;
data.serverAddr = serverAddress;
data.serverPort = serverPort;
data.password = pwd;
context.SetAddrFamily(ipv6 ? AF_INET6 : AF_INET);
context.SetServerAddr(serverAddress);
context.SetServerPort(serverPort);
context.SetPassword(pwd);
}
ClientCallback &
ClientThread::GetCallback()
{
return m_callback;
}
void
ClientThread::Main()
{
m_sender.reset(new SenderThread(m_callback));
m_receiver.reset(new ReceiverHelper);
SetState(CLIENT_INITIAL_STATE::Instance());
try {
m_sender.reset(new SenderThread(GetSenderCallback()));
m_receiver.reset(new ReceiverHelper);
GetSender().Run();
try
{
while (!ShouldTerminate())
{
int msg = GetState().Process(*this);
if (msg != MSG_SOCK_INTERNAL_PENDING)
m_callback.SignalNetSuccess(msg);
GetCallback().SignalNetSuccess(msg);
}
} catch (const NetException &e)
{
m_callback.SignalNetError(e.GetErrorId(), e.GetOsErrorCode());
GetCallback().SignalNetError(e.GetErrorId(), e.GetOsErrorCode());
}
GetSender().SignalTermination();
GetSender().Join(100);
GetSender().Join(SENDER_THREAD_TERMINATE_TIMEOUT);
}
const ClientData &
ClientThread::GetData() const
const ClientContext &
ClientThread::GetContext() const
{
assert(m_data.get());
return *m_data;
assert(m_context.get());
return *m_context;
}
ClientData &
ClientThread::GetData()
ClientContext &
ClientThread::GetContext()
{
assert(m_data.get());
return *m_data;
assert(m_context.get());
return *m_context;
}
ClientState &
@@ -118,3 +148,10 @@ ClientThread::GetReceiver()
return *m_receiver;
}
ClientSenderCallback &
ClientThread::GetSenderCallback()
{
assert(m_senderCallback.get());
return *m_senderCallback;
}
@@ -17,17 +17,17 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <net/clientdata.h>
#include <net/connectdata.h>
ClientData::ClientData()
: sockfd(INVALID_SOCKET), addrFamily(AF_INET), serverPort(0)
ConnectData::ConnectData()
: m_sockfd(INVALID_SOCKET)
{
bzero(&clientAddr, sizeof(clientAddr));
bzero(&m_sockaddr, sizeof(m_sockaddr));
}
ClientData::~ClientData()
ConnectData::~ConnectData()
{
if (sockfd != INVALID_SOCKET)
CLOSESOCKET(sockfd);
if (m_sockfd != INVALID_SOCKET)
CLOSESOCKET(m_sockfd);
}
+25
View File
@@ -0,0 +1,25 @@
/***************************************************************************
* 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/netcontext.h>
NetContext::~NetContext()
{
}
+4 -4
View File
@@ -45,7 +45,7 @@ ReceiverHelper::Init(SOCKET socket)
}
boost::shared_ptr<NetPacket>
ReceiverHelper::Recv()
ReceiverHelper::Recv(SOCKET sock)
{
boost::shared_ptr<NetPacket> tmpPacket(InternalGetPacket());
@@ -59,18 +59,18 @@ ReceiverHelper::Recv()
struct timeval timeout;
FD_ZERO(&readSet);
FD_SET(m_socket, &readSet);
FD_SET(sock, &readSet);
timeout.tv_sec = 0;
timeout.tv_usec = RECV_TIMEOUT_MSEC * 1000;
int selectResult = select(m_socket + 1, &readSet, NULL, NULL, &timeout);
int selectResult = select(sock + 1, &readSet, NULL, NULL, &timeout);
if (!IS_VALID_SELECT(selectResult))
{
throw NetException(ERR_SOCK_SELECT_FAILED, SOCKET_ERRNO());
}
if (selectResult > 0) // recv is possible
{
int bytesRecvd = recv(m_socket, m_tmpInBuf + m_tmpInBufSize, bufSize, 0);
int bytesRecvd = recv(sock, m_tmpInBuf + m_tmpInBufSize, bufSize, 0);
if (!IS_VALID_RECV(bytesRecvd))
{
+22 -22
View File
@@ -18,7 +18,7 @@
***************************************************************************/
#include <net/resolverthread.h>
#include <net/clientdata.h>
#include <net/clientcontext.h>
#include <net/clientexception.h>
#include <sstream>
@@ -30,7 +30,7 @@ using namespace std;
ResolverThread::ResolverThread()
: m_retVal(false)
{
m_data.reset(new ClientData);
m_context.reset(new ClientContext);
}
ResolverThread::~ResolverThread()
@@ -38,24 +38,24 @@ ResolverThread::~ResolverThread()
}
void
ResolverThread::Init(const ClientData &data)
ResolverThread::Init(const ClientContext &context)
{
if (IsRunning())
return; // TODO: throw exception
m_data->addrFamily = data.addrFamily;
m_data->serverAddr = data.serverAddr;
m_data->serverPort = data.serverPort;
GetContext().SetAddrFamily(context.GetAddrFamily());
GetContext().SetServerAddr(context.GetServerAddr());
GetContext().SetServerPort(context.GetServerPort());
}
bool
ResolverThread::GetResult(ClientData &data)
ResolverThread::GetResult(ClientContext &context) const
{
if (IsRunning())
return false; // TODO: throw exception
if (m_retVal)
memcpy(&data.clientAddr, &GetData().clientAddr, GetData().GetServerAddrSize());
memcpy(context.GetClientSockaddr(), GetContext().GetClientSockaddr(), GetContext().GetClientSockaddrSize());
return m_retVal;
}
@@ -63,34 +63,34 @@ ResolverThread::GetResult(ClientData &data)
void
ResolverThread::Main()
{
const ClientData &data = GetData();
const ClientContext &context = GetContext();
// Convert the port to a string.
ostringstream tmpStr;
tmpStr << data.serverPort;
tmpStr << context.GetServerPort();
// Start the name resolution.
m_retVal = socket_resolve(
data.serverAddr.c_str(),
context.GetServerAddr().c_str(),
tmpStr.str().c_str(),
data.addrFamily,
context.GetAddrFamily(),
SOCK_STREAM,
0,
(struct sockaddr *)&data.clientAddr,
data.GetServerAddrSize());
(struct sockaddr *)context.GetClientSockaddr(),
context.GetClientSockaddrSize());
}
const ClientData &
ResolverThread::GetData() const
const ClientContext &
ResolverThread::GetContext() const
{
assert(m_data.get());
return *m_data;
assert(m_context.get());
return *m_context;
}
ClientData &
ResolverThread::GetData()
ClientContext &
ResolverThread::GetContext()
{
assert(m_data.get());
return *m_data;
assert(m_context.get());
return *m_context;
}
+26
View File
@@ -0,0 +1,26 @@
/***************************************************************************
* 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/sendercallback.h>
SenderCallback::~SenderCallback()
{
}
+23 -25
View File
@@ -18,7 +18,7 @@
***************************************************************************/
#include <net/senderthread.h>
#include <net/netcallback.h>
#include <net/sendercallback.h>
#include <net/socket_msg.h>
#include <cstring>
@@ -26,8 +26,8 @@ using namespace std;
#define SEND_TIMEOUT_MSEC 50
SenderThread::SenderThread(NetCallback &cb)
: m_tmpOutBufSize(0), m_callback(cb)
SenderThread::SenderThread(SenderCallback &cb)
: m_curSocket(INVALID_SOCKET), m_tmpOutBufSize(0), m_callback(cb)
{
}
@@ -36,19 +36,13 @@ SenderThread::~SenderThread()
}
void
SenderThread::Init(SOCKET socket)
SenderThread::Send(boost::shared_ptr<NetPacket> packet, SOCKET sock)
{
if (!IS_VALID_SOCKET(socket) || IsRunning())
return; // TODO: throw exception
m_socket = socket;
}
void
SenderThread::Send(boost::shared_ptr<NetPacket> packet)
{
boost::mutex::scoped_lock lock(m_outBufMutex);
m_outBuf.push_back(packet);
if (packet.get() && IS_VALID_SOCKET(sock))
{
boost::mutex::scoped_lock lock(m_outBufMutex);
m_outBuf.push_back(std::make_pair(packet, sock));
}
}
void
@@ -61,22 +55,26 @@ SenderThread::Main()
// For reasons of simplicity, only one packet is sent at a time.
if (!m_tmpOutBufSize)
{
boost::shared_ptr<NetPacket> tmpPacket;
SendData tmpData;
{
boost::mutex::scoped_lock lock(m_outBufMutex);
if (!m_outBuf.empty())
{
tmpPacket = m_outBuf.front();
tmpData = m_outBuf.front();
m_outBuf.pop_front();
}
}
if (tmpPacket.get())
if (tmpData.first.get())
{
u_int16_t tmpLen = ntohs(tmpPacket->GetData()->length);
if (IS_VALID_SOCKET(tmpData.second))
m_curSocket = tmpData.second;
u_int16_t tmpLen = ntohs(tmpData.first->GetData()->length);
if (tmpLen <= MAX_PACKET_SIZE)
{
m_tmpOutBufSize = tmpLen;
memcpy(m_tmpOutBuf, tmpPacket->GetData(), m_tmpOutBufSize);
memcpy(m_tmpOutBuf, tmpData.first->GetData(), m_tmpOutBufSize);
}
}
}
@@ -86,25 +84,25 @@ SenderThread::Main()
struct timeval timeout;
FD_ZERO(&writeSet);
FD_SET(m_socket, &writeSet);
FD_SET(m_curSocket, &writeSet);
timeout.tv_sec = 0;
timeout.tv_usec = SEND_TIMEOUT_MSEC * 1000;
int selectResult = select(m_socket + 1, NULL, &writeSet, NULL, &timeout);
int selectResult = select(m_curSocket + 1, NULL, &writeSet, NULL, &timeout);
if (!IS_VALID_SELECT(selectResult))
{
m_callback.SignalNetError(ERR_SOCK_SELECT_FAILED, SOCKET_ERRNO());
m_callback.SignalNetError(m_curSocket, ERR_SOCK_SELECT_FAILED, SOCKET_ERRNO());
// Assume that this is a fatal error, terminate thread.
return;
}
if (selectResult > 0) // send is possible
{
// send next chunk of data
int bytesSent = send(m_socket, m_tmpOutBuf, m_tmpOutBufSize, 0);
int bytesSent = send(m_curSocket, m_tmpOutBuf, m_tmpOutBufSize, 0);
if (!IS_VALID_SEND(bytesSent))
{
m_callback.SignalNetError(ERR_SOCK_SEND_FAILED, SOCKET_ERRNO());
m_callback.SignalNetError(m_curSocket, ERR_SOCK_SEND_FAILED, SOCKET_ERRNO());
// Assume that this is a fatal error, terminate thread.
return;
}
+52
View File
@@ -0,0 +1,52 @@
/***************************************************************************
* 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/servercontext.h>
ServerContext::ServerContext()
: m_sockfd(INVALID_SOCKET), m_addrFamily(AF_INET), m_serverPort(0)
{
bzero(&m_serverSockaddr, sizeof(m_serverSockaddr));
}
ServerContext::~ServerContext()
{
if (m_sockfd != INVALID_SOCKET)
CLOSESOCKET(m_sockfd);
}
SOCKET
ServerContext::GetSocket() const
{
return m_sockfd;
}
u_int32_t
ServerContext::GetId() const
{
// Id is unused for main server thread.
return 0;
}
void
ServerContext::SetSocket(SOCKET sockfd)
{
m_sockfd = sockfd;
}
+26
View File
@@ -0,0 +1,26 @@
/***************************************************************************
* 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/serverexception.h>
ServerException::~ServerException()
{
}
+63
View File
@@ -0,0 +1,63 @@
/***************************************************************************
* 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/serverrecvstate.h>
#include <net/serverrecvthread.h>
#include <net/socket_msg.h>
using namespace std;
#define SERVER_WAIT_TIMEOUT_MSEC 50
ServerRecvState::~ServerRecvState()
{
}
//-----------------------------------------------------------------------------
ServerRecvStateInit &
ServerRecvStateInit::Instance()
{
static ServerRecvStateInit state;
return state;
}
ServerRecvStateInit::ServerRecvStateInit()
{
}
ServerRecvStateInit::~ServerRecvStateInit()
{
}
void
ServerRecvStateInit::HandleNewConnection(ServerRecvThread &server, boost::shared_ptr<ConnectData> data)
{
}
int
ServerRecvStateInit::Process(ServerRecvThread &server)
{
Thread::Msleep(SERVER_WAIT_TIMEOUT_MSEC);
return MSG_SOCK_INIT_DONE;
}
//-----------------------------------------------------------------------------
+129
View File
@@ -0,0 +1,129 @@
/***************************************************************************
* 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/serverrecvthread.h>
#include <net/serverexception.h>
#include <net/serverrecvstate.h>
#include <net/senderthread.h>
#include <net/sendercallback.h>
#include <net/receiverhelper.h>
#include <net/socket_msg.h>
class ServerSenderCallback : public SenderCallback
{
public:
ServerSenderCallback(ServerRecvThread &server) : m_server(server) {}
virtual ~ServerSenderCallback() {}
virtual void SignalNetError(SOCKET sock, int errorID, int osErrorID)
{
// TODO
}
private:
ServerRecvThread &m_server;
};
ServerRecvThread::ServerRecvThread()
{
m_senderCallback.reset(new ServerSenderCallback(*this));
}
ServerRecvThread::~ServerRecvThread()
{
}
void
ServerRecvThread::AddConnection(boost::shared_ptr<ConnectData> data)
{
boost::mutex::scoped_lock lock(m_connectQueueMutex);
m_connectQueue.push_back(data);
}
void
ServerRecvThread::Main()
{
SetState(SERVER_INITIAL_STATE::Instance());
m_sender.reset(new SenderThread(GetSenderCallback()));
m_receiver.reset(new ReceiverHelper);
GetSender().Run();
try
{
while (!ShouldTerminate())
{
{
boost::shared_ptr<ConnectData> tmpData;
{
boost::mutex::scoped_lock lock(m_connectQueueMutex);
if (!m_connectQueue.empty())
{
tmpData = m_connectQueue.front();
m_connectQueue.pop_front();
}
}
if (tmpData.get())
GetState().HandleNewConnection(*this, tmpData);
}
GetState().Process(*this);
}
} catch (const NetException &)
{
// TODO
}
GetSender().SignalTermination();
GetSender().Join(SENDER_THREAD_TERMINATE_TIMEOUT);
}
ServerRecvState &
ServerRecvThread::GetState()
{
assert(m_curState);
return *m_curState;
}
void
ServerRecvThread::SetState(ServerRecvState &newState)
{
m_curState = &newState;
}
SenderThread &
ServerRecvThread::GetSender()
{
assert(m_sender.get());
return *m_sender;
}
ReceiverHelper &
ServerRecvThread::GetReceiver()
{
assert(m_receiver.get());
return *m_receiver;
}
ServerSenderCallback &
ServerRecvThread::GetSenderCallback()
{
assert(m_senderCallback.get());
return *m_senderCallback;
}
+131 -28
View File
@@ -18,11 +18,20 @@
***************************************************************************/
#include <net/serverthread.h>
#include <net/servercontext.h>
#include <net/connectdata.h>
#include <net/serverrecvthread.h>
#include <net/socket_helper.h>
#include <net/serverexception.h>
#include <net/socket_msg.h>
#define ACCEPT_TIMEOUT_MSEC 50
#define NET_SERVER_LISTEN_BACKLOG 5
ServerThread::ServerThread()
{
m_context.reset(new ServerContext);
}
ServerThread::~ServerThread()
@@ -30,44 +39,138 @@ ServerThread::~ServerThread()
}
void
ServerThread::Init()
ServerThread::Init(unsigned serverPort, bool ipv6, const std::string &pwd)
{
if (IsRunning())
return; // TODO: throw exception
ServerContext &context = GetContext();
context.SetAddrFamily(ipv6 ? AF_INET6 : AF_INET);
context.SetServerPort(serverPort);
context.SetPassword(pwd);
}
void
ServerThread::Main()
{
while (!ShouldTerminate())
m_recvThread.reset(new ServerRecvThread);
try
{
// 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;
socklen_t addrSize;
Listen();
GetRecvThread().Run();
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);
while (!ShouldTerminate())
{
// The main server thread is simple. It only accepts connections.
AcceptLoop();
}
} catch (const NetException &)
{
// TODO: callback.
}
}
void
ServerThread::Listen()
{
ServerContext &context = GetContext();
// if (context.GetServerPort() < 1024)
// throw ServerException(ERR_SOCK_INVALID_PORT, 0);
context.SetSocket(socket(context.GetAddrFamily(), SOCK_STREAM, 0));
if (!IS_VALID_SOCKET(context.GetSocket()))
throw ServerException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO());
unsigned long mode = 1;
if (IOCTLSOCKET(context.GetSocket(), FIONBIO, &mode) == SOCKET_ERROR)
throw ServerException(ERR_SOCK_CREATION_FAILED, SOCKET_ERRNO());
context.GetServerSockaddr()->ss_family = context.GetAddrFamily();
if (!socket_string_to_addr(
"0.0.0.0",
context.GetAddrFamily(),
(struct sockaddr *)context.GetServerSockaddr(),
context.GetServerSockaddrSize()))
{
throw ServerException(ERR_SOCK_SET_ADDR_FAILED, 0);
}
if (!socket_set_port(
context.GetServerPort(),
context.GetAddrFamily(),
(struct sockaddr *)context.GetServerSockaddr(),
context.GetServerSockaddrSize()))
{
throw ServerException(ERR_SOCK_SET_PORT_FAILED, 0);
}
if (!IS_VALID_BIND(bind(
context.GetSocket(),
(const struct sockaddr *)context.GetServerSockaddr(),
context.GetServerSockaddrSize())))
{
throw ServerException(ERR_SOCK_BIND_FAILED, SOCKET_ERRNO());
}
if (!IS_VALID_LISTEN(listen(context.GetSocket(), NET_SERVER_LISTEN_BACKLOG)))
{
throw ServerException(ERR_SOCK_LISTEN_FAILED, SOCKET_ERRNO());
}
}
void
ServerThread::AcceptLoop()
{
ServerContext &context = GetContext();
fd_set readSet;
struct timeval timeout;
FD_ZERO(&readSet);
FD_SET(context.GetSocket(), &readSet);
timeout.tv_sec = 0;
timeout.tv_usec = ACCEPT_TIMEOUT_MSEC * 1000;
int selectResult = select(context.GetSocket() + 1, &readSet, NULL, NULL, &timeout);
if (!IS_VALID_SELECT(selectResult))
{
throw ServerException(ERR_SOCK_SELECT_FAILED, SOCKET_ERRNO());
}
if (selectResult > 0) // accept is possible
{
boost::shared_ptr<ConnectData> tmpData(new ConnectData);
socklen_t addrSize = sizeof(*tmpData->GetSockaddr());
tmpData->SetSocket(accept(context.GetSocket(), (struct sockaddr *)tmpData->GetSockaddr(), &addrSize));
if (!IS_VALID_SOCKET(tmpData->GetSocket()))
{
throw ServerException(ERR_SOCK_ACCEPT_FAILED, SOCKET_ERRNO());
}
GetRecvThread().AddConnection(tmpData);
}
}
const ServerContext &
ServerThread::GetContext() const
{
assert(m_context.get());
return *m_context;
}
ServerContext &
ServerThread::GetContext()
{
assert(m_context.get());
return *m_context;
}
ServerRecvThread &
ServerThread::GetRecvThread()
{
assert(m_recvThread.get());
return *m_recvThread;
}