Skip to content

Commit 6ed2ae6

Browse files
committed
Add more conection tests
recv_buf_full was intended to reproduce a bug. I never could get it to reproduce the buf, but I think it's a useful test nonetheless. I also added another network contidions block to the 'normal' mode, which did reliable reproduce a bug.
1 parent 0e4365d commit 6ed2ae6

1 file changed

Lines changed: 116 additions & 3 deletions

File tree

tests/test_connection.cpp

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ struct SFakePeer
7777
m_flUnreliableMsgDelay = 0.0f;
7878
m_hSteamNetConnection = k_HSteamNetConnection_Invalid;
7979
m_bIsConnected = false;
80-
m_cbSendBuffer = 384 * 1024;
80+
m_cbSendBuffer = 2048 * 1024;
8181
memset( &m_realtimeStatus, 0, sizeof(m_realtimeStatus) );
8282
m_flSendRate = 0.0f;
8383
m_flRecvRate = 0.0f;
@@ -536,6 +536,11 @@ static void Test_Connection( ETestConnectionMode eMode, const SteamNetworkingIPA
536536
{
537537
Test( 128000, 10, 50, 2, 50 ); // Low bandwidth, high packet loss
538538
Test( 1000000, 5, 10, 1, 10 ); // Medium bandwidth, still pretty bad packet loss
539+
540+
// Zero loss so acks flow freely (stop_waiting tracks within one RTT of
541+
// current packet number). Heavy reorder builds reliable stream fragments
542+
// without the retransmission backlog that keeps stop_waiting far behind.
543+
TestNetworkConditions( 2000000, 0, 50, 30, 40, false, eMode );
539544
}
540545
else
541546
{
@@ -1086,6 +1091,113 @@ void Test_lane_quick_priority_and_background()
10861091

10871092
}
10881093

1094+
// Coverage/stress test for a stalled receiver: the application stops draining
1095+
// its receive queue while the peer keeps blasting reliable data, under loss and
1096+
// reorder. This drives a code path almost nothing else in the suite reaches.
1097+
//
1098+
// Simulates an app stall (e.g. loading assets from disk) while the server is
1099+
// blasting reliable state data. The client never calls ReceiveMessagesOnConnection,
1100+
// so its application receive queue (RecvBufferMessages) fills up. Once full,
1101+
// ReceivedMessage() returns false, which propagates to bInhibitMarkReceived in the
1102+
// SNP decoder: the packet is processed (TrackProcessSequencedPacket advances
1103+
// m_nMaxRecvPktNum) but SNP_RecordReceivedPktNum is skipped, so the packet is not
1104+
// entered into the gap map. Meanwhile the client still sends acks back and the
1105+
// server keeps advancing its stop_waiting. Layering loss and reorder on the
1106+
// server->client path keeps the gap map churning throughout.
1107+
void Test_recv_buf_full()
1108+
{
1109+
TEST_Printf( "***************************************************\n" );
1110+
TEST_Printf( "Test: recv buffer full / stalled receiver under loss+reorder\n" );
1111+
TEST_Printf( "***************************************************\n" );
1112+
1113+
// Network loopback so packets go through the full SNP encode/decode path.
1114+
HSteamNetConnection hServer, hClient;
1115+
assert( SteamNetworkingSockets()->CreateSocketPair( &hServer, &hClient, true, nullptr, nullptr ) );
1116+
SteamNetworkingSockets()->SetConnectionName( hServer, "Server" );
1117+
SteamNetworkingSockets()->SetConnectionName( hClient, "Client" );
1118+
1119+
// Small application receive queue on the client. After this many messages
1120+
// are queued without the app draining them, ReceivedMessage() returns false,
1121+
// which propagates to bInhibitMarkReceived in the SNP decoder.
1122+
SteamNetworkingUtils()->SetConnectionConfigValueInt32( hClient,
1123+
k_ESteamNetworkingConfig_RecvBufferMessages, 32 );
1124+
1125+
// Moderate send rate -- enough to fill the queue quickly but not so fast
1126+
// that the service thread starves the client's keepalive sending.
1127+
const int k_nSendRate = 256 * 1024;
1128+
SteamNetworkingUtils()->SetConnectionConfigValueInt32( hServer,
1129+
k_ESteamNetworkingConfig_SendRateMin, k_nSendRate );
1130+
SteamNetworkingUtils()->SetConnectionConfigValueInt32( hServer,
1131+
k_ESteamNetworkingConfig_SendRateMax, k_nSendRate );
1132+
1133+
// Simulate real-world network conditions: packet loss and reordering on
1134+
// the server->client path. This creates non-trivial gap map state on the
1135+
// client before and during the buffer-full condition, which may interact
1136+
// with the stop_waiting/sentinel relationship differently.
1137+
SteamNetworkingUtils()->SetConnectionConfigValueInt32( hClient,
1138+
k_ESteamNetworkingConfig_FakePacketLoss_Recv, 5 );
1139+
SteamNetworkingUtils()->SetConnectionConfigValueInt32( hClient,
1140+
k_ESteamNetworkingConfig_FakePacketReorder_Recv, 10 );
1141+
SteamNetworkingUtils()->SetConnectionConfigValueInt32( hClient,
1142+
k_ESteamNetworkingConfig_FakePacketReorder_Time, 20 );
1143+
1144+
// Let the connection settle so all handshake traffic is complete and acks
1145+
// have drained before the burst begins.
1146+
for ( int i = 0; i < 20; ++i )
1147+
{
1148+
TEST_PumpCallbacks();
1149+
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
1150+
}
1151+
1152+
// Queue up a large initial burst of reliable data -- far more than the
1153+
// client's RecvBufferMessages limit. The server drains this at k_nSendRate.
1154+
static const char kData[ 1000 ] = {};
1155+
int nSent = 0;
1156+
for ( int i = 0; i < 2000; ++i )
1157+
{
1158+
EResult r = SteamNetworkingSockets()->SendMessageToConnection(
1159+
hServer, kData, sizeof(kData), k_nSteamNetworkingSend_Reliable, nullptr );
1160+
if ( r == k_EResultOK )
1161+
++nSent;
1162+
}
1163+
TEST_Printf( "Queued %d messages (~%d KB) for server to send\n", nSent, nSent * (int)sizeof(kData) / 1024 );
1164+
1165+
// Continue sending from both sides for several seconds while the client
1166+
// never drains its receive queue. The client sends small messages back so
1167+
// its outgoing packets carry acks, giving the server frequent opportunities
1168+
// to advance its stop_waiting. The server keeps queuing small messages to
1169+
// stay active. The client never calls ReceiveMessagesOnConnection.
1170+
static const char kTiny[1] = {};
1171+
for ( int i = 0; i < 60; ++i )
1172+
{
1173+
TEST_PumpCallbacks();
1174+
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
1175+
1176+
SteamNetworkingSockets()->SendMessageToConnection(
1177+
hServer, kTiny, sizeof(kTiny), k_nSteamNetworkingSend_Unreliable, nullptr );
1178+
SteamNetworkingSockets()->SendMessageToConnection(
1179+
hClient, kTiny, sizeof(kTiny), k_nSteamNetworkingSend_Unreliable, nullptr );
1180+
1181+
SteamNetConnectionInfo_t infoClient, infoServer;
1182+
bool bGotClient = SteamNetworkingSockets()->GetConnectionInfo( hClient, &infoClient );
1183+
SteamNetworkingSockets()->GetConnectionInfo( hServer, &infoServer );
1184+
1185+
if ( !bGotClient || infoClient.m_eState != k_ESteamNetworkingConnectionState_Connected )
1186+
{
1187+
TEST_Printf( "*** Connection dropped (iter %d) -- stalled receiver was not handled ***\n", i );
1188+
TEST_Printf( " Client: state=%d reason=%d '%s'\n",
1189+
(int)infoClient.m_eState, (int)infoClient.m_eEndReason, infoClient.m_szEndDebug );
1190+
TEST_Printf( " Server: state=%d reason=%d '%s'\n",
1191+
(int)infoServer.m_eState, (int)infoServer.m_eEndReason, infoServer.m_szEndDebug );
1192+
assert( false );
1193+
}
1194+
}
1195+
1196+
TEST_Printf( "Connection survived with full receive queue -- OK\n" );
1197+
SteamNetworkingSockets()->CloseConnection( hServer, 0, nullptr, false );
1198+
SteamNetworkingSockets()->CloseConnection( hClient, 0, nullptr, false );
1199+
}
1200+
10891201
void Test_pipe()
10901202
{
10911203
TEST_Printf( "***************************************************\n" );
@@ -1459,15 +1571,16 @@ int main( int argc, const char **argv )
14591571
TEST(lane_quick_queueanddrain),
14601572
TEST(lane_quick_priority_and_background),
14611573
TEST(pipe),
1462-
TEST(send_buffer_full)
1574+
TEST(send_buffer_full),
1575+
TEST(recv_buf_full)
14631576
};
14641577

14651578
struct Suite_t {
14661579
const char *m_pszName;
14671580
std::vector< Test_t > m_vecTests;
14681581
};
14691582
static const Suite_t test_suites[] = {
1470-
{ "suite-quick", { TEST(identity), TEST(quick), TEST(lane_quick_queueanddrain), TEST(lane_quick_priority_and_background), TEST(pipe), TEST(send_buffer_full) } }
1583+
{ "suite-quick", { TEST(identity), TEST(quick), TEST(lane_quick_queueanddrain), TEST(lane_quick_priority_and_background), TEST(pipe), TEST(send_buffer_full), TEST(recv_buf_full) } }
14711584
};
14721585

14731586
if ( argc < 2 )

0 commit comments

Comments
 (0)