You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
6.2 KiB
300 lines
6.2 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include <windows.h> |
|
#include "messagemgr.h" |
|
#include "tcpsocket.h" |
|
#include "iphelpers.h" |
|
#include "tier0/platform.h" |
|
#include "threadhelpers.h" |
|
|
|
|
|
#define MSGMGR_LISTEN_PORT_FIRST 22512 |
|
#define MSGMGR_LISTEN_PORT_LAST 22520 |
|
|
|
|
|
|
|
#define BROADCAST_INTERVAL 2 // Broadcast our presence every N seconds. |
|
|
|
#define NUM_QUEUED_MESSAGES 200 |
|
|
|
|
|
|
|
class CMessageMgr : public IMessageMgr |
|
{ |
|
public: |
|
CMessageMgr(); |
|
~CMessageMgr(); |
|
|
|
bool Init(); |
|
void Term(); |
|
|
|
|
|
// IMessageMgr overrides. |
|
public: |
|
|
|
virtual void Print( const char *pMsg ); |
|
|
|
|
|
|
|
private: |
|
|
|
DWORD ThreadFn(); |
|
static DWORD WINAPI StaticThreadFn( LPVOID pParameter ); |
|
|
|
|
|
private: |
|
|
|
// Only our thread touches this, NOT the main thread. |
|
CUtlLinkedList<ITCPSocket*,int> m_Sockets; |
|
|
|
HANDLE m_hThread; |
|
DWORD m_dwThreadID; |
|
|
|
HANDLE m_hExitObj; // This is signalled when we want the thread to exit. |
|
HANDLE m_hExitResponseObj; // The thread sets this when it exits. |
|
|
|
HANDLE m_hMessageObj; // This signals to the thread that there's a message to send. |
|
HANDLE m_hMessageSentObj; // This signals back to the main thread that the message was sent. |
|
const char *m_pMessageText; // The text to send. |
|
|
|
// This is only touched by the thread. |
|
CUtlLinkedList<char*,int> m_MessageQ; // FIFO of NUM_QUEUED_MESSAGES. |
|
|
|
ITCPListenSocket *m_pListenSocket; |
|
int m_iListenPort; |
|
|
|
ISocket *m_pBroadcastSocket; |
|
double m_flLastBroadcast; |
|
|
|
}; |
|
|
|
|
|
|
|
CMessageMgr::CMessageMgr() |
|
{ |
|
m_pBroadcastSocket = NULL; |
|
m_pListenSocket = NULL; |
|
m_hThread = NULL; |
|
m_hExitObj = m_hExitResponseObj = m_hMessageObj = m_hMessageSentObj = NULL; |
|
} |
|
|
|
|
|
CMessageMgr::~CMessageMgr() |
|
{ |
|
Term(); |
|
} |
|
|
|
|
|
bool CMessageMgr::Init() |
|
{ |
|
m_hExitObj = CreateEvent( NULL, false, false, NULL ); |
|
m_hExitResponseObj = CreateEvent( NULL, false, false, NULL ); |
|
m_hMessageObj = CreateEvent( NULL, false, false, NULL ); |
|
m_hMessageSentObj = CreateEvent( NULL, false, false, NULL ); |
|
if ( !m_hExitObj || !m_hExitResponseObj || !m_hMessageObj || !m_hMessageSentObj ) |
|
return false; |
|
|
|
// Create the broadcast socket. |
|
m_pBroadcastSocket = CreateIPSocket(); |
|
if ( !m_pBroadcastSocket ) |
|
return false; |
|
|
|
if ( !m_pBroadcastSocket->BindToAny( 0 ) ) |
|
return false; |
|
|
|
|
|
// Create the listen socket. |
|
m_pListenSocket = NULL; |
|
for ( m_iListenPort=MSGMGR_LISTEN_PORT_FIRST; m_iListenPort <= MSGMGR_LISTEN_PORT_LAST; m_iListenPort++ ) |
|
{ |
|
m_pListenSocket = CreateTCPListenSocket( m_iListenPort ); |
|
if ( m_pListenSocket ) |
|
break; |
|
} |
|
if ( !m_pListenSocket ) |
|
return false; |
|
|
|
|
|
// Create our broadcast/connection thread. |
|
m_flLastBroadcast = 0; |
|
m_hThread = CreateThread( |
|
NULL, |
|
0, |
|
&CMessageMgr::StaticThreadFn, |
|
this, |
|
0, |
|
&m_dwThreadID ); |
|
|
|
if ( !m_hThread ) |
|
return false; |
|
|
|
Plat_SetThreadName( m_dwThreadID, "MessageMgr" ); |
|
return true; |
|
} |
|
|
|
|
|
void CMessageMgr::Term() |
|
{ |
|
// Wait for the thread to exit? |
|
if ( m_hThread ) |
|
{ |
|
DWORD dwExitCode = 0; |
|
if ( GetExitCodeThread( m_hThread, &dwExitCode ) && dwExitCode == STILL_ACTIVE ) |
|
{ |
|
SetEvent( m_hExitObj ); |
|
WaitForSingleObject( m_hExitResponseObj, INFINITE ); |
|
} |
|
|
|
CloseHandle( m_hThread ); |
|
m_hThread = NULL; |
|
} |
|
|
|
CloseHandle( m_hExitObj ); |
|
m_hExitObj = NULL; |
|
|
|
CloseHandle( m_hExitResponseObj ); |
|
m_hExitResponseObj = NULL; |
|
|
|
CloseHandle( m_hMessageObj ); |
|
m_hMessageObj = NULL; |
|
|
|
CloseHandle( m_hMessageSentObj ); |
|
m_hMessageSentObj = NULL; |
|
|
|
if ( m_pListenSocket ) |
|
{ |
|
m_pListenSocket->Release(); |
|
m_pListenSocket = NULL; |
|
} |
|
|
|
if ( m_pBroadcastSocket ) |
|
{ |
|
m_pBroadcastSocket->Release(); |
|
m_pBroadcastSocket = NULL; |
|
} |
|
} |
|
|
|
|
|
void CMessageMgr::Print( const char *pMsg ) |
|
{ |
|
m_pMessageText = pMsg; |
|
SetEvent( m_hMessageObj ); |
|
WaitForSingleObject( m_hMessageSentObj, INFINITE ); |
|
} |
|
|
|
|
|
DWORD CMessageMgr::ThreadFn() |
|
{ |
|
while ( 1 ) |
|
{ |
|
// Broadcast our presence? |
|
double flCurTime = Plat_FloatTime(); |
|
if ( flCurTime - m_flLastBroadcast >= BROADCAST_INTERVAL ) |
|
{ |
|
// Broadcast our presence. |
|
char msg[9]; |
|
msg[0] = MSGMGR_PACKETID_ANNOUNCE_PRESENCE; |
|
*((int*)&msg[1]) = MSGMGR_VERSION; |
|
*((int*)&msg[5]) = m_iListenPort; |
|
m_pBroadcastSocket->Broadcast( msg, sizeof( msg ), MSGMGR_BROADCAST_PORT ); |
|
|
|
m_flLastBroadcast = flCurTime; |
|
} |
|
|
|
|
|
// Accept new connections. |
|
CIPAddr addr; |
|
ITCPSocket *pConn = m_pListenSocket->UpdateListen( &addr ); |
|
if ( pConn ) |
|
{ |
|
// Send what's in our queue. |
|
FOR_EACH_LL( m_MessageQ, iQ ) |
|
{ |
|
char *pMsg = m_MessageQ[iQ]; |
|
int bufLen = strlen( pMsg ) + 1; |
|
|
|
char packetID = MSGMGR_PACKETID_MSG; |
|
const void *data[2] = { &packetID, pMsg }; |
|
int len[2] = { 1, bufLen }; |
|
|
|
// Send it out to our sockets. |
|
pConn->SendChunks( data, len, 2 ); |
|
} |
|
|
|
m_Sockets.AddToTail( pConn ); |
|
} |
|
|
|
|
|
// Should we exit? |
|
HANDLE handles[2] = {m_hExitObj, m_hMessageObj}; |
|
DWORD ret = WaitForMultipleObjects( 2, handles, FALSE, 200 ); |
|
if ( ret == WAIT_OBJECT_0 ) |
|
{ |
|
break; |
|
} |
|
else if ( ret == (WAIT_OBJECT_0+1) ) |
|
{ |
|
// Add it to the queue. |
|
int index; |
|
if ( m_MessageQ.Count() >= NUM_QUEUED_MESSAGES ) |
|
{ |
|
index = m_MessageQ.Tail(); |
|
delete m_MessageQ[index]; |
|
} |
|
else |
|
{ |
|
index = m_MessageQ.AddToTail(); |
|
} |
|
int bufLen = strlen( m_pMessageText ) + 1; |
|
m_MessageQ[index] = new char[ bufLen ]; |
|
strcpy( m_MessageQ[index], m_pMessageText ); |
|
|
|
|
|
|
|
// Ok, send out the message. |
|
char packetID = MSGMGR_PACKETID_MSG; |
|
const void *data[2] = { &packetID, m_pMessageText }; |
|
int len[2] = { 1, bufLen }; |
|
|
|
// Send it out to our sockets. |
|
FOR_EACH_LL( m_Sockets, i ) |
|
{ |
|
m_Sockets[i]->SendChunks( data, len, 2 ); |
|
} |
|
|
|
// Notify the main thread that we've sent it. |
|
SetEvent( m_hMessageSentObj ); |
|
} |
|
} |
|
|
|
// Cleanup all our sockets (the main thread should never touch them). |
|
FOR_EACH_LL( m_Sockets, i ) |
|
m_Sockets[i]->Release(); |
|
|
|
m_Sockets.Purge(); |
|
|
|
m_MessageQ.PurgeAndDeleteElements(); |
|
|
|
SetEvent( m_hExitResponseObj ); |
|
return 0; |
|
} |
|
|
|
|
|
DWORD CMessageMgr::StaticThreadFn( LPVOID pParameter ) |
|
{ |
|
return ((CMessageMgr*)pParameter)->ThreadFn(); |
|
} |
|
|
|
|
|
static CMessageMgr g_MessageMgr; |
|
|
|
IMessageMgr* GetMessageMgr() |
|
{ |
|
return &g_MessageMgr; |
|
} |
|
|
|
|