You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/GridMate/Tests/CarrierStreamSocketDriverTe...

1011 lines
44 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "Tests.h"
#include <AzCore/std/parallel/thread.h>
#include <GridMate/Carrier/DefaultSimulator.h>
#include <GridMate/Carrier/SocketDriver.h>
#include <GridMate/Carrier/StreamSocketDriver.h>
#include <GridMate/Carrier/DefaultHandshake.h>
using namespace GridMate;
static const AZ::u32 kMaxPacketSize = 1500;
static const AZ::u32 kInboundBufferSize = 1024 * 64;
static const AZ::u32 kOutboundBufferSize = 1024 * 64;
class SocketDriverSupplier
{
public:
virtual SocketDriver* CreateDriverForJoin(CarrierDesc& desc, AZ::u32 recvBuffSize = kMaxPacketSize, AZ::u32 sendBuffSize = kMaxPacketSize, AZ::u32 maxPacketSize = kMaxPacketSize)
{
desc.m_threadInstantResponse = false; //Instant response not supported by StreamSocketDriver
if (desc.m_driverReceiveBufferSize == 0)
{
desc.m_driverReceiveBufferSize = recvBuffSize;
}
if (desc.m_driverSendBufferSize == 0)
{
desc.m_driverSendBufferSize = sendBuffSize;
}
return aznew StreamSocketDriver(1, maxPacketSize, kInboundBufferSize, kOutboundBufferSize);
}
virtual SocketDriver* CreateDriverForHost(CarrierDesc& desc, AZ::u32 recvBuffSize = kMaxPacketSize, AZ::u32 sendBuffSize = kMaxPacketSize, AZ::u32 maxPacketSize = kMaxPacketSize)
{
desc.m_threadInstantResponse = false; //Instant response not supported by StreamSocketDriver
if (desc.m_driverReceiveBufferSize == 0)
{
desc.m_driverReceiveBufferSize = recvBuffSize;
}
if (desc.m_driverSendBufferSize == 0)
{
desc.m_driverSendBufferSize = sendBuffSize;
}
return aznew StreamSocketDriver(8, maxPacketSize, kInboundBufferSize, kOutboundBufferSize);
}
};
class CarrierStreamCallbacksHandler
: public CarrierEventBus::Handler
, public StreamSocketDriverEventsBus::Handler
{
public:
CarrierStreamCallbacksHandler()
: m_carrier(nullptr)
, m_connectionID(InvalidConnectionID)
, m_disconnectID(InvalidConnectionID)
, m_incommingConnectionID(InvalidConnectionID)
, m_errorCode(-1)
, m_active(false)
{
}
~CarrierStreamCallbacksHandler()
{
if (m_active)
{
CarrierEventBus::Handler::BusDisconnect();
StreamSocketDriverEventsBus::Handler::BusDisconnect();
}
}
void Activate(Carrier* carrier, Driver* driver)
{
m_active = true;
m_carrier = carrier;
m_driver = driver;
CarrierEventBus::Handler::BusConnect(carrier->GetGridMate());
StreamSocketDriverEventsBus::Handler::BusConnect(carrier->GetGridMate());
}
//
// StreamSocketDriverEventsBus
//
void OnConnectionEstablished(const SocketDriverAddress& address) override
{
if (m_driver == address.GetDriver())
{
AZ_TracePrintf("GridMate", "OnConnectionEstablished to %s\n", address.ToString().c_str());
}
}
void OnConnectionDisconnected(const SocketDriverAddress& address) override
{
if (m_driver == address.GetDriver())
{
AZ_TracePrintf("GridMate", "OnConnectionEstablished to %s\n", address.ToString().c_str());
}
}
//
// CarrierEventBus
//
void OnIncomingConnection(Carrier* carrier, ConnectionID id) override
{
if (carrier != m_carrier)
{
return; // not for us
}
m_incommingConnectionID = id;
}
void OnFailedToConnect(Carrier* carrier, ConnectionID id, CarrierDisconnectReason reason) override
{
(void)id;
(void)reason;
if (carrier != m_carrier)
{
return; // not for us
}
AZ_TEST_ASSERT(false);
}
void OnConnectionEstablished(Carrier* carrier, ConnectionID id) override
{
if (carrier != m_carrier)
{
return; // not for us
}
m_connectionID = id;
}
void OnDisconnect(Carrier* carrier, ConnectionID id, CarrierDisconnectReason reason) override
{
(void)reason;
if (carrier != m_carrier)
{
return; // not for us
}
m_disconnectID = id;
}
void OnDriverError(Carrier* carrier, ConnectionID id, const DriverError& error) override
{
(void)id;
if (carrier != m_carrier)
{
return; // not for us
}
m_errorCode = static_cast<int>(error.m_errorCode);
}
void OnSecurityError(Carrier* carrier, ConnectionID id, const SecurityError& error) override
{
(void)carrier;
(void)id;
(void)error;
//Ignore security warnings in unit tests
}
Driver* m_driver;
Carrier* m_carrier;
ConnectionID m_connectionID;
ConnectionID m_disconnectID;
ConnectionID m_incommingConnectionID;
int m_errorCode;
bool m_active;
};
namespace UnitTest
{
class Integ_CarrierStreamBasicTest
: public GridMateMPTestFixture
, protected SocketDriverSupplier
{
public:
void run()
{
#if AZ_TRAIT_GRIDMATE_TEST_SOCKET_IPV6_SUPPORT_ENABLED
bool useIpv6 = true;
#else
bool useIpv6 = false;
#endif
CarrierStreamCallbacksHandler clientCB, serverCB;
TestCarrierDesc serverCarrierDesc, clientCarrierDesc;
string str("Hello this is a carrier test!");
const char* targetAddress = "127.0.0.1";
if (useIpv6)
{
clientCarrierDesc.m_familyType = Driver::BSD_AF_INET6;
serverCarrierDesc.m_familyType = Driver::BSD_AF_INET6;
targetAddress = "::1";
}
clientCarrierDesc.m_enableDisconnectDetection = false;
serverCarrierDesc.m_enableDisconnectDetection = false;
clientCarrierDesc.m_driver = CreateDriverForJoin(clientCarrierDesc);
serverCarrierDesc.m_driver = CreateDriverForHost(serverCarrierDesc);
clientCarrierDesc.m_port = 4427;
serverCarrierDesc.m_port = 4433;
Carrier* clientCarrier = DefaultCarrier::Create(clientCarrierDesc, m_gridMate);
clientCB.Activate(clientCarrier, clientCarrierDesc.m_driver);
Carrier* serverCarrier = DefaultCarrier::Create(serverCarrierDesc, m_gridMate);
serverCB.Activate(serverCarrier, serverCarrierDesc.m_driver);
// stream socket driver has explicit connection calls
auto serverName = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->IPPortToAddress(targetAddress, serverCarrierDesc.m_port);
auto serverAddr = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->CreateDriverAddress(serverName);
static_cast<StreamSocketDriver*>(serverCarrierDesc.m_driver)->StartListen(100);
static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->ConnectTo(AZStd::static_pointer_cast<SocketDriverAddress>(serverAddr));
//////////////////////////////////////////////////////////////////////////
// Test carriers [0 is server, 1 is client]
bool isClientDone = false;
bool isServerDone = false;
bool isDisconnect = false;
char clientBuffer[kMaxPacketSize];
char serverBuffer[kMaxPacketSize];
Carrier::ReceiveResult receiveResult;
ConnectionID connId = InvalidConnectionID;
int maxNumUpdates = 2000;
int numUpdates = 0;
while (numUpdates <= maxNumUpdates)
{
// Client
if (!isClientDone)
{
if (connId == InvalidConnectionID)
{
connId = clientCarrier->Connect(targetAddress, serverCarrierDesc.m_port);
AZ_TEST_ASSERT(connId != InvalidConnectionID);
}
else
{
if (connId != AllConnections && clientCB.m_connectionID == connId)
{
clientCarrier->Send(str.c_str(), static_cast<unsigned>(str.length() + 1), clientCB.m_connectionID);
connId = AllConnections;
}
if (clientCB.m_connectionID != InvalidConnectionID)
{
receiveResult = clientCarrier->Receive(clientBuffer, AZ_ARRAY_SIZE(clientBuffer), clientCB.m_connectionID);
if (receiveResult.m_state == Carrier::ReceiveResult::RECEIVED)
{
AZ_TEST_ASSERT(memcmp(str.c_str(), clientBuffer, str.length()) == 0)
isClientDone = true;
}
}
}
}
// Server
if (!isServerDone)
{
if (serverCB.m_connectionID != InvalidConnectionID)
{
AZ_TEST_ASSERT(serverCB.m_incommingConnectionID == serverCB.m_connectionID);
receiveResult = serverCarrier->Receive(serverBuffer, AZ_ARRAY_SIZE(serverBuffer), serverCB.m_connectionID);
if (receiveResult.m_state == Carrier::ReceiveResult::RECEIVED)
{
serverCarrier->Send(str.c_str(), static_cast<unsigned>(str.length() + 1), connId);
AZ_TEST_ASSERT(memcmp(str.c_str(), serverBuffer, str.length()) == 0);
isServerDone = true;
}
}
}
serverCarrier->Update();
clientCarrier->Update();
if ((clientCB.m_disconnectID != InvalidConnectionID && serverCB.m_disconnectID != InvalidConnectionID) ||
clientCB.m_errorCode != -1 || serverCB.m_errorCode != -1)
{
break;
}
if (!isDisconnect && isClientDone && isServerDone && numUpdates > 50 /* we need 1 sec to update statistics */)
{
// check statistics
Carrier::Statistics clientStats, clientStatsLifeTime, clientStatsLastSecond;
Carrier::Statistics serverStats, serverStatsLifeTime, serverStatsLastSecond;
Carrier::ConnectionStates clientState = clientCarrier->QueryStatistics(clientCB.m_connectionID, &clientStatsLastSecond, &clientStatsLifeTime);
Carrier::ConnectionStates serverState = serverCarrier->QueryStatistics(serverCB.m_connectionID, &serverStatsLastSecond, &serverStatsLifeTime);
clientStats = clientStatsLifeTime;
clientStats.m_rtt = (clientStats.m_rtt + clientStatsLastSecond.m_rtt) * .5f;
clientStats.m_packetSend += clientStatsLastSecond.m_packetSend;
clientStats.m_dataSend += clientStatsLastSecond.m_dataSend;
serverStats = serverStatsLifeTime;
serverStats.m_rtt = (serverStats.m_rtt + serverStatsLastSecond.m_rtt) * .5f;
serverStats.m_packetSend += serverStatsLastSecond.m_packetSend;
serverStats.m_dataSend += serverStatsLastSecond.m_dataSend;
AZ_TEST_ASSERT(clientState == Carrier::CST_CONNECTED);
AZ_TEST_ASSERT(serverState == Carrier::CST_CONNECTED);
AZ_TEST_ASSERT(clientStats.m_rtt > 0);
AZ_TEST_ASSERT(serverStats.m_rtt > 0);
AZ_TEST_ASSERT(clientStats.m_packetSend > 0);
AZ_TEST_ASSERT(serverStats.m_packetSend > 0);
AZ_TEST_ASSERT(clientStats.m_dataSend > str.length() + 1);
AZ_TEST_ASSERT(serverStats.m_dataSend > str.length() + 1);
// Disconnect the server and test that the disconnect message will reach the client too.
serverCarrier->Disconnect(serverCB.m_connectionID);
isDisconnect = true;
}
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
numUpdates++;
}
DefaultCarrier::Destroy(clientCarrier);
DefaultCarrier::Destroy(serverCarrier);
//////////////////////////////////////////////////////////////////////////
AZ_TEST_ASSERT(isServerDone && isClientDone);
}
};
class Integ_CarrierStreamAsyncHandshakeTest
: public GridMateMPTestFixture
, protected SocketDriverSupplier
{
public:
static const unsigned int kHandshakeTimeoutMsec = 5000;
static const unsigned int kVersion = 1;
struct AsyncHandshake : public DefaultHandshake
{
AsyncHandshake()
: DefaultHandshake(kHandshakeTimeoutMsec, kVersion)
, m_isDone(false)
, m_numPendingRequests(0)
{
}
HandshakeErrorCode OnReceiveRequest(ConnectionID id, ReadBuffer& rb, WriteBuffer& wb) override
{
if (!m_isDone)
{
++m_numPendingRequests;
return HandshakeErrorCode::PENDING;
}
return DefaultHandshake::OnReceiveRequest(id, rb, wb);
}
void Done()
{
m_isDone = true;
}
bool m_isDone;
unsigned int m_numPendingRequests;
};
void run()
{
CarrierStreamCallbacksHandler clientCB, serverCB;
TestCarrierDesc serverCarrierDesc, clientCarrierDesc;
string str("Hello this is a carrier test!");
clientCarrierDesc.m_driver = CreateDriverForJoin(clientCarrierDesc);
serverCarrierDesc.m_driver = CreateDriverForHost(serverCarrierDesc);
clientCarrierDesc.m_port = 4427;
serverCarrierDesc.m_port = 4429;
AsyncHandshake serverHandshake;
serverCarrierDesc.m_handshake = &serverHandshake;
Carrier* clientCarrier = DefaultCarrier::Create(clientCarrierDesc, m_gridMate);
clientCB.Activate(clientCarrier, clientCarrierDesc.m_driver);
Carrier* serverCarrier = DefaultCarrier::Create(serverCarrierDesc, m_gridMate);
serverCB.Activate(serverCarrier, serverCarrierDesc.m_driver);
// stream socket driver has explicit connection calls
const char* targetAddress = "127.0.0.1";
static_cast<StreamSocketDriver*>(serverCarrierDesc.m_driver)->StartListen(100);
auto serverName = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->IPPortToAddress(targetAddress, serverCarrierDesc.m_port);
auto serverAddr = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->CreateDriverAddress(serverName);
static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->ConnectTo(AZStd::static_pointer_cast<SocketDriverAddress>(serverAddr));
char buffer[kMaxPacketSize];
ConnectionID connId = InvalidConnectionID;
int maxNumUpdates = 2000;
int numUpdates = 0;
bool clientReceived = false;
bool serverReceived = false;
while (numUpdates++ < maxNumUpdates)
{
if (numUpdates == 1)
{
connId = clientCarrier->Connect(targetAddress, serverCarrierDesc.m_port);
AZ_TEST_ASSERT(connId != InvalidConnectionID);
}
else if (numUpdates == 200)
{
serverHandshake.Done();
}
else if (numUpdates == 400) // should be connected by now
{
AZ_TEST_ASSERT(serverCB.m_connectionID != InvalidConnectionID);
AZ_TEST_ASSERT(clientCB.m_connectionID == connId);
AZ_TEST_ASSERT(serverHandshake.m_numPendingRequests > 2); // checking we received multiple requests before accepted it
serverHandshake.m_numPendingRequests = 0;
serverCarrier->Send(str.c_str(), static_cast<unsigned int>(str.size()), serverCB.m_connectionID);
clientCarrier->Send(str.c_str(), static_cast<unsigned int>(str.size()), clientCB.m_connectionID);
}
else if (numUpdates > 400)
{
Carrier::ReceiveResult result = clientCarrier->Receive(buffer, sizeof(buffer), clientCB.m_connectionID);
if (result.m_state == Carrier::ReceiveResult::RECEIVED && result.m_numBytes == str.size())
{
clientReceived = strncmp(str.c_str(), buffer, result.m_numBytes) == 0;
}
result = serverCarrier->Receive(buffer, sizeof(buffer), serverCB.m_connectionID);
if (result.m_state == Carrier::ReceiveResult::RECEIVED && result.m_numBytes == str.size())
{
serverReceived = strncmp(str.c_str(), buffer, result.m_numBytes) == 0;
}
if (clientReceived && serverReceived) // end test
{
break;
}
}
serverCarrier->Update();
clientCarrier->Update();
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
}
AZ_TEST_ASSERT(serverHandshake.m_numPendingRequests == 0); // no new requests after connection accomplished
AZ_TEST_ASSERT(clientReceived);
AZ_TEST_ASSERT(serverReceived);
DefaultCarrier::Destroy(clientCarrier);
DefaultCarrier::Destroy(serverCarrier);
}
};
class Integ_CarrierStreamStressTest
: public GridMateMPTestFixture
, protected SocketDriverSupplier
, public ::testing::Test
{
public:
};
TEST_F(Integ_CarrierStreamStressTest, Stress_Test)
{
CarrierStreamCallbacksHandler clientCB, serverCB;
UnitTest::TestCarrierDesc serverCarrierDesc, clientCarrierDesc;
string str("Hello this is a carrier stress test!");
clientCarrierDesc.m_enableDisconnectDetection = /*false*/ true;
serverCarrierDesc.m_enableDisconnectDetection = /*false*/ true;
clientCarrierDesc.m_threadUpdateTimeMS = 5;
serverCarrierDesc.m_threadUpdateTimeMS = 5;
clientCarrierDesc.m_port = 4427;
serverCarrierDesc.m_port = 4430;
clientCarrierDesc.m_driver = CreateDriverForJoin(clientCarrierDesc);
serverCarrierDesc.m_driver = CreateDriverForHost(serverCarrierDesc);
Carrier* clientCarrier = DefaultCarrier::Create(clientCarrierDesc, m_gridMate);
clientCB.Activate(clientCarrier, clientCarrierDesc.m_driver);
Carrier* serverCarrier = DefaultCarrier::Create(serverCarrierDesc, m_gridMate);
serverCB.Activate(serverCarrier, serverCarrierDesc.m_driver);
// stream socket driver has explicit connection calls
const char* targetAddress = "127.0.0.1";
static_cast<StreamSocketDriver*>(serverCarrierDesc.m_driver)->StartListen(100);
auto serverName = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->IPPortToAddress(targetAddress, serverCarrierDesc.m_port);
auto serverAddr = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->CreateDriverAddress(serverName);
static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->ConnectTo(AZStd::static_pointer_cast<SocketDriverAddress>(serverAddr));
//////////////////////////////////////////////////////////////////////////
// Test carriers [0 is server, 1 is client]
char serverBuffer[kMaxPacketSize];
ConnectionID connId = InvalidConnectionID;
int numUpdates = 0;
int numSend = 0;
int numRecv = 0;
int numUpdatesLastPrint = 0;
while (numRecv < 70000)
{
// Client
if (connId == InvalidConnectionID)
{
connId = clientCarrier->Connect(targetAddress, serverCarrierDesc.m_port);
AZ_TEST_ASSERT(connId != InvalidConnectionID);
}
else
{
if (connId != AllConnections && clientCB.m_connectionID == connId)
{
clientCarrier->Send(str.c_str(), static_cast<unsigned>(str.length() + 1), clientCB.m_connectionID);
numSend++;
}
}
// Server
if (serverCB.m_connectionID != InvalidConnectionID)
{
AZ_TEST_ASSERT(serverCB.m_incommingConnectionID == serverCB.m_connectionID);
Carrier::ReceiveResult result;
while ((result = serverCarrier->Receive(serverBuffer, AZ_ARRAY_SIZE(serverBuffer), serverCB.m_connectionID)).m_state == Carrier::ReceiveResult::RECEIVED)
{
AZ_TEST_ASSERT(memcmp(str.c_str(), serverBuffer, str.length()) == 0);
numRecv++;
}
}
serverCarrier->Update();
clientCarrier->Update();
if ((clientCB.m_disconnectID != InvalidConnectionID && serverCB.m_disconnectID != InvalidConnectionID) ||
clientCB.m_errorCode != -1 || serverCB.m_errorCode != -1)
{
break;
}
if (numUpdates - numUpdatesLastPrint == 5000)
{
numUpdatesLastPrint = numUpdates;
AZ_Printf("GridMate", "numSend:%d numRecv:%d\n", numSend, numRecv);
// check statistics
Carrier::Statistics clientStats, clientStatsLifeTime, clientStatsLastSecond;
Carrier::Statistics serverStats, serverStatsLifeTime, serverStatsLastSecond;
clientCarrier->QueryStatistics(clientCB.m_connectionID, &clientStatsLastSecond, &clientStatsLifeTime);
serverCarrier->QueryStatistics(serverCB.m_connectionID, &serverStatsLastSecond, &serverStatsLifeTime);
clientStats = clientStatsLifeTime;
clientStats.m_rtt = (clientStats.m_rtt + clientStatsLastSecond.m_rtt) * .5f;
clientStats.m_packetSend += clientStatsLastSecond.m_packetSend;
clientStats.m_dataSend += clientStatsLastSecond.m_dataSend;
serverStats = serverStatsLifeTime;
serverStats.m_rtt = (serverStats.m_rtt + serverStatsLastSecond.m_rtt) * .5f;
serverStats.m_packetSend += serverStatsLastSecond.m_packetSend;
serverStats.m_dataSend += serverStatsLastSecond.m_dataSend;
AZ_Printf("GridMate", "Server rtt %.2f ms numPkgSent %d dataSend %d\n", serverStats.m_rtt, serverStats.m_packetSend, serverStats.m_dataSend);
AZ_Printf("GridMate", "Client rtt %.2f ms numPkgSent %d dataSend %d\n", clientStats.m_rtt, clientStats.m_packetSend, clientStats.m_dataSend);
}
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(2));
numUpdates++;
}
DefaultCarrier::Destroy(clientCarrier);
DefaultCarrier::Destroy(serverCarrier);
//////////////////////////////////////////////////////////////////////////
}
class Integ_CarrierStreamTest
: public GridMateMPTestFixture
, protected SocketDriverSupplier
{
public:
void run()
{
//////////////////////////////////////////////////////////////////////////
// Setup simulators
DefaultSimulator clientSimulator;
clientSimulator.Enable();
clientSimulator.SetOutgoingLatency(150, 150); // in milliseconds
clientSimulator.SetOutgoingPacketLoss(5, 5); // drop 1 every X packets
clientSimulator.SetOutgoingReorder(true); // enable reorder
clientSimulator.SetIncomingLatency(200, 200);
clientSimulator.SetIncomingPacketLoss(7, 7);
clientSimulator.SetIncomingReorder(true);
clientSimulator.Enable();
//////////////////////////////////////////////////////////////////////////
CarrierStreamCallbacksHandler clientCB, serverCB;
TestCarrierDesc serverCarrierDesc, clientCarrierDesc;
clientCarrierDesc.m_port = 4427;
clientCarrierDesc.m_driver = CreateDriverForJoin(clientCarrierDesc, 16 * 1024, 16 * 1024, kMaxPacketSize);
//clientCarrierDesc.m_simulator = &clientSimulator;
serverCarrierDesc.m_port = 4431;
serverCarrierDesc.m_driver = CreateDriverForHost(serverCarrierDesc, 16 * 1024, 16 * 1024, kMaxPacketSize);
Carrier* clientCarrier = DefaultCarrier::Create(clientCarrierDesc, m_gridMate);
clientCB.Activate(clientCarrier, clientCarrierDesc.m_driver);
Carrier* serverCarrier = DefaultCarrier::Create(serverCarrierDesc, m_gridMate);
serverCB.Activate(serverCarrier, serverCarrierDesc.m_driver);
//////////////////////////////////////////////////////////////////////////
// stream socket driver has explicit connection calls
const char* targetAddress = "127.0.0.1";
static_cast<StreamSocketDriver*>(serverCarrierDesc.m_driver)->StartListen(100);
auto serverName = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->IPPortToAddress(targetAddress, serverCarrierDesc.m_port);
auto serverAddr = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->CreateDriverAddress(serverName);
static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->ConnectTo(AZStd::static_pointer_cast<SocketDriverAddress>(serverAddr));
//////////////////////////////////////////////////////////////////////////
unsigned int intArray[10240];
for (unsigned int i = 0; i < AZ_ARRAY_SIZE(intArray); ++i)
{
intArray[i] = i;
}
//////////////////////////////////////////////////////////////////////////
// Test carriers [0 is server, 1 is client]
Carrier::ReceiveResult receiveResult;
bool isClientDone = false;
bool isServerDone = false;
bool isDisconnect = false;
ConnectionID connId = InvalidConnectionID;
char clientBuffer[65 * 1024];
char serverBuffer[65 * 1024];
const int maxNumUpdates = 5000;
int numUpdates = 0;
bool isPrintStatus = false;
while (numUpdates <= maxNumUpdates)
{
// Client
if (!isClientDone)
{
if (connId == InvalidConnectionID)
{
connId = clientCarrier->Connect(targetAddress, serverCarrierDesc.m_port);
AZ_TEST_ASSERT(connId != InvalidConnectionID);
}
else
{
if (connId != AllConnections && clientCB.m_connectionID == connId)
{
clientCarrier->Send((char*)intArray, sizeof(intArray), clientCB.m_connectionID);
connId = AllConnections;
}
if (clientCB.m_connectionID != InvalidConnectionID)
{
/////////////////////////////////////////////////////////////////////
// Test Receive functions buffer overflow and buffer size
unsigned int queryBufferSize = clientCarrier->QueryNextReceiveMessageMaxSize(clientCB.m_connectionID);
if (queryBufferSize > 0) // if there is message waiting on to be received
{
// we can receive messages only of intArray size
AZ_TEST_ASSERT(queryBufferSize >= sizeof(intArray));
}
receiveResult = clientCarrier->Receive(clientBuffer, 100, clientCB.m_connectionID); // receive with smaller buffer
switch (receiveResult.m_state)
{
case Carrier::ReceiveResult::NO_MESSAGE_TO_RECEIVE:
{
// make sure the query return 0 if we have no message to receive
AZ_TEST_ASSERT(queryBufferSize == 0);
break;
}
case Carrier::ReceiveResult::UNSUFFICIENT_BUFFER_SIZE:
{
// we should have a message waiting if we fail to receive it
AZ_TEST_ASSERT(queryBufferSize > 0);
break;
}
case Carrier::ReceiveResult::RECEIVED:
{
// we have small buffer we should never be able to receive a message
AZ_TEST_ASSERT(false);
break;
}
default:
break;
}
/////////////////////////////////////////////////////////////////////
receiveResult = clientCarrier->Receive(clientBuffer, AZ_ARRAY_SIZE(clientBuffer), clientCB.m_connectionID);
if (receiveResult.m_state == Carrier::ReceiveResult::RECEIVED)
{
AZ_TEST_ASSERT(queryBufferSize >= receiveResult.m_numBytes); // make sure the query was correct
AZ_TEST_ASSERT(memcmp(intArray, clientBuffer, sizeof(intArray)) == 0);
isClientDone = true;
}
}
}
}
// Server
if (!isServerDone)
{
if (serverCB.m_connectionID != InvalidConnectionID)
{
receiveResult = serverCarrier->Receive(serverBuffer, AZ_ARRAY_SIZE(serverBuffer), serverCB.m_connectionID);
if (receiveResult.m_state == Carrier::ReceiveResult::RECEIVED)
{
AZ_TEST_ASSERT(memcmp(intArray, serverBuffer, sizeof(intArray)) == 0);
serverCarrier->Send((char*)intArray, sizeof(intArray), connId);
isServerDone = true;
}
}
}
serverCarrier->Update();
clientCarrier->Update();
if (!isPrintStatus && connId == AllConnections && clientCB.m_connectionID != InvalidConnectionID)
{
clientCarrier->DebugStatusReport(clientCB.m_connectionID);
serverCarrier->DebugStatusReport(serverCB.m_connectionID);
isPrintStatus = true;
}
if (!isDisconnect && isClientDone && isServerDone && numUpdates > 50)
{
// check statistics
Carrier::Statistics clientStats, clientStatsLifeTime, clientStatsLastSecond;
Carrier::Statistics serverStats, serverStatsLifeTime, serverStatsLastSecond;
Carrier::ConnectionStates clientState = clientCarrier->QueryStatistics(clientCB.m_connectionID, &clientStatsLastSecond, &clientStatsLifeTime);
Carrier::ConnectionStates serverState = serverCarrier->QueryStatistics(serverCB.m_connectionID, &serverStatsLastSecond, &serverStatsLifeTime);
clientStats = clientStatsLifeTime;
clientStats.m_rtt = (clientStats.m_rtt + clientStatsLastSecond.m_rtt) * .5f;
clientStats.m_packetSend += clientStatsLastSecond.m_packetSend;
clientStats.m_dataSend += clientStatsLastSecond.m_dataSend;
serverStats = serverStatsLifeTime;
serverStats.m_rtt = (serverStats.m_rtt + serverStatsLastSecond.m_rtt) * .5f;
serverStats.m_packetSend += serverStatsLastSecond.m_packetSend;
serverStats.m_dataSend += serverStatsLastSecond.m_dataSend;
AZ_TEST_ASSERT(clientState == Carrier::CST_CONNECTED && serverState == Carrier::CST_CONNECTED);
AZ_TEST_ASSERT(clientStats.m_rtt > 0);
AZ_TEST_ASSERT(serverStats.m_rtt > 0);
AZ_TEST_ASSERT(clientStats.m_packetSend > 0);
AZ_TEST_ASSERT(serverStats.m_packetSend > 0);
AZ_TEST_ASSERT(clientStats.m_dataSend > sizeof(intArray));
AZ_TEST_ASSERT(serverStats.m_dataSend > sizeof(intArray));
// Disconnect the server and test that the disconnect message will reach the client too.
serverCarrier->Disconnect(serverCB.m_connectionID);
isDisconnect = true;
}
if ((clientCB.m_disconnectID != InvalidConnectionID && serverCB.m_disconnectID != InvalidConnectionID) ||
clientCB.m_errorCode != -1 || serverCB.m_errorCode != -1)
{
break;
}
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
numUpdates++;
}
DefaultCarrier::Destroy(clientCarrier);
DefaultCarrier::Destroy(serverCarrier);
AZ_TEST_ASSERT(isServerDone && isClientDone);
//////////////////////////////////////////////////////////////////////////
}
};
class Integ_CarrierStreamDisconnectDetectionTest
: public GridMateMPTestFixture
, protected SocketDriverSupplier
{
public:
void run()
{
// Setup simulators
DefaultSimulator clientSimulator;
clientSimulator.SetOutgoingPacketLoss(2, 2); // drop 50% packets
TestCarrierDesc serverCarrierDesc;
serverCarrierDesc.m_port = 4432;
serverCarrierDesc.m_enableDisconnectDetection = true;
serverCarrierDesc.m_disconnectDetectionPacketLossThreshold = 0.4f; // disconnect once hit 40% loss
serverCarrierDesc.m_disconnectDetectionRttThreshold = 50; // disconnect once hit 50 msec rtt
serverCarrierDesc.m_driver = CreateDriverForHost(serverCarrierDesc);
TestCarrierDesc clientCarrierDesc = serverCarrierDesc;
clientCarrierDesc.m_port = 4427;
clientCarrierDesc.m_simulator = &clientSimulator;
clientCarrierDesc.m_driver = CreateDriverForJoin(clientCarrierDesc);
Carrier* clientCarrier = DefaultCarrier::Create(clientCarrierDesc, m_gridMate);
Carrier* serverCarrier = DefaultCarrier::Create(serverCarrierDesc, m_gridMate);
//////////////////////////////////////////////////////////////////////////
// stream socket driver has explicit connection calls
const char* targetAddress = "127.0.0.1";
static_cast<StreamSocketDriver*>(serverCarrierDesc.m_driver)->StartListen(100);
auto serverName = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->IPPortToAddress(targetAddress, serverCarrierDesc.m_port);
auto serverAddr = static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->CreateDriverAddress(serverName);
static_cast<StreamSocketDriver*>(clientCarrierDesc.m_driver)->ConnectTo(AZStd::static_pointer_cast<SocketDriverAddress>(serverAddr));
//////////////////////////////////////////////////////////////////////////
for (int testCaseNum = 0; testCaseNum < 2; ++testCaseNum)
{
if (testCaseNum == 0)
{
AZ_TracePrintf("GridMate", "Simulating bad packet loss...\n");
clientSimulator.SetIncomingPacketLoss(2, 2); // drop ~50% packets
}
else if (testCaseNum == 1)
{
AZ_TracePrintf("GridMate", "Simulating bad latency...\n");
// ~60 msec rtt
clientSimulator.SetIncomingLatency(30, 30);
clientSimulator.SetOutgoingLatency(30, 30);
clientSimulator.SetIncomingPacketLoss(0, 0);
}
clientCarrier->Connect(targetAddress, serverCarrierDesc.m_port); // connecting client -> server
int numUpdates = 0;
while (serverCarrier->GetNumConnections() == 0 && numUpdates++ <= 1000) // wait until connected
{
clientCarrier->Update();
serverCarrier->Update();
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
}
AZ_TEST_ASSERT(serverCarrier->GetNumConnections() == 1); // check if connected
clientSimulator.Enable(); // enabling bad traffic conditions
numUpdates = 0;
while ((serverCarrier->GetNumConnections() == 1 || clientCarrier->GetNumConnections() == 1) // wait until both disconnect
&& numUpdates++ <= 2000) // max 20 sec
{
if (!(numUpdates % 100) && serverCarrier->GetNumConnections() == 1)
{
TrafficControl::Statistics stats;
serverCarrier->QueryStatistics(serverCarrier->DebugGetConnectionId(0), nullptr, &stats);
AZ_TracePrintf("GridMate", " Server -> Client: rtt=%.0f msec, packetLoss=%.0f%%\n", stats.m_rtt, stats.m_packetLoss * 100.f);
}
clientCarrier->Update();
serverCarrier->Update();
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
}
AZ_TEST_ASSERT(serverCarrier->GetNumConnections() == 0); // disconnected
clientSimulator.Disable();
}
DefaultCarrier::Destroy(clientCarrier);
DefaultCarrier::Destroy(serverCarrier);
}
};
class Integ_CarrierStreamMultiChannelTest
: public GridMateMPTestFixture
, protected SocketDriverSupplier
{
public:
/*
* Sends reliable messages across different channels to each other
*/
void run()
{
// initialize transport
int basePort = 4427;
enum
{
c1,
c2,
nCarriers
};
int nMsgSent[nCarriers];
int nMsgReceived[nCarriers];
Carrier* carriers[nCarriers];
Driver* drivers[nCarriers];
CarrierStreamCallbacksHandler carrierHandlers[nCarriers];
//////////////////////////////////////////////////////////////////////////
const char* targetAddress = "127.0.0.1";
unsigned int serverCarrierDescPort = 0;
for (int i = 0; i < nCarriers; ++i)
{
// Create carriers
TestCarrierDesc desc;
desc.m_enableDisconnectDetection = true;
desc.m_port = basePort + i;
if (i == c1)
{
drivers[i] = CreateDriverForHost(desc);
serverCarrierDescPort = desc.m_port;
}
else
{
drivers[i] = CreateDriverForJoin(desc);
}
desc.m_driver = drivers[i];
carriers[i] = DefaultCarrier::Create(desc, m_gridMate);
carrierHandlers[i].Activate(carriers[i], desc.m_driver);
nMsgSent[i] = nMsgReceived[i] = 0;
}
// stream socket driver has explicit connection calls
for (int k = 0; k < nCarriers; ++k)
{
if (k == c1)
{
static_cast<StreamSocketDriver*>(drivers[k])->StartListen(100);
}
else
{
auto serverName = static_cast<StreamSocketDriver*>(drivers[k])->IPPortToAddress(targetAddress, serverCarrierDescPort);
auto serverAddr = static_cast<StreamSocketDriver*>(drivers[k])->CreateDriverAddress(serverName);
static_cast<StreamSocketDriver*>(drivers[k])->ConnectTo(AZStd::static_pointer_cast<SocketDriverAddress>(serverAddr));
}
}
//////////////////////////////////////////////////////////////////////////
carriers[c2]->Connect(targetAddress, basePort + c1);
int maxNumUpdates = 100;
int numUpdates = 0;
while (numUpdates <= maxNumUpdates)
{
//////////////////////////////////////////////////////////////////////////
Update();
for (int iCarrier = 0; iCarrier < nCarriers; ++iCarrier)
{
if (carrierHandlers[iCarrier].m_connectionID != InvalidConnectionID)
{
for (unsigned int iConn = 0; iConn < carriers[iCarrier]->GetNumConnections(); ++iConn)
{
ConnectionID connId = carriers[iCarrier]->DebugGetConnectionId(iConn);
for (unsigned char iChannel = 0; iChannel < 3; ++iChannel)
{
char buf[kMaxPacketSize];
// receive
Carrier::ReceiveResult receiveResult = carriers[iCarrier]->Receive(buf, AZ_ARRAY_SIZE(buf), connId, iChannel);
if (receiveResult.m_state == Carrier::ReceiveResult::RECEIVED)
{
nMsgReceived[iCarrier]++;
}
// send something
if (numUpdates < 50)
{
azsnprintf(buf, 1024, "Msg %d", nMsgSent[iCarrier]++);
carriers[iCarrier]->Send(buf, (unsigned int)strlen(buf) + 1, connId, Carrier::SEND_RELIABLE, Carrier::PRIORITY_NORMAL, iChannel);
}
}
}
}
carriers[iCarrier]->Update();
}
//////////////////////////////////////////////////////////////////////////
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
numUpdates++;
}
int nSent = 0;
int nReceived = 0;
for (int i = 0; i < nCarriers; ++i)
{
nSent += nMsgSent[i];
nReceived += nMsgReceived[i];
DefaultCarrier::Destroy(carriers[i]);
}
AZ_TEST_ASSERT(nSent > 0);
AZ_TEST_ASSERT(nSent == nReceived);
}
};
}
GM_TEST_SUITE(CarrierStreamSuite)
GM_TEST(Integ_CarrierStreamBasicTest)
GM_TEST(Integ_CarrierStreamTest)
GM_TEST(Integ_CarrierStreamAsyncHandshakeTest)
GM_TEST(Integ_CarrierStreamMultiChannelTest)
GM_TEST_SUITE_END()