mirror of
https://github.com/ValveSoftware/GameNetworkingSockets.git
synced 2026-05-29 16:20:34 +00:00
The test is compiling, but not fully working. NOTE: THIS IS UNIT TEST, NOT A TYPICAL SAMPLE OF HOW TO USE THE LIBRARY, PLEASE DON'T CUT AND PASTE. If we have time we'll add a better sample code with a proper server and client. Also renamed some "SteamDatagram" things (which is the name used to refer to the relay network, not included with the opensource code) to "SteamnetworkingSockets". Remove include of platform.h in a public header. That's not supposed to be visible or needed outside of compiling the library. Added a stub for ISteamNetworkingUtils. The only thing that's available in the opensource library is the timestamp function. In the Steamworks version there are utilities for reasoning about the relay network and estimating ping between two locations.
388 lines
13 KiB
C++
388 lines
13 KiB
C++
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <random>
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
#include <steamnetworkingsockets/isteamnetworkingsockets.h>
|
|
#include <steamnetworkingsockets/isteamnetworkingutils.h>
|
|
#ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE
|
|
#include <steam/steam_api.h>
|
|
#endif
|
|
|
|
#define PORT_CLIENT 27005 // Default client port, UDP/TCP
|
|
#define PORT_SERVER 27200 // Default server port, UDP/TCP
|
|
|
|
void InitSteamDatagramConnectionSockets()
|
|
{
|
|
#ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
|
|
SteamDatagramErrMsg errMsg;
|
|
if ( !GameNetworkingSockets_Init( errMsg ) )
|
|
{
|
|
fprintf( stderr, "GameNetworkingSockets_Init failed. %s", errMsg );
|
|
exit(1);
|
|
}
|
|
#else
|
|
SteamAPI_Init();
|
|
|
|
SteamDatagramErrMsg errMsg;
|
|
if ( !SteamDatagramClient_Init( -1, errMsg ) )
|
|
{
|
|
fprintf( stderr, "SteamDatagramClient_Init failed. %s", errMsg );
|
|
exit(1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ShutdownSteamDatagramConnectionSockets()
|
|
{
|
|
#ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
|
|
GameNetworkingSockets_Kill();
|
|
#else
|
|
SteamDatagramClient_Kill();
|
|
#endif
|
|
}
|
|
|
|
HSteamListenSocket g_hSteamListenSocket = k_HSteamListenSocket_Invalid;
|
|
|
|
struct TestMsg
|
|
{
|
|
int64 m_nMsgNum;
|
|
bool m_bReliable;
|
|
int m_cbSize;
|
|
static constexpr int k_cbMaxSize = 20*1000;
|
|
uint8 m_data[ k_cbMaxSize ];
|
|
};
|
|
|
|
struct SSteamNetConnection
|
|
{
|
|
SSteamNetConnection( const char *pName )
|
|
: m_sName( pName )
|
|
{
|
|
}
|
|
|
|
std::string m_sName;
|
|
int64 m_nReliableSendMsgCount = 0;
|
|
int64 m_nSendMsgCount = 0;
|
|
int64 m_nReliableExpectedRecvMsg = 1;
|
|
int64 m_nExpectedRecvMsg = 1;
|
|
HSteamNetConnection m_hSteamNetConnection = k_HSteamNetConnection_Invalid;
|
|
SteamNetworkingMicroseconds m_usecLastTransmit = 0;
|
|
SteamNetworkingMicroseconds m_usecTransmitTime = 0;
|
|
bool m_bIsConnected = false;
|
|
int m_nMaxPendingBytes = 384 * 1024;
|
|
bool m_bQuiet = true;
|
|
SteamNetworkingMicroseconds m_usecQuietDuration = 5000000;
|
|
SteamNetworkingMicroseconds m_usecActiveDuration = 10000000;
|
|
SteamNetworkingMicroseconds m_usecWhenStateEnd = 0;
|
|
};
|
|
|
|
SSteamNetConnection g_connectionServer( "Server" );
|
|
SSteamNetConnection g_connectionClient( "Client" );
|
|
|
|
std::default_random_engine g_rand;
|
|
SteamNetworkingMicroseconds g_usecTestElapsed;
|
|
|
|
void Send( ISteamNetworkingSockets *pSteamSocketNetworking, SSteamNetConnection &connection )
|
|
{
|
|
if ( connection.m_hSteamNetConnection == k_HSteamNetConnection_Invalid )
|
|
return;
|
|
|
|
if ( g_usecTestElapsed - connection.m_usecLastTransmit < connection.m_usecTransmitTime )
|
|
return;
|
|
|
|
// Start and stop active use of the connection. This exercizes a bunch of important edge cases
|
|
// such as keepalives, the bandwidth estimation, etc.
|
|
if ( g_usecTestElapsed > connection.m_usecWhenStateEnd )
|
|
{
|
|
connection.m_bQuiet = !connection.m_bQuiet;
|
|
connection.m_usecWhenStateEnd = g_usecTestElapsed +
|
|
( connection.m_bQuiet ? connection.m_usecQuietDuration : connection.m_usecActiveDuration );
|
|
}
|
|
if ( connection.m_bQuiet )
|
|
return;
|
|
|
|
TestMsg msg;
|
|
msg.m_bReliable = std::uniform_real_distribution<>()( g_rand ) < .75f;
|
|
msg.m_cbSize = std::uniform_int_distribution<>( 20, msg.m_bReliable ? TestMsg::k_cbMaxSize : 5000 )( g_rand );
|
|
//bIsReliable = false;
|
|
//nBytes = 1200-13;
|
|
|
|
// Don't send unless we are clear
|
|
SteamNetworkingQuickConnectionStatus info;
|
|
pSteamSocketNetworking->GetQuickConnectionStatus( connection.m_hSteamNetConnection, &info );
|
|
|
|
if ( info.m_cbPendingReliable + info.m_cbPendingUnreliable >= connection.m_nMaxPendingBytes )
|
|
{
|
|
// Log_Msg( LOG_NETWORKING_STEAMCONNECTIONS, "Send: %s stopped due to %d bytes pending\n",
|
|
// connection.m_sName.c_str(), connection.m_nMaxPendingBytes );
|
|
return; // Still stuff in the buffer
|
|
}
|
|
|
|
connection.m_usecLastTransmit = g_usecTestElapsed;
|
|
|
|
msg.m_nMsgNum = msg.m_bReliable ? ++connection.m_nReliableSendMsgCount : ++connection.m_nSendMsgCount;
|
|
for ( int n = 0; n < msg.m_cbSize; ++n )
|
|
{
|
|
msg.m_data[n] = (uint8)( msg.m_nMsgNum + n );
|
|
}
|
|
|
|
EResult result = pSteamSocketNetworking->SendMessageToConnection(
|
|
connection.m_hSteamNetConnection,
|
|
&msg,
|
|
(uint32)( sizeof(msg) - sizeof(msg.m_data) + msg.m_cbSize ),
|
|
msg.m_bReliable ? k_ESteamNetworkingSendType_Reliable : k_ESteamNetworkingSendType_Unreliable );
|
|
|
|
if ( result != k_EResultOK )
|
|
{
|
|
fprintf( stderr, "***ERROR ON Send: %s %.3f %s message %lld, %d bytes (pending %d bytes)\n",
|
|
connection.m_sName.c_str(),
|
|
g_usecTestElapsed*1e-6,
|
|
msg.m_bReliable ? "reliable" : "unreliable",
|
|
(long long)msg.m_nMsgNum,
|
|
msg.m_cbSize,
|
|
info.m_cbPendingReliable + info.m_cbPendingUnreliable );
|
|
}
|
|
else if ( 0 )
|
|
printf( "Send: %s %.3f %s message %lld, %d bytes (pending %d bytes)\n",
|
|
connection.m_sName.c_str(),
|
|
g_usecTestElapsed*1e-6,
|
|
msg.m_bReliable ? "reliable" : "unreliable",
|
|
(long long)msg.m_nMsgNum,
|
|
msg.m_cbSize,
|
|
info.m_cbPendingReliable + info.m_cbPendingUnreliable );
|
|
}
|
|
|
|
void Recv( ISteamNetworkingSockets *pSteamSocketNetworking )
|
|
{
|
|
|
|
while ( true )
|
|
{
|
|
SSteamNetConnection *pConnection = &g_connectionServer;
|
|
ISteamNetworkingMessage *pIncomingMsg = nullptr;
|
|
int numMsgs = pSteamSocketNetworking->ReceiveMessagesOnConnection( pConnection->m_hSteamNetConnection, &pIncomingMsg, 1 );
|
|
if ( numMsgs <= 0 )
|
|
{
|
|
pConnection = &g_connectionClient;
|
|
numMsgs = pSteamSocketNetworking->ReceiveMessagesOnConnection( pConnection->m_hSteamNetConnection, &pIncomingMsg, 1 );
|
|
if ( numMsgs <= 0 )
|
|
return;
|
|
}
|
|
|
|
const TestMsg *pTestMsg = static_cast<const TestMsg*>( pIncomingMsg->GetData() );
|
|
|
|
// Size makes sense?
|
|
assert( sizeof(*pTestMsg) - sizeof(pTestMsg->m_data) + pTestMsg->m_cbSize == pIncomingMsg->GetSize() );
|
|
|
|
// Check for sequence number nomoly.
|
|
int64 &nExpectedMsgNum = pTestMsg->m_bReliable ? pConnection->m_nReliableExpectedRecvMsg : pConnection->m_nExpectedRecvMsg;
|
|
if ( pTestMsg->m_nMsgNum != nExpectedMsgNum )
|
|
{
|
|
|
|
// Print that it happened.
|
|
printf(
|
|
"Recv: %s, %s MISMATCH NUM wanted %lld got %lld\n",
|
|
pConnection->m_sName.c_str(),
|
|
pTestMsg->m_bReliable ? "RELIABLE" : "UNRELIABLE",
|
|
(long long)nExpectedMsgNum,
|
|
(long long)pTestMsg->m_nMsgNum );
|
|
|
|
// This should not happen for reliable messages!
|
|
assert( !pTestMsg->m_bReliable );
|
|
}
|
|
|
|
nExpectedMsgNum = pTestMsg->m_nMsgNum + 1;
|
|
pIncomingMsg->Release();
|
|
}
|
|
}
|
|
|
|
struct TestSteamNetworkingSocketsCallbacks : public ISteamNetworkingSocketsCallbacks
|
|
{
|
|
virtual ~TestSteamNetworkingSocketsCallbacks() {} // Silence GCC warning
|
|
virtual void OnSteamNetConnectionStatusChanged( SteamNetConnectionStatusChangedCallback_t *pInfo ) override
|
|
{
|
|
// What's the state of the connection?
|
|
switch ( pInfo->m_info.m_eState )
|
|
{
|
|
case k_ESteamNetworkingConnectionState_ClosedByPeer:
|
|
case k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
|
|
printf( "Steam Net connection %x %s, reason %d: %s\n",
|
|
pInfo->m_hConn,
|
|
( pInfo->m_info.m_eState == k_ESteamNetworkingConnectionState_ClosedByPeer ? "closed by peer" : "problem detected locally" ),
|
|
pInfo->m_info.m_eEndReason,
|
|
pInfo->m_info.m_szEndDebug
|
|
);
|
|
|
|
// Close our end
|
|
SteamNetworkingSockets()->CloseConnection( pInfo->m_hConn, 0, nullptr, false );
|
|
|
|
if ( g_connectionServer.m_hSteamNetConnection == pInfo->m_hConn )
|
|
{
|
|
g_connectionServer.m_hSteamNetConnection = k_HSteamNetConnection_Invalid;
|
|
}
|
|
if ( g_connectionClient.m_hSteamNetConnection == pInfo->m_hConn )
|
|
{
|
|
g_connectionClient.m_hSteamNetConnection = k_HSteamNetConnection_Invalid;
|
|
}
|
|
|
|
break;
|
|
|
|
/*
|
|
case k_ESteamNetworkingConnectionState_None:
|
|
printf( "No steam Net connection %x (%s)\n", pInfo->m_hConn, pInfo->m_info.m_steamIDRemote.Render() );
|
|
|
|
if ( g_hSteamNetConnection == pInfo->m_hConn )
|
|
{
|
|
g_bIsConnected = false;
|
|
g_hSteamNetConnection = k_HSteamNetConnection_Invalid;
|
|
}
|
|
break;
|
|
*/
|
|
|
|
case k_ESteamNetworkingConnectionState_Connecting:
|
|
|
|
// Is this a connection we initiated, or one that we are receiving?
|
|
if ( g_hSteamListenSocket != k_HSteamListenSocket_Invalid && pInfo->m_info.m_hListenSocket == g_hSteamListenSocket )
|
|
{
|
|
// Somebody's knocking
|
|
printf( "Accepting Steam Net connection %x\n", pInfo->m_hConn );
|
|
g_connectionServer.m_hSteamNetConnection = pInfo->m_hConn;
|
|
g_connectionServer.m_bIsConnected = true;
|
|
SteamNetworkingSockets()->AcceptConnection( pInfo->m_hConn );
|
|
SteamNetworkingSockets()->SetConnectionName( g_connectionServer.m_hSteamNetConnection, "Server" );
|
|
|
|
}
|
|
break;
|
|
|
|
case k_ESteamNetworkingConnectionState_Connected:
|
|
if ( pInfo->m_hConn == g_connectionClient.m_hSteamNetConnection )
|
|
{
|
|
g_connectionClient.m_bIsConnected = true;
|
|
}
|
|
printf( "Connected Steam Net connection %x\n", pInfo->m_hConn );
|
|
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
TestSteamNetworkingSocketsCallbacks g_Callbacks;
|
|
|
|
void RunSteamDatagramConnectionTest()
|
|
{
|
|
ISteamNetworkingSockets *pSteamSocketNetworking = SteamNetworkingSockets();
|
|
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_DebugWindow, 1 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_Log_RTT, 0 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_Log_Packet, 0 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_Log_Segments, 0 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_Log_Feedback, 0 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_Log_Reliable, 0 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_Log_Message, 0 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_Log_Loss, 1 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_Log_X, 0 );
|
|
|
|
//pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_MinRate, 50000 );
|
|
//pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_SNP_MaxRate, 250000 );
|
|
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_FakePacketLoss_Send, 5 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_FakePacketLoss_Recv, 0 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_FakePacketLag_Send, 10 );
|
|
pSteamSocketNetworking->SetConfigurationValue( k_ESteamNetworkingConfigurationValue_FakePacketLag_Recv, 0 );
|
|
|
|
// Command line options:
|
|
// -connect:ip -- don't create a server, just try to connect to the given ip
|
|
// -serveronly -- don't create a client only create a server and wait for connection
|
|
bool bServerOnly = false;//( CommandLine()->FindParm( "-serveronly" ) != 0 );
|
|
bool bClientOnly = false;
|
|
uint32 nConnectIP = 0x7f000001; // 127.0.0.1
|
|
|
|
//const char *s_pszConnectParm = "-connect:";
|
|
//for ( int i = 0; i < CommandLine()->ParmCount(); ++i )
|
|
//{
|
|
// if ( V_strnicmp( CommandLine()->GetParm( i ), s_pszConnectParm, V_strlen( s_pszConnectParm ) ) == 0 )
|
|
// {
|
|
// bClientOnly = true;
|
|
// connection_adr.SetFromString( CommandLine()->GetParm( i ) + V_strlen( s_pszConnectParm ) );
|
|
// break;
|
|
// }
|
|
//}
|
|
|
|
// Create listen socket
|
|
if ( !bClientOnly )
|
|
{
|
|
g_hSteamListenSocket = pSteamSocketNetworking->CreateListenSocket( -1, 0x0u, PORT_SERVER );
|
|
}
|
|
if ( !bServerOnly )
|
|
{
|
|
g_connectionClient.m_hSteamNetConnection = pSteamSocketNetworking->ConnectByIPv4Address( nConnectIP, PORT_SERVER );
|
|
pSteamSocketNetworking->SetConnectionName( g_connectionClient.m_hSteamNetConnection, "Client" );
|
|
}
|
|
|
|
SteamNetworkingMicroseconds usecWhenStarted = SteamNetworkingUtils()->GetLocalTimestamp();
|
|
SteamNetworkingMicroseconds usecTestDuration = 120*1000000;
|
|
SteamNetworkingMicroseconds usecWorstCase = usecTestDuration + 30*1000000;
|
|
|
|
// Loop!
|
|
|
|
//SteamNetworkingMicroseconds usecLastNow = usecWhenStarted;
|
|
|
|
while ( true )
|
|
{
|
|
SteamNetworkingMicroseconds now = SteamNetworkingUtils()->GetLocalTimestamp();
|
|
g_usecTestElapsed = now - usecWhenStarted;
|
|
// If flElapsed > 1.0 when in debug, just slamp it
|
|
//if ( Plat_IsInDebugSession() && now - flLastNow > 1.0f )
|
|
//{
|
|
// flStartTime += now - flLastNow - 1.0f;
|
|
// flElapsed = now - flStartTime;
|
|
//}
|
|
//usecLastNow = now;
|
|
|
|
// Execute Steam net connection callbacks now
|
|
#ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE
|
|
SteamAPI_RunCallbacks();
|
|
#endif
|
|
pSteamSocketNetworking->RunCallbacks( &g_Callbacks );
|
|
|
|
// Break out if we lose connection
|
|
if ( !bServerOnly && g_connectionClient.m_hSteamNetConnection == k_HSteamNetConnection_Invalid )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( !g_connectionClient.m_bIsConnected && !g_connectionServer.m_bIsConnected )
|
|
{
|
|
continue; // Just spin until connected
|
|
}
|
|
|
|
Send( pSteamSocketNetworking, g_connectionServer );
|
|
Send( pSteamSocketNetworking, g_connectionClient );
|
|
|
|
Recv( pSteamSocketNetworking );
|
|
|
|
if ( g_usecTestElapsed > usecTestDuration && g_connectionServer.m_hSteamNetConnection != k_HSteamNetConnection_Invalid )
|
|
{
|
|
pSteamSocketNetworking->CloseConnection( g_connectionServer.m_hSteamNetConnection, 0, nullptr, false );
|
|
g_connectionServer.m_hSteamNetConnection = k_HSteamNetConnection_Invalid;
|
|
}
|
|
|
|
// Make sure we haven't taken too long
|
|
assert( g_usecTestElapsed < usecWorstCase );
|
|
|
|
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
|
|
}
|
|
}
|
|
|
|
int main( )
|
|
{
|
|
// Create client and server sockets
|
|
InitSteamDatagramConnectionSockets();
|
|
|
|
// Run the test
|
|
RunSteamDatagramConnectionTest();
|
|
|
|
ShutdownSteamDatagramConnectionSockets();
|
|
return 0;
|
|
}
|