/* * Copyright (c) 2014, Oculus VR, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * RakNet License.txt file in the licenses directory of this source tree. An additional grant * of patent rights can be found in the RakNet Patents.txt file in the same directory. * */ #include "DroppedConnectionConvertTest.h" /* Description: Tests silently dropping multiple instances of RakNet. This is used to test that lost connections are detected properly. Randomly tests the timout detections to see if the connections are dropped. Success conditions: Clients connect and reconnect normally and do not have an extra connection. Random timout detection passes. Failure conditions: Client has more than one connection. Client unable to reconnect. Connect function fails and there is no pending operation and there is no current connection with server. Random timout detection fails. */ static const int NUMBER_OF_CLIENTS=9; int DroppedConnectionConvertTest::RunTest(DataStructures::List params,bool isVerbose,bool noPauses) { RakPeerInterface *server; RakPeerInterface *clients[NUMBER_OF_CLIENTS]; unsigned index, connectionCount; SystemAddress serverID; Packet *p; unsigned short numberOfSystems; unsigned short numberOfSystems2; int sender; // Buffer for input (an ugly hack to keep *nix happy) // char buff[256]; // Used to refer to systems. We already know the IP unsigned short serverPort = 20000; serverID.binaryAddress=inet_addr("127.0.0.1"); serverID.port=serverPort; server=RakPeerInterface::GetInstance(); destroyList.Clear(false,_FILE_AND_LINE_); destroyList.Push(server,_FILE_AND_LINE_); // server->InitializeSecurity(0,0,0,0); SocketDescriptor socketDescriptor(serverPort,0); server->Startup(NUMBER_OF_CLIENTS, &socketDescriptor, 1); server->SetMaximumIncomingConnections(NUMBER_OF_CLIENTS); server->SetTimeoutTime(2000,UNASSIGNED_SYSTEM_ADDRESS); for (index=0; index < NUMBER_OF_CLIENTS; index++) { clients[index]=RakPeerInterface::GetInstance(); destroyList.Push(clients[index],_FILE_AND_LINE_); SocketDescriptor socketDescriptor2(serverPort+1+index,0); clients[index]->Startup(1, &socketDescriptor2, 1); if (clients[index]->Connect("127.0.0.1", serverPort, 0, 0)!=CONNECTION_ATTEMPT_STARTED) { DebugTools::ShowError("Connect function failed.",!noPauses && isVerbose,__LINE__,__FILE__); return 2; } clients[index]->SetTimeoutTime(5000,UNASSIGNED_SYSTEM_ADDRESS); RakSleep(1000); if (isVerbose) printf("%i. ", index); } TimeMS entryTime=GetTimeMS();//Loop entry time int seed = 12345; if (isVerbose) printf("Using seed %i\n", seed); seedMT(seed);//specify seed to keep execution path the same. int randomTest; bool dropTest=false; RakTimer timeoutWaitTimer(1000); while (GetTimeMS()-entryTime<30000)//run for 30 seconds. { // User input randomTest=randomMT() %4; if(dropTest) { server->GetConnectionList(0, &numberOfSystems); numberOfSystems2=numberOfSystems; connectionCount=0; for (index=0; index < NUMBER_OF_CLIENTS; index++) { clients[index]->GetConnectionList(0, &numberOfSystems); if (numberOfSystems>1) { if (isVerbose) { printf("Client %i has %i connections\n", index, numberOfSystems); DebugTools::ShowError("Client has more than one connection",!noPauses && isVerbose,__LINE__,__FILE__); return 1; } } if (numberOfSystems==1) { connectionCount++; } } if (connectionCount!=numberOfSystems2) { if (isVerbose) DebugTools::ShowError("Timeout on dropped clients not detected",!noPauses && isVerbose,__LINE__,__FILE__); return 3; } } dropTest=false; switch(randomTest) { case 0: { index = randomMT() % NUMBER_OF_CLIENTS; clients[index]->GetConnectionList(0, &numberOfSystems); clients[index]->CloseConnection(serverID, false,0); if (numberOfSystems==0) { if (isVerbose) printf("Client %i silently closing inactive connection.\n",index); } else { if (isVerbose) printf("Client %i silently closing active connection.\n",index); } } break; case 1: { index = randomMT() % NUMBER_OF_CLIENTS; clients[index]->GetConnectionList(0, &numberOfSystems); if(!CommonFunctions::ConnectionStateMatchesOptions (clients[index],serverID,true,true,true,true) )//Are we connected or is there a pending operation ? { if (clients[index]->Connect("127.0.0.1", serverPort, 0, 0)!=CONNECTION_ATTEMPT_STARTED) { DebugTools::ShowError("Connect function failed.",!noPauses && isVerbose,__LINE__,__FILE__); return 2; } } if (numberOfSystems==0) { if (isVerbose) printf("Client %i connecting to same existing connection.\n",index); } else { if (isVerbose) printf("Client %i connecting to closed connection.\n",index); } } break; case 2: { if (isVerbose) printf("Randomly connecting and disconnecting each client\n"); for (index=0; index < NUMBER_OF_CLIENTS; index++) { if (NUMBER_OF_CLIENTS==1 || (randomMT()%2)==0) { if (clients[index]->IsActive()) { int randomTest2=randomMT() %2; if (randomTest2) clients[index]->CloseConnection(serverID, false, 0); else clients[index]->CloseConnection(serverID, true, 0); } } else { if(!CommonFunctions::ConnectionStateMatchesOptions (clients[index],serverID,true,true,true,true) )//Are we connected or is there a pending operation ? { if (clients[index]->Connect("127.0.0.1", serverPort, 0, 0)!=CONNECTION_ATTEMPT_STARTED) { DebugTools::ShowError("Connect function failed.",!noPauses && isVerbose,__LINE__,__FILE__); return 2; } } } } } break; case 3: { if (isVerbose) printf("Testing if clients dropped after timeout.\n"); timeoutWaitTimer.Start(); //Wait half the timeout time, the other half after receive so we don't drop all connections only missing ones, Active ait so the threads run on linux while (!timeoutWaitTimer.IsExpired()) { RakSleep(50); } dropTest=true; } break; default: // Ignore anything else break; } server->GetConnectionList(0, &numberOfSystems); numberOfSystems2=numberOfSystems; if (isVerbose) printf("The server thinks %i clients are connected.\n", numberOfSystems); connectionCount=0; for (index=0; index < NUMBER_OF_CLIENTS; index++) { clients[index]->GetConnectionList(0, &numberOfSystems); if (numberOfSystems>1) { if (isVerbose) { printf("Client %i has %i connections\n", index, numberOfSystems); DebugTools::ShowError("Client has more than one connection",!noPauses && isVerbose,__LINE__,__FILE__); return 1; } } if (numberOfSystems==1) { connectionCount++; } } if (isVerbose) printf("%i clients are actually connected.\n", connectionCount); if (isVerbose) printf("server->NumberOfConnections==%i.\n", server->NumberOfConnections()); //} // Parse messages while (1) { p = server->Receive(); sender=NUMBER_OF_CLIENTS; if (p==0) { for (index=0; index < NUMBER_OF_CLIENTS; index++) { p = clients[index]->Receive(); if (p!=0) { sender=index; break; } } } if (p) { switch (p->data[0]) { case ID_CONNECTION_REQUEST_ACCEPTED: if (isVerbose) printf("%i: %ID_CONNECTION_REQUEST_ACCEPTED from %i.\n",sender, p->systemAddress.port); break; case ID_DISCONNECTION_NOTIFICATION: // Connection lost normally if (isVerbose) printf("%i: ID_DISCONNECTION_NOTIFICATION from %i.\n",sender, p->systemAddress.port); break; case ID_NEW_INCOMING_CONNECTION: // Somebody connected. We have their IP now if (isVerbose) printf("%i: ID_NEW_INCOMING_CONNECTION from %i.\n",sender, p->systemAddress.port); break; case ID_CONNECTION_LOST: // Couldn't deliver a reliable packet - i.e. the other system was abnormally // terminated if (isVerbose) printf("%i: ID_CONNECTION_LOST from %i.\n",sender, p->systemAddress.port); break; case ID_NO_FREE_INCOMING_CONNECTIONS: if (isVerbose) printf("%i: ID_NO_FREE_INCOMING_CONNECTIONS from %i.\n",sender, p->systemAddress.port); break; default: // Ignore anything else break; } } else break; if (sender==NUMBER_OF_CLIENTS) server->DeallocatePacket(p); else clients[sender]->DeallocatePacket(p); } if (dropTest) { //Trigger the timeout if no recieve timeoutWaitTimer.Start(); while (!timeoutWaitTimer.IsExpired()) { RakSleep(50); } } // 11/29/05 - No longer necessary since I added the keepalive /* // Have everyone send a reliable packet so dropped connections are noticed. ch=255; server->Send((char*)&ch, 1, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true); for (index=0; index < NUMBER_OF_CLIENTS; index++) clients[index]->Send((char*)&ch, 1, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true); */ // Sleep so this loop doesn't take up all the CPU time RakSleep(10); } return 0; } RakString DroppedConnectionConvertTest::GetTestName() { return "DroppedConnectionConvertTest"; } RakString DroppedConnectionConvertTest::ErrorCodeToString(int errorCode) { switch (errorCode) { case 0: return "No error"; break; case 1: return "Client has more than one connection"; break; case 2: return "Connect failed"; break; case 3: return "Timeout not detected"; break; default: return "Undefined Error"; } } void DroppedConnectionConvertTest::DestroyPeers() { int theSize=destroyList.Size(); for (int i=0; i < theSize; i++) RakPeerInterface::DestroyInstance(destroyList[i]); } DroppedConnectionConvertTest::DroppedConnectionConvertTest(void) { } DroppedConnectionConvertTest::~DroppedConnectionConvertTest(void) { }