added better handling of network connections:

- only listen for new clients on a server if an open slot exists (unconnected)
- for both server and client we wait a max of 10 seconds to receive a proper intro packet or we disconnect (could have connected to / from a non glest application using the same port)
This commit is contained in:
Mark Vejvoda 2010-05-12 15:25:56 +00:00
parent d064b52418
commit 319b672e20
11 changed files with 152 additions and 52 deletions

View File

@ -39,6 +39,7 @@ public:
static const int cameraFps= 100;
static const int networkFramePeriod= 10;
static const int networkExtraLatency= 200;
static const int maxClientConnectHandshakeSecs= 10;
static const char *folder_path_maps;
static const char *folder_path_scenarios;

View File

@ -44,6 +44,7 @@ ClientInterface::ClientInterface(){
introDone= false;
playerIndex= -1;
gameSettingsReceived=false;
gotIntro = false;
networkGameDataSynchCheckOkMap = false;
networkGameDataSynchCheckOkTile = false;
@ -78,6 +79,7 @@ void ClientInterface::connect(const Ip &ip, int port)
clientSocket= new ClientSocket();
clientSocket->setBlock(false);
clientSocket->connect(ip, port);
connectedTime = time(NULL);
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] END - socket = %d\n",__FILE__,__FUNCTION__,clientSocket->getSocketId());
}
@ -133,13 +135,13 @@ void ClientInterface::updateLobby()
{
NetworkMessageIntro networkMessageIntro;
if(receiveMessage(&networkMessageIntro))
{
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] got NetworkMessageIntro\n",__FILE__,__FUNCTION__);
if(receiveMessage(&networkMessageIntro)) {
gotIntro = true;
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] got NetworkMessageIntro, networkMessageIntro.getGameState() = %d\n",__FILE__,__FUNCTION__,networkMessageIntro.getGameState());
//check consistency
if(networkMessageIntro.getVersionString() != getNetworkVersionString())
{
if(networkMessageIntro.getVersionString() != getNetworkVersionString()) {
bool versionMatched = false;
string platformFreeVersion = getNetworkPlatformFreeVersionString();
string sErr = "";
@ -165,8 +167,8 @@ void ClientInterface::updateLobby()
sendTextMessage(" Client: "+ getNetworkVersionString(),-1);
}
if(Config::getInstance().getBool("PlatformConsistencyChecks","true") && versionMatched == false)
{// error message and disconnect only if checked
if(Config::getInstance().getBool("PlatformConsistencyChecks","true") &&
versionMatched == false) { // error message and disconnect only if checked
DisplayErrorMessage(sErr);
quit= true;
close();
@ -174,15 +176,31 @@ void ClientInterface::updateLobby()
}
}
//send intro message
NetworkMessageIntro sendNetworkMessageIntro(getNetworkVersionString(), Config::getInstance().getString("NetPlayerName",Socket::getHostName().c_str()), -1);
if(networkMessageIntro.getGameState() == nmgstOk) {
//send intro message
NetworkMessageIntro sendNetworkMessageIntro(getNetworkVersionString(), Config::getInstance().getString("NetPlayerName",Socket::getHostName().c_str()), -1, nmgstOk);
playerIndex= networkMessageIntro.getPlayerIndex();
serverName= networkMessageIntro.getName();
sendMessage(&sendNetworkMessageIntro);
playerIndex= networkMessageIntro.getPlayerIndex();
serverName= networkMessageIntro.getName();
sendMessage(&sendNetworkMessageIntro);
assert(playerIndex>=0 && playerIndex<GameConstants::maxPlayers);
introDone= true;
assert(playerIndex>=0 && playerIndex<GameConstants::maxPlayers);
introDone= true;
}
else if(networkMessageIntro.getGameState() == nmgstNoSlots) {
string sErr = "Cannot join the server because there are no open slots for new players.";
DisplayErrorMessage(sErr);
quit= true;
close();
return;
}
else {
string sErr = "Unknown response from server: " + intToStr(networkMessageIntro.getGameState());
DisplayErrorMessage(sErr);
quit= true;
close();
return;
}
}
}
break;
@ -399,6 +417,11 @@ void ClientInterface::updateLobby()
close();
}
}
if(gotIntro == false && difftime(time(NULL),connectedTime) > GameConstants::maxClientConnectHandshakeSecs) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] difftime(time(NULL),connectedTime) = %d\n",__FILE__,__FUNCTION__,__LINE__,difftime(time(NULL),connectedTime));
close();
}
}
void ClientInterface::updateKeyframe(int frameCount)
@ -699,6 +722,9 @@ void ClientInterface::close()
delete clientSocket;
clientSocket= NULL;
connectedTime = 0;
gotIntro = false;
}
void ClientInterface::discoverServers(DiscoveredServersInterface *cb) {

View File

@ -42,6 +42,8 @@ private:
bool launchGame;
int playerIndex;
bool gameSettingsReceived;
time_t connectedTime;
bool gotIntro;
Ip ip;
int port;

View File

@ -39,6 +39,7 @@ ConnectionSlot::ConnectionSlot(ServerInterface* serverInterface, int playerIndex
this->playerIndex= playerIndex;
socket= NULL;
ready= false;
gotIntro = false;
networkGameDataSynchCheckOkMap = false;
networkGameDataSynchCheckOkTile = false;
@ -77,20 +78,36 @@ void ConnectionSlot::update(bool checkForNewClients)
//if(serverInterface->getServerSocket()->isReadable() == true)
if(checkForNewClients == true)
{
socket = serverInterface->getServerSocket()->accept();
//SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] BEFORE accept new client connection, serverInterface->getOpenSlotCount() = %d\n",__FILE__,__FUNCTION__,serverInterface->getOpenSlotCount());
bool hasOpenSlots = (serverInterface->getOpenSlotCount() > 0);
if(serverInterface->getServerSocket()->hasDataToRead() == true) {
socket = serverInterface->getServerSocket()->accept();
serverInterface->updateListen();
}
//send intro message when connected
if(socket != NULL) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] accepted new client connection, serverInterface->getOpenSlotCount() = %d\n",__FILE__,__FUNCTION__,serverInterface->getOpenSlotCount());
connectedTime = time(NULL);
//send intro message when connected
if(socket != NULL)
{
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] accepted new client connection\n",__FILE__,__FUNCTION__);
chatText.clear();
chatSender.clear();
chatTeamIndex= -1;
chatText.clear();
chatSender.clear();
chatTeamIndex= -1;
if(hasOpenSlots == false) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] no open slots, disconnecting client\n",__FILE__,__FUNCTION__);
NetworkMessageIntro networkMessageIntro(getNetworkVersionString(), socket->getHostName(), playerIndex);
sendMessage(&networkMessageIntro);
}
NetworkMessageIntro networkMessageIntro(getNetworkVersionString(), socket->getHostName(), playerIndex, nmgstNoSlots);
sendMessage(&networkMessageIntro);
close();
}
else {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] client will be assigned to the next open slot\n",__FILE__,__FUNCTION__);
NetworkMessageIntro networkMessageIntro(getNetworkVersionString(), socket->getHostName(), playerIndex, nmgstOk);
sendMessage(&networkMessageIntro);
}
}
}
}
else
@ -144,12 +161,12 @@ void ConnectionSlot::update(bool checkForNewClients)
//process intro messages
case nmtIntro:
{
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] got nmtIntro\n",__FILE__,__FUNCTION__);
NetworkMessageIntro networkMessageIntro;
if(receiveMessage(&networkMessageIntro))
{
gotIntro = true;
name= networkMessageIntro.getName();
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] got name [%s]\n",__FILE__,__FUNCTION__,name.c_str());
@ -189,11 +206,7 @@ void ConnectionSlot::update(bool checkForNewClients)
}
//tileset
//world.loadTileset(config.getPathListForType(ptTilesets,scenarioDir), tilesetName, &checksum);
//int32 tilesetCRC = getFolderTreeContentsCheckSumRecursively(string(GameConstants::folder_path_tilesets) + "/" + serverInterface->getGameSettings()->getTileset() + "/*", ".xml", NULL);
int32 tilesetCRC = getFolderTreeContentsCheckSumRecursively(config.getPathListForType(ptTilesets,scenarioDir), string("/") + serverInterface->getGameSettings()->getTileset() + string("/*"), ".xml", NULL);
//int32 techCRC = getFolderTreeContentsCheckSumRecursively(string(GameConstants::folder_path_techs) + "/" + serverInterface->getGameSettings()->getTech() + "/*", ".xml", NULL);
int32 techCRC = getFolderTreeContentsCheckSumRecursively(config.getPathListForType(ptTechs,scenarioDir), "/" + serverInterface->getGameSettings()->getTech() + "/*", ".xml", NULL);
Checksum checksum;
string file = Map::getMapPath(serverInterface->getGameSettings()->getMap(),scenarioDir);
@ -342,6 +355,11 @@ void ConnectionSlot::update(bool checkForNewClients)
return;
}
}
if(gotIntro == false && difftime(time(NULL),connectedTime) > GameConstants::maxClientConnectHandshakeSecs) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] difftime(time(NULL),connectedTime) = %d\n",__FILE__,__FUNCTION__,__LINE__,difftime(time(NULL),connectedTime));
close();
}
}
else
{
@ -362,6 +380,10 @@ void ConnectionSlot::close()
chatText.clear();
chatSender.clear();
chatTeamIndex= -1;
ready = false;
gotIntro = false;
serverInterface->updateListen();
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] END\n",__FILE__,__FUNCTION__);
}

View File

@ -13,10 +13,9 @@
#define _GLEST_GAME_CONNECTIONSLOT_H_
#include <vector>
#include "socket.h"
#include "network_interface.h"
#include <time.h>
using Shared::Platform::ServerSocket;
using Shared::Platform::Socket;
@ -39,6 +38,8 @@ private:
bool ready;
vector<std::pair<string,int32> > vctFileList;
bool receivedNetworkGameStatus;
time_t connectedTime;
bool gotIntro;
public:
ConnectionSlot(ServerInterface* serverInterface, int playerIndex);

View File

@ -119,13 +119,15 @@ void NetworkMessage::send(Socket* socket, const void* data, int dataSize) const
NetworkMessageIntro::NetworkMessageIntro(){
data.messageType= -1;
data.playerIndex= -1;
data.gameState = nmgstInvalid;
}
NetworkMessageIntro::NetworkMessageIntro(const string &versionString, const string &name, int playerIndex){
data.messageType=nmtIntro;
data.versionString= versionString;
data.name= name;
data.playerIndex= static_cast<int16>(playerIndex);
NetworkMessageIntro::NetworkMessageIntro(const string &versionString, const string &name, int playerIndex, NetworkGameStateType gameState) {
data.messageType = nmtIntro;
data.versionString = versionString;
data.name = name;
data.playerIndex = static_cast<int16>(playerIndex);
data.gameState = static_cast<int8>(gameState);
}
bool NetworkMessageIntro::receive(Socket* socket){

View File

@ -45,6 +45,14 @@ enum NetworkMessageType {
nmtCount
};
enum NetworkGameStateType {
nmgstInvalid,
nmgstOk,
nmgstNoSlots,
nmgstCount
};
const int32 commandListHeaderSize = 6;
// =====================================================
@ -81,6 +89,7 @@ private:
NetworkString<maxVersionStringSize> versionString;
NetworkString<maxNameSize> name;
int16 playerIndex;
int8 gameState;
};
private:
@ -88,11 +97,12 @@ private:
public:
NetworkMessageIntro();
NetworkMessageIntro(const string &versionString, const string &name, int playerIndex);
NetworkMessageIntro(const string &versionString, const string &name, int playerIndex, NetworkGameStateType gameState);
string getVersionString() const {return data.versionString.getString();}
string getName() const {return data.name.getString();}
int getPlayerIndex() const {return data.playerIndex;}
string getVersionString() const { return data.versionString.getString(); }
string getName() const { return data.name.getString(); }
int getPlayerIndex() const { return data.playerIndex; }
NetworkGameStateType getGameState() const { return static_cast<NetworkGameStateType>(data.gameState); }
virtual bool receive(Socket* socket);
virtual void send(Socket* socket) const;

View File

@ -602,7 +602,6 @@ void ServerInterface::broadcastMessageToConnectedClients(const NetworkMessage* n
void ServerInterface::updateListen()
{
int openSlotCount= 0;
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
if(slots[i] != NULL && slots[i]->isConnected() == false)
@ -614,6 +613,20 @@ void ServerInterface::updateListen()
serverSocket.listen(openSlotCount);
}
int ServerInterface::getOpenSlotCount() {
int openSlotCount= 0;
for(int i= 0; i<GameConstants::maxPlayers; ++i)
{
if(slots[i] != NULL && slots[i]->isConnected() == false)
{
++openSlotCount;
}
}
return openSlotCount;
}
void ServerInterface::setGameSettings(GameSettings *serverGameSettings, bool waitForClientAck)
{
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] START gameSettingsUpdateCount = %d, waitForClientAck = %d\n",__FILE__,__FUNCTION__,gameSettingsUpdateCount,waitForClientAck);

View File

@ -73,14 +73,15 @@ public:
void removeSlot(int playerIndex);
ConnectionSlot* getSlot(int playerIndex);
int getConnectedSlotCount();
int getOpenSlotCount();
bool launchGame(const GameSettings* gameSettings);
virtual void setGameSettings(GameSettings *serverGameSettings, bool waitForClientAck = false);
void broadcastGameSetup(const GameSettings* gameSettings);
void updateListen();
private:
void broadcastMessage(const NetworkMessage* networkMessage, int excludeSlot= -1);
void updateListen();
void broadcastMessageToConnectedClients(const NetworkMessage* networkMessage, int excludeSlot = -1);
bool shouldDiscardNetworkMessage(NetworkMessageType networkMessageType,ConnectionSlot* connectionSlot);
};

View File

@ -181,6 +181,7 @@ public:
protected:
int boundPort;
BroadCastSocketThread *broadCastThread;
void startBroadCastThread();
bool isBroadCastThreadRunning();

View File

@ -1504,6 +1504,7 @@ bool ServerSocket::isBroadCastThreadRunning() {
void ServerSocket::bind(int port)
{
boundPort = port;
//sockaddr structure
sockaddr_in addr;
addr.sin_family= AF_INET;
@ -1527,14 +1528,31 @@ void ServerSocket::bind(int port)
}
}
void ServerSocket::listen(int connectionQueueSize)
{
int err= ::listen(sock, connectionQueueSize);
if(err < 0)
{
char szBuf[1024]="";
sprintf(szBuf, "In [%s::%s] Error listening socket sock = %d, err = %d, error = %s\n",__FILE__,__FUNCTION__,sock,err,getLastSocketErrorFormattedText().c_str());
throwException(szBuf);
void ServerSocket::listen(int connectionQueueSize) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d connectionQueueSize = %d\n",__FILE__,__FUNCTION__,__LINE__,connectionQueueSize);
if(connectionQueueSize > 0) {
if(isSocketValid() == false) {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(isSocketValid() == false) {
throwException("Error creating socket");
}
setBlock(false);
bind(boundPort);
}
int err= ::listen(sock, connectionQueueSize);
if(err < 0) {
char szBuf[1024]="";
sprintf(szBuf, "In [%s::%s] Error listening socket sock = %d, err = %d, error = %s\n",__FILE__,__FUNCTION__,sock,err,getLastSocketErrorFormattedText().c_str());
throwException(szBuf);
}
}
else {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
disconnectSocket();
}
if(connectionQueueSize > 0) {
@ -1545,7 +1563,6 @@ void ServerSocket::listen(int connectionQueueSize)
else {
stopBroadCastThread();
}
}
Socket *ServerSocket::accept()
@ -1555,6 +1572,7 @@ Socket *ServerSocket::accept()
{
char szBuf[1024]="";
sprintf(szBuf, "In [%s::%s] Error accepting socket connection sock = %d, err = %d, error = %s\n",__FILE__,__FUNCTION__,sock,newSock,getLastSocketErrorFormattedText().c_str());
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] %s\n",__FILE__,__FUNCTION__,__LINE__,szBuf);
if(getLastSocketError() == PLATFORM_SOCKET_TRY_AGAIN)
{
@ -1563,6 +1581,9 @@ Socket *ServerSocket::accept()
throwException(szBuf);
}
else {
SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] got connection, newSock = %d\n",__FILE__,__FUNCTION__,__LINE__,newSock);
}
return new Socket(newSock);
}