From d9826ae275b57590ef3f443c65e00329a21bf51b Mon Sep 17 00:00:00 2001 From: ehouse Date: Mon, 5 Sep 2005 15:50:49 +0000 Subject: [PATCH] Add and handle disconnect message so a device can reuse its socket for a new game. --- relay/cref.cpp | 103 +++++++++++++++++++++++++++++----------------- relay/cref.h | 22 +++++----- relay/crefmgr.cpp | 30 ++++++++++---- relay/crefmgr.h | 13 +++--- relay/states.cpp | 26 ++++++++---- relay/states.h | 11 ++++- relay/xwrelay.cpp | 24 ++++++++++- relay/xwrelay.h | 16 ++++--- 8 files changed, 169 insertions(+), 76 deletions(-) diff --git a/relay/cref.cpp b/relay/cref.cpp index e94e99842..3adcd8b3f 100644 --- a/relay/cref.cpp +++ b/relay/cref.cpp @@ -71,7 +71,7 @@ SocketsIterator::Next() * CookieRef class *****************************************************************************/ -CookieRef::CookieRef( string s, CookieID id ) +CookieRef::CookieRef( const char* s, CookieID id ) : m_heatbeat(RelayConfigs::GetConfigs()->GetHeartbeatInterval()) , m_name(s) , m_totalSent(0) @@ -97,15 +97,15 @@ CookieRef::~CookieRef() for ( ; ; ) { RWWriteLock rwl( &m_sockets_rwlock ); - map::iterator iter = m_hostSockets.begin(); + map::iterator iter = m_sockets.begin(); - if ( iter == m_hostSockets.end() ) { + if ( iter == m_sockets.end() ) { break; } int socket = iter->second.m_socket; tPool->CloseSocket( socket ); - m_hostSockets.erase( iter ); + m_sockets.erase( iter ); } pthread_rwlock_destroy( &m_sockets_rwlock ); @@ -133,12 +133,28 @@ CookieRef::_Reconnect( int socket, HostID srcID ) handleEvents(); } +void +CookieRef::_Disconnect( int socket, HostID hostID ) +{ + CRefMgr::Get()->Disassociate( socket, this ); + + MutexLock ml( &m_EventsMutex ); + + CRefEvent evt; + evt.type = XW_EVENT_DISCONNECTMSG; + evt.u.discon.socket = socket; + evt.u.discon.srcID = hostID; + m_eventQueue.push_back( evt ); + + handleEvents(); +} + int CookieRef::SocketForHost( HostID dest ) { int socket; - map::iterator iter = m_hostSockets.find( dest ); - if ( iter == m_hostSockets.end() ) { + map::iterator iter = m_sockets.find( dest ); + if ( iter == m_sockets.end() ) { socket = -1; } else { socket = iter->second.m_socket; @@ -148,6 +164,18 @@ CookieRef::SocketForHost( HostID dest ) return socket; } +/* The idea here is: have we never seen the XW_ST_ALLCONNECTED state. This + needs to include any states reachable from XW_ST_ALLCONNECTED from which + recovery back to XW_ST_ALLCONNECTED is possible. This is used to decide + whether to admit a connection based on its cookie -- whether that cookie + should join an existing cref or get a new one? */ +int +CookieRef::NeverFullyConnected() +{ + return m_curState != XW_ST_ALLCONNECTED + && m_curState != XW_ST_MISSING; +} + void CookieRef::notifyDisconn( const CRefEvent* evt ) { @@ -161,19 +189,18 @@ CookieRef::notifyDisconn( const CRefEvent* evt ) } /* notifyDisconn */ void -CookieRef::removeSocket( const CRefEvent* evt ) +CookieRef::removeSocket( int socket ) { - int socket = evt->u.rmsock.socket; int count; { RWWriteLock rwl( &m_sockets_rwlock ); - count = CountSockets(); + count = m_sockets.size(); assert( count > 0 ); - map::iterator iter = m_hostSockets.begin(); - while ( iter != m_hostSockets.end() ) { + map::iterator iter = m_sockets.begin(); + while ( iter != m_sockets.end() ) { if ( iter->second.m_socket == socket ) { - m_hostSockets.erase(iter); + m_sockets.erase(iter); --count; break; } @@ -181,9 +208,6 @@ CookieRef::removeSocket( const CRefEvent* evt ) } } - /* Does this belong here or at a higher level? */ - XWThreadPool::GetTPool()->CloseSocket( socket ); - if ( count == 0 ) { pushLastSocketGoneEvent(); } @@ -196,8 +220,8 @@ CookieRef::HasSocket( int socket ) logf( "CookieRef::HasSocket" ); RWReadLock rwl( &m_sockets_rwlock ); - map::iterator iter = m_hostSockets.begin(); - while ( iter != m_hostSockets.end() ) { + map::iterator iter = m_sockets.begin(); + while ( iter != m_sockets.end() ) { if ( iter->second.m_socket == socket ) { found = 1; break; @@ -222,8 +246,8 @@ CookieRef::_CheckHeartbeats( time_t now ) MutexLock ml( &m_EventsMutex ); { RWReadLock rwl( &m_sockets_rwlock ); - map::iterator iter = m_hostSockets.begin(); - while ( iter != m_hostSockets.end() ) { + map::iterator iter = m_sockets.begin(); + while ( iter != m_sockets.end() ) { time_t last = iter->second.m_lastHeartbeat; if ( (now - last) > GetHeartbeat() ) { pushHeartFailedEvent( iter->second.m_socket ); @@ -375,9 +399,9 @@ CookieRef::handleEvents() XW_RELAY_ACTION takeAction; if ( getFromTable( m_curState, evt.type, &takeAction, &m_nextState ) ) { - logf( "moving from state %s to state %s for event %s", - stateString(m_curState), stateString(m_nextState), - eventString(evt.type) ); + logf( "cid %d: moving from state %s to state %s for event %s", + m_connectionID, stateString(m_curState), + stateString(m_nextState), eventString(evt.type) ); switch( takeAction ) { case XW_ACTION_SEND_1ST_RSP: @@ -413,6 +437,11 @@ CookieRef::handleEvents() XWRELAY_ERROR_HEART_YOU ); break; + case XW_ACTION_DISCONNECT: + removeSocket( evt.u.discon.socket ); + /* Don't notify. This is a normal part of a game ending. */ + break; + case XW_ACTION_NOTEHEART: noteHeartbeat( &evt ); break; @@ -424,7 +453,7 @@ CookieRef::handleEvents() case XW_ACTION_REMOVESOCKET: notifyOthers( evt.u.rmsock.socket, XWRELAY_DISCONNECT_OTHER, XWRELAY_ERROR_LOST_OTHER ); - removeSocket( &evt ); + removeSocket( evt.u.rmsock.socket ); break; case XW_ACTION_NONE: @@ -437,8 +466,6 @@ CookieRef::handleEvents() } m_curState = m_nextState; - } else { - assert(0); } } } /* handleEvents */ @@ -447,12 +474,14 @@ void CookieRef::send_with_length( int socket, unsigned char* buf, int bufLen ) { SocketWriteLock slock( socket ); + if ( slock.socketFound() ) { - if ( send_with_length_unsafe( socket, buf, bufLen ) ) { - RecordSent( bufLen, socket ); - } else { - /* ok that the slock above is still in scope */ - killSocket( socket, "couldn't send" ); + if ( send_with_length_unsafe( socket, buf, bufLen ) ) { + RecordSent( bufLen, socket ); + } else { + /* ok that the slock above is still in scope */ + killSocket( socket, "couldn't send" ); + } } } @@ -498,7 +527,7 @@ CookieRef::sendResponse( const CRefEvent* evt ) logf( "remembering pair: hostid=%x, socket=%d", id, socket ); RWWriteLock ml( &m_sockets_rwlock ); HostRec hr(socket); - m_hostSockets.insert( pair(id,hr) ); + m_sockets.insert( pair(id,hr) ); /* Now send the response */ unsigned char buf[7]; @@ -589,8 +618,8 @@ CookieRef::notifyOthers( int socket, XWRelayMsg msg, XWREASON why ) RWReadLock ml( &m_sockets_rwlock ); - map::iterator iter = m_hostSockets.begin(); - while ( iter != m_hostSockets.end() ) { + map::iterator iter = m_sockets.begin(); + while ( iter != m_sockets.end() ) { int other = iter->second.m_socket; if ( other != socket ) { send_msg( other, iter->first, msg, why ); @@ -604,8 +633,8 @@ CookieRef::disconnectSockets( int socket, XWREASON why ) { if ( socket == 0 ) { RWReadLock ml( &m_sockets_rwlock ); - map::iterator iter = m_hostSockets.begin(); - while ( iter != m_hostSockets.end() ) { + map::iterator iter = m_sockets.begin(); + while ( iter != m_sockets.end() ) { assert( iter->second.m_socket != 0 ); disconnectSockets( iter->second.m_socket, why ); ++iter; @@ -624,8 +653,8 @@ CookieRef::noteHeartbeat( const CRefEvent* evt ) RWWriteLock rwl( &m_sockets_rwlock ); - map::iterator iter = m_hostSockets.find(id); - if ( iter == m_hostSockets.end() ) { + map::iterator iter = m_sockets.find(id); + if ( iter == m_sockets.end() ) { logf( "no socket for HostID %d", id ); } else { diff --git a/relay/cref.h b/relay/cref.h index 6ecfd8215..f85f3499e 100644 --- a/relay/cref.h +++ b/relay/cref.h @@ -52,34 +52,35 @@ class CookieRef { friend class SafeCref; friend class CookieMapIterator; - CookieRef( string s, CookieID id ); + CookieRef( const char* s, CookieID id ); ~CookieRef(); /* Within this cookie, remember that this hostID and socket go together. If the hostID is HOST_ID_SERVER, it's the server. */ CookieID GetCookieID() { return m_connectionID; } int HostKnown( HostID host ) { return -1 != SocketForHost( host ); } - int CountSockets() { return m_hostSockets.size(); } + int CountSockets() { return m_sockets.size(); } int HasSocket( int socket ); string Name() { return m_name; } short GetHeartbeat() { return m_heatbeat; } int SocketForHost( HostID dest ); - int NotFullyConnected() { return m_curState != XW_ST_ALLCONNECTED; } + int NeverFullyConnected(); /* for console */ void _PrintCookieInfo( string& out ); void PrintSocketInfo( string& out, int socket ); static CookieMapIterator GetCookieIterator(); - static CookieRef* AddNew( string s, CookieID id ); + /* Nuke an existing */ static void Delete( CookieID id ); static void Delete( const char* name ); void _Connect( int socket, HostID srcID ); void _Reconnect( int socket, HostID srcID ); + void _Disconnect(int socket, HostID hostID ); void _HandleHeartbeat( HostID id, int socket ); void _CheckHeartbeats( time_t now ); void _Forward( HostID src, HostID dest, unsigned char* buf, int buflen ); @@ -101,6 +102,10 @@ class CookieRef { int socket; HostID srcID; } con; + struct { + int socket; + HostID srcID; + } discon; struct { HostID id; int socket; @@ -109,10 +114,6 @@ class CookieRef { time_t now; vector* victims; } htime; - struct { - HostID hostID; - int reason; - } discon; struct { int socket; } rmsock; @@ -160,14 +161,15 @@ class CookieRef { void disconnectSockets( int socket, XWREASON why ); void noteHeartbeat(const CRefEvent* evt); void notifyDisconn(const CRefEvent* evt); - void removeSocket(const CRefEvent* evt); + void removeSocket( int socket ); /* timer callback */ static void s_checkAllConnected( void* closure ); - map m_hostSockets; + map m_sockets; pthread_rwlock_t m_sockets_rwlock; + CookieID m_connectionID; short m_heatbeat; /* might change per carrier or something. */ string m_name; diff --git a/relay/crefmgr.cpp b/relay/crefmgr.cpp index 87a7c95a9..f59945046 100644 --- a/relay/crefmgr.cpp +++ b/relay/crefmgr.cpp @@ -70,7 +70,7 @@ CRefMgr::getMakeCookieRef_locked( const char* cookie, CookieID connID ) CookieID newId = CookieIdForName( cookie ); if ( newId == 0 ) { /* not in the system */ - cref = AddNew( string(cookie), connID ); + cref = AddNew( cookie, connID ); } else { cref = getCookieRef_impl( newId ); } @@ -94,7 +94,7 @@ CRefMgr::CookieIdForName( const char* name ) CookieMap::iterator iter = m_cookieMap.begin(); while ( iter != m_cookieMap.end() ) { cref = iter->second; - if ( cref->Name() == s && cref->NotFullyConnected() ) { + if ( cref->Name() == s && cref->NeverFullyConnected() ) { return cref->GetCookieID(); } ++iter; @@ -115,6 +115,21 @@ CRefMgr::Associate( int socket, CookieRef* cref ) m_SocketStuff.insert( pair< int, SocketStuff* >( socket, stuff ) ); } +void +CRefMgr::Disassociate( int socket, CookieRef* cref ) +{ + MutexLock ml( &m_SocketStuffMutex ); + SocketMap::iterator iter = m_SocketStuff.find( socket ); + if ( iter == m_SocketStuff.end() ) { + logf( "can't find cref/threadID pair for socket %d", socket ); + } else { + SocketStuff* stuff = iter->second; + assert( stuff->m_cref == cref ); + delete stuff; + m_SocketStuff.erase( iter ); + } +} + pthread_mutex_t* CRefMgr::GetWriteMutexForSocket( int socket ) { @@ -124,7 +139,8 @@ CRefMgr::GetWriteMutexForSocket( int socket ) SocketStuff* stuff = iter->second; return &stuff->m_writeMutex; } - assert( 0 ); + logf( "GetWriteMutexForSocket: not found" ); + return NULL; } /* GetWriteMutexForSocket */ void @@ -240,13 +256,13 @@ CRefMgr::UnlockCref( CookieRef* cref ) } CookieRef* -CRefMgr::AddNew( string s, CookieID id ) +CRefMgr::AddNew( const char* s, CookieID id ) { RWWriteLock rwl( &m_cookieMapRWLock ); logf( "making new cref" ); CookieRef* ref = new CookieRef( s, id ); m_cookieMap.insert( pair(ref->GetCookieID(), ref ) ); - logf( "paired cookie %s with id %d", s.c_str(), ref->GetCookieID() ); + logf( "paired cookie %s with id %d", s, ref->GetCookieID() ); return ref; } /* AddNew */ @@ -366,7 +382,6 @@ CookieMapIterator::Next() SafeCref::SafeCref( const char* cookie, CookieID connID ) : m_cref( NULL ) - , m_connID(0) , m_mgr( CRefMgr::Get() ) { CookieRef* cref = m_mgr->getMakeCookieRef_locked( cookie, connID ); @@ -379,7 +394,6 @@ SafeCref::SafeCref( const char* cookie, CookieID connID ) SafeCref::SafeCref( CookieID connID ) : m_cref( NULL ) - , m_connID(0) , m_mgr( CRefMgr::Get() ) { CookieRef* cref = m_mgr->getCookieRef_locked( connID ); @@ -392,7 +406,6 @@ SafeCref::SafeCref( CookieID connID ) SafeCref::SafeCref( int socket ) : m_cref( NULL ) - , m_connID(0) , m_mgr( CRefMgr::Get() ) { CookieRef* cref = m_mgr->getCookieRef_locked( socket ); @@ -405,7 +418,6 @@ SafeCref::SafeCref( int socket ) SafeCref::SafeCref( CookieRef* cref ) : m_cref( NULL ) - , m_connID(0) , m_mgr( CRefMgr::Get() ) { if ( m_mgr->checkCookieRef_locked( cref ) ) { diff --git a/relay/crefmgr.h b/relay/crefmgr.h index fc37f0f75..13e8704ad 100644 --- a/relay/crefmgr.h +++ b/relay/crefmgr.h @@ -69,6 +69,7 @@ class CRefMgr { /* Track sockets independent of cookie refs */ void Associate( int socket, CookieRef* cref ); + void Disassociate( int socket, CookieRef* cref ); pthread_mutex_t* GetWriteMutexForSocket( int socket ); void RemoveSocketRefs( int socket ); void PrintSocketInfo( int socket, string& out ); @@ -76,12 +77,12 @@ class CRefMgr { private: friend class SafeCref; - CookieRef* getMakeCookieRef_locked( const char* cookie, CookieID connID ); + CookieRef* getMakeCookieRef_locked( const char* cookie, CookieID cid ); CookieRef* getCookieRef_locked( CookieID cookieID ); CookieRef* getCookieRef_locked( int socket ); int checkCookieRef_locked( CookieRef* cref ); CookieRef* getCookieRef_impl( CookieID cookieID ); - CookieRef* AddNew( string s, CookieID id ); + CookieRef* AddNew( const char* s, CookieID id ); int LockCref( CookieRef* cref ); void UnlockCref( CookieRef* cref ); @@ -105,8 +106,8 @@ class SafeCref { CookieRef instance at a time. */ public: - SafeCref( const char* cookie, CookieID connID ); - SafeCref( CookieID connID ); + SafeCref( const char* cookie, CookieID cid ); + SafeCref( CookieID cid ); SafeCref( int socket ); SafeCref( CookieRef* cref ); ~SafeCref(); @@ -122,6 +123,9 @@ class SafeCref { void Reconnect( int socket, HostID srcID ) { m_cref->_Reconnect( socket, srcID ); } + void Disconnect(int socket, HostID hostID ) { + m_cref->_Disconnect( socket, hostID ); + } void Remove( int socket ) { m_cref->_Remove( socket ); } @@ -142,7 +146,6 @@ class SafeCref { private: CookieRef* m_cref; - CookieID m_connID; CRefMgr* m_mgr; }; diff --git a/relay/states.cpp b/relay/states.cpp index bc2d88c6e..c81e0b1a8 100644 --- a/relay/states.cpp +++ b/relay/states.cpp @@ -66,6 +66,12 @@ StateTable g_stateTable[] = { { XW_ST_CONNECTING, XW_EVENT_CONNECTMSG, XW_ACTION_SENDRSP, XW_ST_CONNECTING }, { XW_ST_CONNECTING, XW_EVENT_RECONNECTMSG, XW_ACTION_SENDRSP, XW_ST_CONNECTING }, + /* Disconnect. */ + { XW_ST_ALLCONNECTED, XW_EVENT_DISCONNECTMSG, XW_ACTION_DISCONNECT, XW_ST_MISSING }, + { XW_ST_CONNECTING, XW_EVENT_DISCONNECTMSG, XW_ACTION_DISCONNECT, XW_ST_CONNECTING }, + { XW_ST_MISSING, XW_EVENT_DISCONNECTMSG, XW_ACTION_DISCONNECT, XW_ST_MISSING }, + { XW_ST_MISSING, XW_EVENT_NOMORESOCKETS, XW_ACTION_NONE, XW_ST_DEAD }, + /* Forward requests while not locked are ok -- but we must check that the target is actually present. If no socket available must drop the message */ { XW_ST_CONNECTING, XW_EVENT_FORWARDMSG, XW_ACTION_CHECKDEST, XW_ST_CHECKINGDEST }, @@ -79,27 +85,31 @@ StateTable g_stateTable[] = { /* Timeout before all connected */ { XW_ST_CONNECTING, XW_EVENT_CONNTIMER, XW_ACTION_TIMERDISCONNECT,XW_ST_DEAD }, + { XW_ST_ALLCONNECTED, XW_EVENT_HEARTFAILED, XW_ACTION_HEARTDISCONNECT, XW_ST_MISSING }, { XW_ST_CONNECTING, XW_EVENT_HEARTFAILED, XW_ACTION_HEARTDISCONNECT, XW_ST_CONNECTING }, - { XW_ST_ALLCONNECTED, XW_EVENT_HEARTFAILED, XW_ACTION_HEARTDISCONNECT, XW_ST_ALLCONNECTED }, + { XW_ST_MISSING, XW_EVENT_HEARTFAILED, XW_ACTION_HEARTDISCONNECT, XW_ST_MISSING }, { XW_ST_CONNECTING, XW_EVENT_REMOVESOCKET, XW_ACTION_REMOVESOCKET, XW_ST_CONNECTING }, - { XW_ST_ALLCONNECTED, XW_EVENT_REMOVESOCKET, XW_ACTION_REMOVESOCKET, XW_ST_CONNECTING }, + { XW_ST_ALLCONNECTED, XW_EVENT_REMOVESOCKET, XW_ACTION_REMOVESOCKET, XW_ST_MISSING }, + { XW_ST_MISSING, XW_EVENT_REMOVESOCKET, XW_ACTION_REMOVESOCKET, XW_ST_MISSING }, - { XW_ST_CONNECTING, XW_EVENT_NOMORESOCKETS, XW_ACTION_NONE, XW_ST_DEAD }, - { XW_ST_DEAD, XW_EVENT_NOMORESOCKETS, XW_ACTION_NONE, XW_ST_DEAD }, + { XW_ST_CONNECTING, XW_EVENT_NOMORESOCKETS, XW_ACTION_NONE, XW_ST_DEAD }, + { XW_ST_DEAD, XW_EVENT_NOMORESOCKETS, XW_ACTION_NONE, XW_ST_DEAD }, /* This is the entry we'll use most of the time */ { XW_ST_ALLCONNECTED, XW_EVENT_FORWARDMSG, XW_ACTION_FWD, XW_ST_ALLCONNECTED }, { XW_ST_ALLCONNECTED, XW_EVENT_CONNTIMER, XW_ACTION_NONE, XW_ST_ALLCONNECTED }, /* Heartbeat arrived */ - { XW_ST_CONNECTING, XW_EVENT_HEARTMSG, XW_ACTION_NOTEHEART, XW_ST_CONNECTING }, - { XW_ST_ALLCONNECTED, XW_EVENT_HEARTMSG, XW_ACTION_NOTEHEART, XW_ST_ALLCONNECTED }, + { XW_ST_CONNECTING, XW_EVENT_HEARTMSG, XW_ACTION_NOTEHEART, XW_ST_CONNECTING }, + { XW_ST_ALLCONNECTED, XW_EVENT_HEARTMSG, XW_ACTION_NOTEHEART, XW_ST_ALLCONNECTED }, + { XW_ST_MISSING, XW_EVENT_HEARTMSG, XW_ACTION_NOTEHEART, XW_ST_MISSING }, /* I think we need a state XW_ST_SOMEMISSING. The game can't be played, but we're open to XWRELAY_RECONNECT (but not to XWRELAY_CONNECT) */ { XW_ST_CONNECTING, XW_EVENT_NOTIFYDISCON, XW_ACTION_NOTIFYDISCON, XW_ST_CONNECTING }, - { XW_ST_ALLCONNECTED, XW_EVENT_NOTIFYDISCON, XW_ACTION_NOTIFYDISCON, XW_ST_ALLCONNECTED }, + { XW_ST_ALLCONNECTED, XW_EVENT_NOTIFYDISCON, XW_ACTION_NOTIFYDISCON, XW_ST_MISSING }, + { XW_ST_MISSING, XW_EVENT_NOTIFYDISCON, XW_ACTION_NOTIFYDISCON, XW_ST_DEAD }, { XW_ST_DEAD, XW_EVENT_NOTIFYDISCON, XW_ACTION_NOTIFYDISCON, XW_ST_DEAD }, { XW_ST_DEAD, XW_EVENT_REMOVESOCKET, XW_ACTION_REMOVESOCKET, XW_ST_DEAD }, @@ -150,6 +160,7 @@ stateString( XW_RELAY_STATE state ) CASESTR(XW_ST_CHECKING_CONN); CASESTR(XW_ST_CHECKINGDEST); CASESTR(XW_ST_CHECKING_CAN_LOCK); + CASESTR(XW_ST_MISSING); } assert(0); return ""; @@ -162,6 +173,7 @@ eventString( XW_RELAY_EVENT evt ) CASESTR(XW_EVENT_NONE); CASESTR(XW_EVENT_CONNECTMSG); CASESTR(XW_EVENT_RECONNECTMSG); + CASESTR(XW_EVENT_DISCONNECTMSG); CASESTR(XW_EVENT_FORWARDMSG); CASESTR(XW_EVENT_HEARTMSG); CASESTR(XW_EVENT_CONNTIMER); diff --git a/relay/states.h b/relay/states.h index 740cfc3ba..7870dbd4d 100644 --- a/relay/states.h +++ b/relay/states.h @@ -22,7 +22,8 @@ /* states */ -typedef enum { +typedef +enum { XW_ST_NONE ,XW_ST_INITED /* Relay's running and the object's been @@ -42,6 +43,10 @@ typedef enum { relay to do its work. This is the state we're in most of the time. */ + ,XW_ST_MISSING /* We've been fully connected before but lost + somebody. Once [s]he's back we can be + fully connected again. */ + ,XW_ST_WAITING_RECON /* At least one device has been timed out or sent a disconnect message. We can't flow messages in this state, and will be killing @@ -67,6 +72,8 @@ typedef enum { ,XW_EVENT_RECONNECTMSG /* A device is re-connecting using the connID for this object */ + ,XW_EVENT_DISCONNECTMSG /* disconnect socket from this game/cref */ + ,XW_EVENT_FORWARDMSG /* A message needs forwarding */ ,XW_EVENT_HEARTMSG /* A heartbeat message arrived */ @@ -110,6 +117,8 @@ typedef enum { ,XW_ACTION_CHECKDEST /* check that a given hostID has a socket */ + ,XW_ACTION_DISCONNECT + ,XW_ACTION_NOTIFYDISCON ,XW_ACTION_REMOVESOCKET diff --git a/relay/xwrelay.cpp b/relay/xwrelay.cpp index b5b6a0474..c02c6e0cf 100644 --- a/relay/xwrelay.cpp +++ b/relay/xwrelay.cpp @@ -215,6 +215,22 @@ processConnect( unsigned char* bufp, int bufLen, int socket, int recon ) } } /* processConnect */ +static void +processDisconnect( unsigned char* bufp, int bufLen, int socket ) +{ + if ( bufLen == 6 ) { + CookieID cookieID = getNetLong( &bufp ); + HostID hostID = getNetShort( &bufp ); + + SafeCref scr( cookieID ); + if ( scr.IsValid() ) { + scr.Disconnect( socket, hostID ); + } + } else { + logf( "dropping XWRELAY_GAME_DISCONNECT; wrong length" ); + } +} + void killSocket( int socket, char* why ) { @@ -223,6 +239,7 @@ killSocket( int socket, char* why ) /* Might want to kill the thread it belongs to if we're not in it, e.g. when unable to write to another socket. */ logf( "killSocket done" ); + XWThreadPool::GetTPool()->CloseSocket( socket ); } time_t @@ -258,14 +275,17 @@ processMessage( unsigned char* buf, int bufLen, int socket ) { XWRELAY_Cmd cmd = *buf; switch( cmd ) { - case XWRELAY_CONNECT: + case XWRELAY_GAME_CONNECT: logf( "processMessage got XWRELAY_CONNECT" ); processConnect( buf+1, bufLen-1, socket, 0 ); break; - case XWRELAY_RECONNECT: + case XWRELAY_GAME_RECONNECT: logf( "processMessage got XWRELAY_RECONNECT" ); processConnect( buf+1, bufLen-1, socket, 1 ); break; + case XWRELAY_GAME_DISCONNECT: + processDisconnect( buf+1, bufLen-1, socket ); + break; case XWRELAY_CONNECTRESP: logf( "bad: processMessage got XWRELAY_CONNECTRESP" ); break; diff --git a/relay/xwrelay.h b/relay/xwrelay.h index 6a6bf57ef..33371be45 100644 --- a/relay/xwrelay.h +++ b/relay/xwrelay.h @@ -26,18 +26,24 @@ /* Set if device is acting a server; cleared if as client */ #define FLAGS_SERVER_BIT 0x01 -typedef enum { XWRELAY_NONE /* 0 is an illegal value */ +typedef +enum { XWRELAY_NONE /* 0 is an illegal value */ - , XWRELAY_CONNECT + , XWRELAY_GAME_CONNECT /* Sent from device to relay to establish connection to relay. Format: flags: 1; cookieLen: 1; cookie: ; hostID: 2. connectionID: 2. */ - , XWRELAY_RECONNECT + , XWRELAY_GAME_RECONNECT /* Connect using ID rather than cookie. Used by a device that's lost its connection to a game in progress. Once a game is locked this is the only way a host can get (back) in. */ + , XWRELAY_GAME_DISCONNECT + /* Tell the relay that we're gone for this game. After this message is + sent, the host can reconnect on the same socket for a new game. + Format: cookieID: 4; srcID: 2 */ + , XWRELAY_CONNECTRESP /* Sent from relay to device in response to XWRELAY_CONNECT or XWRELAY_RECONNECT. Format: heartbeat_seconds: 2; connectionID: @@ -58,10 +64,10 @@ typedef enum { XWRELAY_NONE /* 0 is an illegal value */ /* The relay says go away. Format: reason code: 1 */ , XWRELAY_HEARTBEAT - /* Sent in either direction. Format: cookieID: 2; srcID: 2 */ + /* Sent in either direction. Format: cookieID: 4; srcID: 2 */ , XWRELAY_MSG_FROMRELAY - /* Sent from relay to device. Format: cookieID: 2; src_hostID: 2; + /* Sent from relay to device. Format: cookieID: 4; src_hostID: 2; dest_hostID: 2; data */ , XWRELAY_MSG_TORELAY