First test version with network timeout. Only activity timeout and global timeout, no game admin timeout yet. Set to test values (will disconnect rapidly).

Modified method for init timeout (if session cannot be established) - now using one non-modifiable timer for each session, and an additional timer which is reset whenever session data is received.
This commit is contained in:
lotodore
2008-03-04 23:47:45 +00:00
parent 6d7e6d9a94
commit 9f0d204f56
21 changed files with 158 additions and 53 deletions
+2 -1
View File
@@ -39,6 +39,7 @@ public:
virtual void SignalNetClientError(int errorID, int osErrorID) = 0;
virtual void SignalNetClientNotification(int notificationId) = 0;
virtual void SignalNetClientStatsUpdate(const ServerStats &stats) = 0;
virtual void SignalNetClientShowTimeoutDialog(NetTimeoutReason reason, unsigned remainingSec) = 0;
virtual void SignalNetClientRemovedFromGame(int notificationId) = 0;
virtual void SignalNetClientGameListNew(unsigned gameId) = 0;
@@ -57,6 +58,6 @@ public:
virtual void SignalNetClientChatMsg(const std::string &playerName, const std::string &msg) = 0;
virtual void SignalNetClientWaitDialog() = 0;
};
};
#endif
+1
View File
@@ -65,6 +65,7 @@ public:
void SendJoinFirstGame(const std::string &password);
void SendJoinGame(unsigned gameId, const std::string &password);
void SendCreateGame(const GameData &gameData, const std::string &name, const std::string &password);
void SendResetTimeout();
GameInfo GetGameInfo(unsigned gameId) const;
PlayerInfo GetPlayerInfo(unsigned playerId) const;
+6
View File
@@ -417,6 +417,12 @@ AbstractClientStateReceiving::Process(ClientThread &client)
client.GetCallback().SignalNetClientRemovedFromGame(removedData.removeReason);
client.SetState(ClientStateWaitJoin::Instance());
}
else if (tmpPacket->ToNetPacketTimeoutWarning())
{
NetPacketTimeoutWarning::Data warningData;
tmpPacket->ToNetPacketTimeoutWarning()->GetData(warningData);
client.GetCallback().SignalNetClientShowTimeoutDialog(warningData.timeoutReason, warningData.remainingSeconds);
}
else if (tmpPacket->ToNetPacketChatText())
{
// Chat message - display it in the GUI.
+8
View File
@@ -241,6 +241,14 @@ ClientThread::SendCreateGame(const GameData &gameData, const std::string &name,
}
}
void
ClientThread::SendResetTimeout()
{
boost::shared_ptr<NetPacket> reset(new NetPacketResetTimeout);
boost::mutex::scoped_lock lock(m_outPacketListMutex);
m_outPacketList.push_back(reset);
}
GameInfo
ClientThread::GetGameInfo(unsigned gameId) const
{
+2
View File
@@ -4354,6 +4354,7 @@ NetPacketTimeoutWarning::SetData(const NetPacketTimeoutWarning::Data &inData)
tmpData->timeoutReason = htons(NET_TIMEOUT_OTHER_REASON);
break;
}
tmpData->remainingSeconds = htons(inData.remainingSeconds);
// Check the packet - just in case.
Check(GetRawData());
@@ -4374,6 +4375,7 @@ NetPacketTimeoutWarning::GetData(NetPacketTimeoutWarning::Data &outData) const
outData.timeoutReason = NETWORK_TIMEOUT_GENERIC;
break;
}
outData.remainingSeconds = ntohs(tmpData->remainingSeconds);
}
const NetPacketTimeoutWarning *
+2
View File
@@ -175,6 +175,8 @@ AbstractServerGameStateReceiving::Process(ServerGameThread &server)
// Delegate to Lobby.
server.GetLobbyThread().HandleGameRetrieveAvatar(session, *packet->ToNetPacketRetrieveAvatar());
}
else if (packet->ToNetPacketResetTimeout())
{}
else if (packet->ToNetPacketLeaveCurrentGame())
{
server.MoveSessionToLobby(session, NTF_NET_REMOVED_ON_REQUEST);
+47 -38
View File
@@ -34,11 +34,15 @@
#include <boost/filesystem.hpp>
#include <boost/bind.hpp>
#define SERVER_MAX_NUM_SESSIONS 512 // Maximum number of idle users in lobby.
#define SERVER_CACHE_CLEANUP_INTERVAL_SEC 86400 // 1 day
#define SERVER_SAVE_STATISTICS_INTERVAL_SEC 60
#define SERVER_INIT_SESSION_TIMEOUT_SEC 20
#define SERVER_INIT_AVATAR_CLIENT_LOCK_SEC 30 // Forbid a client to send an additional avatar.
#define SERVER_MAX_NUM_SESSIONS 512 // Maximum number of idle users in lobby.
#define SERVER_CACHE_CLEANUP_INTERVAL_SEC 86400 // 1 day
#define SERVER_SAVE_STATISTICS_INTERVAL_SEC 60
#define SERVER_INIT_AVATAR_CLIENT_LOCK_SEC 30 // Forbid a client to send an additional avatar.
#define SERVER_INIT_SESSION_TIMEOUT_SEC 20
#define SERVER_TIMEOUT_WARNING_REMAINING_SEC 60
#define SERVER_SESSION_ACTIVITY_TIMEOUT_SEC 90 // MUST be > SERVER_TIMEOUT_WARNING_REMAINING_SEC
#define SERVER_SESSION_FORCED_TIMEOUT_SEC 300 // Should be quite large.
#define SERVER_STATISTICS_FILE_NAME "server_statistics.log"
#define SERVER_STATISTICS_STR_TOTAL_PLAYERS "TotalNumPlayersLoggedIn"
@@ -142,10 +146,6 @@ ServerLobbyThread::RemoveSessionFromGame(SessionWrapper session)
void
ServerLobbyThread::CloseSession(SessionWrapper session)
{
{
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
m_initTimerSessionMap.erase(session.sessionData->GetId());
}
m_sessionManager.RemoveSession(session.sessionData->GetId());
m_gameSessionManager.RemoveSession(session.sessionData->GetId());
@@ -308,12 +308,13 @@ ServerLobbyThread::Main()
NewSessionLoop();
// Main loop.
ProcessLoop();
// Close sessions.
CloseSessionLoop();
// Remove games.
RemoveGameLoop();
// Kick players.
KickPlayerLoop();
// Check session timeouts.
m_sessionManager.ForEachRemoveIf(boost::bind(&ServerLobbyThread::CheckSessionTimeouts, boost::ref(*this), _1));
m_gameSessionManager.ForEachRemoveIf(boost::bind(&ServerLobbyThread::CheckSessionTimeouts, boost::ref(*this), _1));
// Update avatar limitation lock.
UpdateAvatarClientTimerLoop();
// Cleanup cache.
@@ -383,6 +384,8 @@ ServerLobbyThread::ProcessLoop()
HandleNetPacketRetrievePlayerInfo(session, *packet->ToNetPacketRetrievePlayerInfo());
else if (packet->ToNetPacketRetrieveAvatar())
HandleNetPacketRetrieveAvatar(session, *packet->ToNetPacketRetrieveAvatar());
else if (packet->ToNetPacketResetTimeout())
{}
else if (packet->ToNetPacketCreateGame())
HandleNetPacketCreateGame(session, *packet->ToNetPacketCreateGame());
else if (packet->ToNetPacketJoinGame())
@@ -697,10 +700,6 @@ ServerLobbyThread::EstablishSession(SessionWrapper session)
SendGameList(session.sessionData);
// Session is now established.
{
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
m_initTimerSessionMap.erase(session.sessionData->GetId());
}
session.sessionData->SetState(SessionData::Established);
{
@@ -764,27 +763,6 @@ ServerLobbyThread::NewSessionLoop()
HandleReAddedSession(tmpSession);
}
void
ServerLobbyThread::CloseSessionLoop()
{
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
TimerSessionMap::iterator i = m_initTimerSessionMap.begin();
TimerSessionMap::iterator end = m_initTimerSessionMap.end();
// Remove sessions if they do not initialize within a certain period.
while (i != end)
{
TimerSessionMap::iterator next = i;
++next;
if (i->second.elapsed().total_seconds() > SERVER_INIT_SESSION_TIMEOUT_SEC)
{
m_sessionManager.RemoveSession(i->first);
m_initTimerSessionMap.erase(i);
}
i = next;
}
}
void
ServerLobbyThread::RemoveGameLoop()
{
@@ -956,8 +934,6 @@ ServerLobbyThread::HandleNewConnection(boost::shared_ptr<ConnectData> connData)
tmpAddress[sizeof(tmpAddress) - 1] = 0; // paranoia
sessionData->SetClientAddr(tmpAddress);
}
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
m_initTimerSessionMap[sessionData->GetId()] = boost::timers::portable::microsec_timer();
}
else
{
@@ -987,6 +963,39 @@ ServerLobbyThread::HandleReAddedSession(SessionWrapper session)
}
}
bool
ServerLobbyThread::CheckSessionTimeouts(SessionWrapper session)
{
bool retVal = false;
if (session.sessionData.get())
{
if (session.sessionData->GetState() == SessionData::Init && session.sessionData->GetAutoDisconnectTimerElapsedSec() >= SERVER_INIT_SESSION_TIMEOUT_SEC)
retVal = true;
else if (session.sessionData->GetActivityTimerElapsedSec() >= SERVER_SESSION_ACTIVITY_TIMEOUT_SEC - SERVER_TIMEOUT_WARNING_REMAINING_SEC
&& !session.sessionData->HasActivityNoticeBeenSent())
{
session.sessionData->MarkActivityNotice();
boost::shared_ptr<NetPacket> packet(new NetPacketTimeoutWarning);
NetPacketTimeoutWarning::Data warningData;
warningData.timeoutReason = NETWORK_TIMEOUT_GENERIC;
warningData.remainingSeconds = SERVER_TIMEOUT_WARNING_REMAINING_SEC;
static_cast<NetPacketTimeoutWarning *>(packet.get())->SetData(warningData);
GetSender().Send(session.sessionData, packet);
}
else if (session.sessionData->GetActivityTimerElapsedSec() >= SERVER_SESSION_ACTIVITY_TIMEOUT_SEC)
{
// TODO SendError(session.sessionData, errorCode);
retVal = true;
}
else if (session.sessionData->GetAutoDisconnectTimerElapsedSec() >= SERVER_SESSION_FORCED_TIMEOUT_SEC)
{
// TODO SendError(session.sessionData, errorCode);
retVal = true;
}
}
return retVal;
}
void
ServerLobbyThread::CleanupConnectQueue()
{
+39 -1
View File
@@ -20,7 +20,8 @@
#include <net/sessiondata.h>
SessionData::SessionData(SOCKET sockfd, SessionId id)
: m_sockfd(sockfd), m_id(id), m_state(SessionData::Init), m_readyFlag(false)
: m_sockfd(sockfd), m_id(id), m_state(SessionData::Init), m_readyFlag(false),
m_activityTimeoutNoticeSent(false)
{
}
@@ -100,3 +101,40 @@ SessionData::GetReceiveBuffer()
return m_receiveBuffer;
}
void
SessionData::ResetActivityTimer()
{
boost::mutex::scoped_lock lock(m_dataMutex);
m_activityTimeoutNoticeSent = false;
m_activityTimer.reset();
m_activityTimer.start();
}
unsigned
SessionData::GetActivityTimerElapsedSec() const
{
boost::mutex::scoped_lock lock(m_dataMutex);
return m_activityTimer.elapsed().total_seconds();
}
bool
SessionData::HasActivityNoticeBeenSent() const
{
boost::mutex::scoped_lock lock(m_dataMutex);
return m_activityTimeoutNoticeSent;
}
void
SessionData::MarkActivityNotice()
{
boost::mutex::scoped_lock lock(m_dataMutex);
m_activityTimeoutNoticeSent = true;
}
unsigned
SessionData::GetAutoDisconnectTimerElapsedSec() const
{
boost::mutex::scoped_lock lock(m_dataMutex);
return m_autoDisconnectTimer.elapsed().total_seconds();
}
+20
View File
@@ -147,6 +147,8 @@ SessionManager::Select(unsigned timeoutMsec)
}
}
}
if (retSession.sessionData.get())
retSession.sessionData->ResetActivityTimer();
return retSession;
}
@@ -294,6 +296,24 @@ SessionManager::ForEach(boost::function<void (SessionWrapper)> func)
}
}
void
SessionManager::ForEachRemoveIf(boost::function<bool (SessionWrapper)> func)
{
boost::mutex::scoped_lock lock(m_sessionMapMutex);
SessionMap::iterator i = m_sessionMap.begin();
SessionMap::iterator end = m_sessionMap.end();
while (i != end)
{
SessionMap::iterator next = i;
next++;
if (func((*i).second))
m_sessionMap.erase(i);
i = next;
}
}
unsigned
SessionManager::CountReadySessions() const
{
+1 -5
View File
@@ -113,7 +113,6 @@ protected:
void RequestPlayerAvatar(SessionWrapper session);
void NewConnectionLoop();
void NewSessionLoop();
void CloseSessionLoop();
void RemoveGameLoop();
void KickPlayerLoop();
void UpdateAvatarClientTimerLoop();
@@ -128,7 +127,7 @@ protected:
void HandleNewConnection(boost::shared_ptr<ConnectData> connData);
void HandleReAddedSession(SessionWrapper session);
SOCKET Select();
bool CheckSessionTimeouts(SessionWrapper session);
void CleanupConnectQueue();
void CleanupSessionMap();
@@ -166,9 +165,6 @@ private:
SessionManager m_sessionManager;
SessionManager m_gameSessionManager;
TimerSessionMap m_initTimerSessionMap;
mutable boost::mutex m_initTimerSessionMapMutex;
TimerClientAddressMap m_timerAvatarClientAddressMap;
mutable boost::mutex m_timerAvatarClientAddressMapMutex;
+10
View File
@@ -25,6 +25,7 @@
#include <net/receivebuffer.h>
#include <string>
#include <boost/thread.hpp>
#include <core/boost/timers.hpp>
#define INVALID_SESSION 0
#define SESSION_ID_INIT INVALID_SESSION
@@ -60,6 +61,12 @@ public:
ReceiveBuffer &GetReceiveBuffer();
void ResetActivityTimer();
unsigned GetActivityTimerElapsedSec() const;
bool HasActivityNoticeBeenSent() const;
void MarkActivityNotice();
unsigned GetAutoDisconnectTimerElapsedSec() const;
private:
SOCKET m_sockfd;
const SessionId m_id;
@@ -67,6 +74,9 @@ private:
std::string m_clientAddr;
ReceiveBuffer m_receiveBuffer;
bool m_readyFlag;
boost::timers::portable::microsec_timer m_activityTimer;
bool m_activityTimeoutNoticeSent;
boost::timers::portable::microsec_timer m_autoDisconnectTimer;
mutable boost::mutex m_dataMutex;
};
+1
View File
@@ -66,6 +66,7 @@ public:
bool IsPlayerConnected(unsigned uniqueId) const;
void ForEach(boost::function<void (SessionWrapper)> func);
void ForEachRemoveIf(boost::function<bool (SessionWrapper)> func);
unsigned CountReadySessions() const;
void ResetAllReadyFlags();