Fix problems reconnecting hosts where some but not all received the

ALLHERE message and connName: change relay protocol so cookie is
included in RECONNECT message, and hostIDs are not assigned until
ALLHERE, and change host-to-game matching to use connName first but
fall back to cookie.  This fixes nearly all cases failing to reconnect
after relay goes down.
This commit is contained in:
ehouse 2009-08-21 12:00:09 +00:00
parent f5f5df990e
commit dbf9daf71b
8 changed files with 313 additions and 208 deletions

View file

@ -87,7 +87,8 @@ typedef enum {
struct CommsCtxt { struct CommsCtxt {
XW_UtilCtxt* util; XW_UtilCtxt* util;
XP_U32 connID; /* 0 means ignore; otherwise must match */ XP_U32 connID; /* set from gameID: 0 means ignore; otherwise
must match. Set by server. */
XP_PlayerAddr nextChannelNo; XP_PlayerAddr nextChannelNo;
AddressRecord* recs; /* return addresses */ AddressRecord* recs; /* return addresses */
@ -136,6 +137,7 @@ struct CommsCtxt {
XP_U16 nPlayersHere; XP_U16 nPlayersHere;
XP_U16 nPlayersTotal; XP_U16 nPlayersTotal;
XP_Bool connecting; XP_Bool connecting;
XP_Bool connNameValid; /* this can probably go. PENDING */
} r; } r;
XP_Bool isServer; XP_Bool isServer;
@ -195,16 +197,47 @@ static XP_S16 send_via_bt_or_ip( CommsCtxt* comms, BTIPMsgType typ,
* implementation * implementation
****************************************************************************/ ****************************************************************************/
#ifdef XWFEATURE_RELAY #ifdef XWFEATURE_RELAY
#ifdef DEBUG
static const char*
CommsRelayState2Str( CommsRelayState state )
{
#define CASE_STR(s) case s: return #s
switch( state ) {
CASE_STR(COMMS_RELAYSTATE_UNCONNECTED);
CASE_STR(COMMS_RELAYSTATE_CONNECT_PENDING);
CASE_STR(COMMS_RELAYSTATE_CONNECTED);
CASE_STR(COMMS_RELAYSTATE_ALLCONNECTED);
default:
assert(0);
}
#undef CASE_STR
return NULL;
}
#endif
static void
set_relay_state( CommsCtxt* comms, CommsRelayState state )
{
if ( comms->r.relayState != state ) {
XP_LOGF( "%s: %s => %s", __func__,
CommsRelayState2Str(comms->r.relayState),
CommsRelayState2Str(state) );
comms->r.relayState = state;
}
}
static void static void
init_relay( CommsCtxt* comms, XP_U16 nPlayersHere, XP_U16 nPlayersTotal ) init_relay( CommsCtxt* comms, XP_U16 nPlayersHere, XP_U16 nPlayersTotal )
{ {
comms->r.myHostID = comms->isServer? HOST_ID_SERVER: HOST_ID_NONE; comms->r.myHostID = comms->isServer? HOST_ID_SERVER: HOST_ID_NONE;
XP_LOGF( "%s: set myHostID to %d", __func__, comms->r.myHostID ); XP_LOGF( "%s: set myHostID to %d", __func__, comms->r.myHostID );
comms->r.relayState = COMMS_RELAYSTATE_UNCONNECTED; set_relay_state( comms, COMMS_RELAYSTATE_UNCONNECTED );
comms->r.nPlayersHere = nPlayersHere; comms->r.nPlayersHere = nPlayersHere;
comms->r.nPlayersTotal = nPlayersTotal; comms->r.nPlayersTotal = nPlayersTotal;
comms->r.cookieID = COOKIE_ID_NONE; comms->r.cookieID = COOKIE_ID_NONE;
comms->r.connName[0] = '\0'; comms->r.connName[0] = '\0';
comms->r.connNameValid = XP_FALSE;
} }
#endif #endif
@ -350,7 +383,7 @@ comms_setConnID( CommsCtxt* comms, XP_U32 connID )
{ {
XP_ASSERT( CONN_ID_NONE != connID ); XP_ASSERT( CONN_ID_NONE != connID );
comms->connID = connID; comms->connID = connID;
XP_STATUSF( "%s: set connID to %lx", __func__, connID ); XP_STATUSF( "%s: set connID (gameID) to %lx", __func__, connID );
} /* comms_setConnID */ } /* comms_setConnID */
static void static void
@ -437,9 +470,12 @@ comms_makeFromStream( MPFORMAL XWStreamCtxt* stream, XW_UtilCtxt* util,
comms->nextChannelNo = stream_getU16( stream ); comms->nextChannelNo = stream_getU16( stream );
if ( addr.conType == COMMS_CONN_RELAY ) { if ( addr.conType == COMMS_CONN_RELAY ) {
comms->r.myHostID = stream_getU8( stream ); comms->r.myHostID = stream_getU8( stream );
comms->r.connNameValid = stream_getU8( stream );
if ( comms->r.connNameValid ) {
stringFromStreamHere( stream, comms->r.connName, stringFromStreamHere( stream, comms->r.connName,
sizeof(comms->r.connName) ); sizeof(comms->r.connName) );
} }
}
#ifdef DEBUG #ifdef DEBUG
comms->nUniqueBytes = stream_getU16( stream ); comms->nUniqueBytes = stream_getU16( stream );
@ -528,7 +564,7 @@ sendConnect( CommsCtxt* comms, XP_Bool breakExisting )
case COMMS_CONN_RELAY: case COMMS_CONN_RELAY:
if ( breakExisting if ( breakExisting
|| COMMS_RELAYSTATE_UNCONNECTED == comms->r.relayState ) { || COMMS_RELAYSTATE_UNCONNECTED == comms->r.relayState ) {
comms->r.relayState = COMMS_RELAYSTATE_UNCONNECTED; set_relay_state( comms, COMMS_RELAYSTATE_UNCONNECTED );
relayConnect( comms ); relayConnect( comms );
} }
break; break;
@ -608,8 +644,11 @@ comms_writeToStream( const CommsCtxt* comms, XWStreamCtxt* stream )
stream_putU16( stream, comms->nextChannelNo ); stream_putU16( stream, comms->nextChannelNo );
if ( comms->addr.conType == COMMS_CONN_RELAY ) { if ( comms->addr.conType == COMMS_CONN_RELAY ) {
stream_putU8( stream, comms->r.myHostID ); stream_putU8( stream, comms->r.myHostID );
stream_putU8( stream, comms->r.connNameValid );
if ( comms->r.connNameValid ) {
stringToStream( stream, comms->r.connName ); stringToStream( stream, comms->r.connName );
} }
}
#ifdef DEBUG #ifdef DEBUG
stream_putU16( stream, comms->nUniqueBytes ); stream_putU16( stream, comms->nUniqueBytes );
@ -1043,17 +1082,20 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
case XWRELAY_CONNECT_RESP: case XWRELAY_CONNECT_RESP:
case XWRELAY_RECONNECT_RESP: case XWRELAY_RECONNECT_RESP:
comms->r.relayState = COMMS_RELAYSTATE_CONNECTED; set_relay_state( comms, COMMS_RELAYSTATE_CONNECTED );
comms->r.heartbeat = stream_getU16( stream ); comms->r.heartbeat = stream_getU16( stream );
comms->r.cookieID = stream_getU16( stream ); comms->r.cookieID = stream_getU16( stream );
comms->r.myHostID = (XWHostID)stream_getU8( stream ); XP_LOGF( "set cookieID = %d", comms->r.cookieID );
XP_LOGF( "set cookieID = %d; set hostid: %x",
comms->r.cookieID, comms->r.myHostID );
setHeartbeatTimer( comms ); setHeartbeatTimer( comms );
break; break;
case XWRELAY_ALLHERE: case XWRELAY_ALLHERE:
comms->r.relayState = COMMS_RELAYSTATE_ALLCONNECTED; set_relay_state( comms, COMMS_RELAYSTATE_ALLCONNECTED );
srcID = (XWHostID)stream_getU8( stream );
XP_ASSERT( comms->r.myHostID == HOST_ID_NONE
|| comms->r.myHostID == srcID );
comms->r.myHostID = srcID;
XP_LOGF( "set hostid: %x", comms->r.myHostID );
hasName = stream_getU8( stream ); hasName = stream_getU8( stream );
if ( hasName ) { if ( hasName ) {
stringFromStreamHere( stream, comms->r.connName, stringFromStreamHere( stream, comms->r.connName,
@ -1081,6 +1123,10 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
XP_LOGF( "%s: rejecting data message", __func__ ); XP_LOGF( "%s: rejecting data message", __func__ );
} else { } else {
*senderID = srcID; *senderID = srcID;
if ( !comms->r.connNameValid ) {
XP_LOGF( "%s: setting connNameValid", __func__ );
comms->r.connNameValid = XP_TRUE;
}
} }
break; break;
@ -1096,7 +1142,7 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID )
case XWRELAY_CONNECTDENIED: /* Close socket for this? */ case XWRELAY_CONNECTDENIED: /* Close socket for this? */
relayErr = stream_getU8( stream ); relayErr = stream_getU8( stream );
util_userError( comms->util, ERR_RELAY_BASE + relayErr ); util_userError( comms->util, ERR_RELAY_BASE + relayErr );
comms->r.relayState = COMMS_RELAYSTATE_UNCONNECTED; set_relay_state( comms, COMMS_RELAYSTATE_UNCONNECTED );
/* fallthru */ /* fallthru */
default: default:
XP_LOGF( "%s: dropping relay msg with cmd %d", __func__, (XP_U16)cmd ); XP_LOGF( "%s: dropping relay msg with cmd %d", __func__, (XP_U16)cmd );
@ -1202,7 +1248,7 @@ getRecordFor( CommsCtxt* comms, const CommsAddrRec* addr,
break; break;
case COMMS_CONN_IR: /* no way to test */ case COMMS_CONN_IR: /* no way to test */
break; break;
case COMMS_CONN_SMS: /* no way to test */ case COMMS_CONN_SMS:
if ( ( 0 == XP_MEMCMP( &addr->u.sms.phone, &rec->addr.u.sms.phone, if ( ( 0 == XP_MEMCMP( &addr->u.sms.phone, &rec->addr.u.sms.phone,
sizeof(addr->u.sms.phone) ) ) sizeof(addr->u.sms.phone) ) )
&& addr->u.sms.port == rec->addr.u.sms.port ) { && addr->u.sms.port == rec->addr.u.sms.port ) {
@ -1326,7 +1372,7 @@ validateChannelMessage( CommsCtxt* comms, const CommsAddrRec* addr,
rec = NULL; rec = NULL;
} }
} else { } else {
XP_LOGF( "%s: no rec for addr", __func__ ); XP_LOGF( "%s: no rec for channelNo %d", __func__, channelNo );
} }
LOG_RETURNF( XP_P, rec ); LOG_RETURNF( XP_P, rec );
@ -1337,7 +1383,7 @@ XP_Bool
comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream, comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
const CommsAddrRec* retAddr ) const CommsAddrRec* retAddr )
{ {
XP_Bool validMessage = XP_FALSE; XP_Bool messageValid = XP_FALSE;
XWHostID senderID = 0; /* unset; default for non-relay cases */ XWHostID senderID = 0; /* unset; default for non-relay cases */
XP_Bool usingRelay = XP_FALSE; XP_Bool usingRelay = XP_FALSE;
AddressRecord* rec = NULL; AddressRecord* rec = NULL;
@ -1357,15 +1403,15 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
XP_U16 payloadSize; XP_U16 payloadSize;
connID = stream_getU32( stream ); connID = stream_getU32( stream );
XP_STATUSF( "%s: read connID of %lx", __func__, connID ); XP_STATUSF( "%s: read connID (gameID) of %lx", __func__, connID );
channelNo = stream_getU16( stream ); channelNo = stream_getU16( stream );
XP_STATUSF( "read channelNo %d", channelNo );
msgID = stream_getU32( stream ); msgID = stream_getU32( stream );
lastMsgRcd = stream_getU32( stream ); lastMsgRcd = stream_getU32( stream );
XP_DEBUGF( "rcd: msgID=" XP_LD ",lastMsgRcd=" XP_LD " on chnl %d", XP_DEBUGF( "rcd on channelNo %d: msgID=%ld,lastMsgRcd=%ld ",
msgID, lastMsgRcd, channelNo ); channelNo, msgID, lastMsgRcd );
payloadSize = stream_getSize( stream ) > 0; /* anything left? */ payloadSize = stream_getSize( stream ) > 0; /* anything left? */
if ( connID == CONN_ID_NONE ) { if ( connID == CONN_ID_NONE ) {
/* special case: initial message from client */ /* special case: initial message from client */
rec = validateInitialMessage( comms, payloadSize > 0, retAddr, rec = validateInitialMessage( comms, payloadSize > 0, retAddr,
@ -1375,13 +1421,13 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
lastMsgRcd ); lastMsgRcd );
} }
validMessage = NULL != rec; messageValid = NULL != rec;
if ( validMessage ) { if ( messageValid ) {
rec->lastMsgRcd = msgID; rec->lastMsgRcd = msgID;
XP_LOGF( "%s: set channel %d's lastMsgRcd to " XP_LD, XP_LOGF( "%s: set channel %d's lastMsgRcd to " XP_LD,
__func__, channelNo, msgID ); __func__, channelNo, msgID );
stream_setAddress( stream, channelNo ); stream_setAddress( stream, channelNo );
validMessage = payloadSize > 0; messageValid = payloadSize > 0;
} }
} else { } else {
XP_LOGF( "%s: message too small", __func__ ); XP_LOGF( "%s: message too small", __func__ );
@ -1391,8 +1437,8 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
/* Call after we've had a chance to create rec for addr */ /* Call after we've had a chance to create rec for addr */
noteHBReceived( comms/* , addr */ ); noteHBReceived( comms/* , addr */ );
LOG_RETURNF( "%d", (XP_U16)validMessage ); LOG_RETURNF( "%d", (XP_U16)messageValid );
return validMessage; return messageValid;
} /* comms_checkIncomingStream */ } /* comms_checkIncomingStream */
#ifdef COMMS_HEARTBEAT #ifdef COMMS_HEARTBEAT
@ -1680,17 +1726,18 @@ send_via_relay( CommsCtxt* comms, XWRELAY_Cmd cmd, XWHostID destID,
stream_putU8( tmpStream, comms->r.nPlayersHere ); stream_putU8( tmpStream, comms->r.nPlayersHere );
stream_putU8( tmpStream, comms->r.nPlayersTotal ); stream_putU8( tmpStream, comms->r.nPlayersTotal );
comms->r.relayState = COMMS_RELAYSTATE_CONNECT_PENDING; set_relay_state( comms,COMMS_RELAYSTATE_CONNECT_PENDING );
break; break;
case XWRELAY_GAME_RECONNECT: case XWRELAY_GAME_RECONNECT:
stream_putU8( tmpStream, XWRELAY_PROTO_VERSION ); stream_putU8( tmpStream, XWRELAY_PROTO_VERSION );
stringToStream( tmpStream, addr.u.ip_relay.cookie );
stream_putU8( tmpStream, comms->r.myHostID ); stream_putU8( tmpStream, comms->r.myHostID );
stream_putU8( tmpStream, comms->r.nPlayersHere ); stream_putU8( tmpStream, comms->r.nPlayersHere );
stream_putU8( tmpStream, comms->r.nPlayersTotal ); stream_putU8( tmpStream, comms->r.nPlayersTotal );
stringToStream( tmpStream, comms->r.connName ); stringToStream( tmpStream, comms->r.connName );
comms->r.relayState = COMMS_RELAYSTATE_CONNECT_PENDING; set_relay_state( comms, COMMS_RELAYSTATE_CONNECT_PENDING );
break; break;
case XWRELAY_GAME_DISCONNECT: case XWRELAY_GAME_DISCONNECT:
@ -1741,9 +1788,8 @@ relayConnect( CommsCtxt* comms )
LOG_FUNC(); LOG_FUNC();
if ( comms->addr.conType == COMMS_CONN_RELAY && !comms->r.connecting ) { if ( comms->addr.conType == COMMS_CONN_RELAY && !comms->r.connecting ) {
comms->r.connecting = XP_TRUE; comms->r.connecting = XP_TRUE;
success = send_via_relay( comms, success = send_via_relay( comms, comms->r.connNameValid?
comms->r.connName[0] == '\0' ? XWRELAY_GAME_RECONNECT : XWRELAY_GAME_CONNECT,
XWRELAY_GAME_CONNECT:XWRELAY_GAME_RECONNECT,
comms->r.myHostID, NULL, 0 ); comms->r.myHostID, NULL, 0 );
comms->r.connecting = XP_FALSE; comms->r.connecting = XP_FALSE;
} }
@ -1788,7 +1834,7 @@ relayDisconnect( CommsCtxt* comms )
LOG_FUNC(); LOG_FUNC();
if ( comms->addr.conType == COMMS_CONN_RELAY ) { if ( comms->addr.conType == COMMS_CONN_RELAY ) {
if ( comms->r.relayState != COMMS_RELAYSTATE_UNCONNECTED ) { if ( comms->r.relayState != COMMS_RELAYSTATE_UNCONNECTED ) {
comms->r.relayState = COMMS_RELAYSTATE_UNCONNECTED; set_relay_state( comms, COMMS_RELAYSTATE_UNCONNECTED );
send_via_relay( comms, XWRELAY_GAME_DISCONNECT, HOST_ID_NONE, send_via_relay( comms, XWRELAY_GAME_DISCONNECT, HOST_ID_NONE,
NULL, 0 ); NULL, 0 );
} }

View file

@ -39,6 +39,7 @@
#include "timermgr.h" #include "timermgr.h"
#include "configs.h" #include "configs.h"
#include "crefmgr.h" #include "crefmgr.h"
#include "permid.h"
using namespace std; using namespace std;
@ -82,7 +83,7 @@ void
CookieRef::ReInit( const char* cookie, const char* connName, CookieID id ) CookieRef::ReInit( const char* cookie, const char* connName, CookieID id )
{ {
m_cookie = cookie==NULL?"":cookie; m_cookie = cookie==NULL?"":cookie;
m_connName = connName; m_connName = connName==NULL?"":connName;
m_cookieID = id; m_cookieID = id;
m_totalSent = 0; m_totalSent = 0;
m_curState = XWS_INITED; m_curState = XWS_INITED;
@ -113,14 +114,9 @@ CookieRef::~CookieRef()
XWThreadPool* tPool = XWThreadPool::GetTPool(); XWThreadPool* tPool = XWThreadPool::GetTPool();
ASSERT_LOCKED(); ASSERT_LOCKED();
for ( ; ; ) { vector<HostRec>::iterator iter;
map<HostID,HostRec>::iterator iter = m_sockets.begin(); for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
int socket = iter->m_socket;
if ( iter == m_sockets.end() ) {
break;
}
int socket = iter->second.m_socket;
tPool->CloseSocket( socket ); tPool->CloseSocket( socket );
m_sockets.erase( iter ); m_sockets.erase( iter );
} }
@ -175,12 +171,6 @@ void
CookieRef::_Connect( int socket, HostID hid, int nPlayersH, int nPlayersT ) CookieRef::_Connect( int socket, HostID hid, int nPlayersH, int nPlayersT )
{ {
if ( CRefMgr::Get()->Associate( socket, this ) ) { if ( CRefMgr::Get()->Associate( socket, this ) ) {
if ( hid == HOST_ID_NONE ) {
hid = nextHostID();
logf( XW_LOGINFO, "assigned host id: %x", hid );
} else {
logf( XW_LOGINFO, "NOT assigned host id; why?" );
}
pushConnectEvent( socket, hid, nPlayersH, nPlayersT ); pushConnectEvent( socket, hid, nPlayersH, nPlayersT );
handleEvents(); handleEvents();
} else { } else {
@ -192,7 +182,6 @@ void
CookieRef::_Reconnect( int socket, HostID hid, int nPlayersH, int nPlayersT ) CookieRef::_Reconnect( int socket, HostID hid, int nPlayersH, int nPlayersT )
{ {
(void)CRefMgr::Get()->Associate( socket, this ); (void)CRefMgr::Get()->Associate( socket, this );
/* MutexLock ml( &m_EventsMutex ); */
pushReconnectEvent( socket, hid, nPlayersH, nPlayersT ); pushReconnectEvent( socket, hid, nPlayersH, nPlayersT );
handleEvents(); handleEvents();
} }
@ -225,14 +214,16 @@ CookieRef::_Shutdown()
int int
CookieRef::SocketForHost( HostID dest ) CookieRef::SocketForHost( HostID dest )
{ {
int socket; int socket = -1;
ASSERT_LOCKED(); ASSERT_LOCKED();
map<HostID,HostRec>::iterator iter = m_sockets.find( dest ); vector<HostRec>::const_iterator iter;
if ( iter == m_sockets.end() ) { for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
socket = -1; if ( iter->m_hostID == dest ) {
} else { socket = iter->m_socket;
socket = iter->second.m_socket; break;
} }
}
logf( XW_LOGVERBOSE0, "returning socket=%d for hostid=%x", socket, dest ); logf( XW_LOGVERBOSE0, "returning socket=%d for hostid=%x", socket, dest );
return socket; return socket;
} }
@ -250,7 +241,8 @@ CookieRef::NeverFullyConnected()
} }
bool bool
CookieRef::AcceptingReconnections( HostID hid, int nPlayersH, int nPlayersT ) CookieRef::AcceptingReconnections( HostID hid, const char* cookie,
int nPlayersH )
{ {
bool accept = false; bool accept = false;
/* First, do we have room. Second, are we missing this guy? */ /* First, do we have room. Second, are we missing this guy? */
@ -274,6 +266,13 @@ CookieRef::AcceptingReconnections( HostID hid, int nPlayersH, int nPlayersT )
} }
} }
/* Error to connect if cookie doesn't match. */
if ( accept && !!cookie && 0 != strcmp( cookie, Cookie() ) ) {
logf( XW_LOGERROR, "%s: not accepting b/c cookie mismatch: %s vs %s",
__func__, cookie, Cookie() );
accept = false;
}
return accept; return accept;
} /* AcceptingReconnections */ } /* AcceptingReconnections */
@ -295,19 +294,18 @@ CookieRef::removeSocket( int socket )
logf( XW_LOGINFO, "%s(socket=%d)", __func__, socket ); logf( XW_LOGINFO, "%s(socket=%d)", __func__, socket );
int count; int count;
{ {
/* RWWriteLock rwl( &m_sockets_rwlock ); */
ASSERT_LOCKED(); ASSERT_LOCKED();
count = m_sockets.size(); count = m_sockets.size();
assert( count > 0 ); assert( count > 0 );
map<HostID,HostRec>::iterator iter = m_sockets.begin();
while ( iter != m_sockets.end() ) { vector<HostRec>::iterator iter;
if ( iter->second.m_socket == socket ) { for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
if ( iter->m_socket == socket ) {
m_sockets.erase(iter); m_sockets.erase(iter);
--count; --count;
break; break;
} }
++iter;
} }
} }
@ -333,13 +331,12 @@ CookieRef::HasSocket_locked( int socket )
bool found = false; bool found = false;
ASSERT_LOCKED(); ASSERT_LOCKED();
map<HostID,HostRec>::iterator iter = m_sockets.begin(); vector<HostRec>::iterator iter;
while ( iter != m_sockets.end() ) { for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
if ( iter->second.m_socket == socket ) { if ( iter->m_socket == socket ) {
found = true; found = true;
break; break;
} }
++iter;
} }
return found; return found;
@ -349,7 +346,6 @@ CookieRef::HasSocket_locked( int socket )
void void
CookieRef::_HandleHeartbeat( HostID id, int socket ) CookieRef::_HandleHeartbeat( HostID id, int socket )
{ {
/* MutexLock ml( &m_EventsMutex ); */
pushHeartbeatEvent( id, socket ); pushHeartbeatEvent( id, socket );
handleEvents(); handleEvents();
} /* HandleHeartbeat */ } /* HandleHeartbeat */
@ -357,15 +353,14 @@ CookieRef::_HandleHeartbeat( HostID id, int socket )
void void
CookieRef::_CheckHeartbeats( time_t now ) CookieRef::_CheckHeartbeats( time_t now )
{ {
logf( XW_LOGINFO, "CookieRef::_CheckHeartbeats" ); logf( XW_LOGINFO, "%s", __func__ );
ASSERT_LOCKED(); ASSERT_LOCKED();
map<HostID,HostRec>::iterator iter = m_sockets.begin(); vector<HostRec>::iterator iter;
while ( iter != m_sockets.end() ) { for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
time_t last = iter->second.m_lastHeartbeat; time_t last = iter->m_lastHeartbeat;
if ( (now - last) > GetHeartbeat() ) { if ( (now - last) > GetHeartbeat() ) {
pushHeartFailedEvent( iter->second.m_socket ); pushHeartFailedEvent( iter->m_socket );
} }
++iter;
} }
handleEvents(); handleEvents();
@ -426,6 +421,7 @@ CookieRef::pushHeartbeatEvent( HostID id, int socket )
void void
CookieRef::pushHeartFailedEvent( int socket ) CookieRef::pushHeartFailedEvent( int socket )
{ {
logf( XW_LOGINFO, "%s", __func__ );
CRefEvent evt; CRefEvent evt;
evt.type = XWE_HEARTFAILED; evt.type = XWE_HEARTFAILED;
evt.u.heart.socket = socket; evt.u.heart.socket = socket;
@ -555,6 +551,8 @@ CookieRef::handleEvents()
case XWA_SENDALLHERE: case XWA_SENDALLHERE:
case XWA_SNDALLHERE_2: case XWA_SNDALLHERE_2:
cancelAllConnectedTimer(); cancelAllConnectedTimer();
assignConnName();
assignHostIds();
sendAllHere( takeAction == XWA_SENDALLHERE ); sendAllHere( takeAction == XWA_SENDALLHERE );
break; break;
@ -607,7 +605,7 @@ CookieRef::increasePlayerCounts( const CRefEvent* evt )
int nPlayersT = evt->u.con.nPlayersT; int nPlayersT = evt->u.con.nPlayersT;
HostID hid = evt->u.con.srcID; HostID hid = evt->u.con.srcID;
logf( XW_LOGVERBOSE1, "increasePlayerCounts: hid=%d, nPlayersH=%d, " logf( XW_LOGINFO, "increasePlayerCounts: hid=%d, nPlayersH=%d, "
"nPlayersT=%d", hid, nPlayersH, nPlayersT ); "nPlayersT=%d", hid, nPlayersH, nPlayersT );
if ( hid == HOST_ID_SERVER ) { if ( hid == HOST_ID_SERVER ) {
@ -633,25 +631,21 @@ CookieRef::reducePlayerCounts( int socket )
{ {
logf( XW_LOGVERBOSE1, "reducePlayerCounts on socket %d", socket ); logf( XW_LOGVERBOSE1, "reducePlayerCounts on socket %d", socket );
ASSERT_LOCKED(); ASSERT_LOCKED();
map<HostID,HostRec>::iterator iter = m_sockets.begin(); vector<HostRec>::iterator iter;
while ( iter != m_sockets.end() ) { for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
if ( iter->m_socket == socket ) {
if ( iter->second.m_socket == socket ) { if ( iter->m_hostID == HOST_ID_SERVER ) {
assert( iter->first != 0 );
if ( iter->first == HOST_ID_SERVER ) {
m_nPlayersSought = 0; m_nPlayersSought = 0;
} else { } else {
assert( iter->second.m_nPlayersT == 0 ); assert( iter->m_nPlayersT == 0 );
} }
m_nPlayersHere -= iter->second.m_nPlayersH; m_nPlayersHere -= iter->m_nPlayersH;
logf( XW_LOGVERBOSE1, "reducePlayerCounts: m_nPlayersHere=%d; m_nPlayersSought=%d", logf( XW_LOGVERBOSE1, "reducePlayerCounts: m_nPlayersHere=%d; m_nPlayersSought=%d",
m_nPlayersHere, m_nPlayersSought ); m_nPlayersHere, m_nPlayersSought );
break; break;
} }
++iter;
} }
} /* reducePlayerCounts */ } /* reducePlayerCounts */
@ -710,11 +704,12 @@ CookieRef::sendResponse( const CRefEvent* evt, bool initial )
int nPlayersH = evt->u.con.nPlayersH; int nPlayersH = evt->u.con.nPlayersH;
int nPlayersT = evt->u.con.nPlayersT; int nPlayersT = evt->u.con.nPlayersT;
assert( id != HOST_ID_NONE );
logf( XW_LOGINFO, "remembering pair: hostid=%x, socket=%d", id, socket );
HostRec hr(socket, nPlayersH, nPlayersT);
ASSERT_LOCKED(); ASSERT_LOCKED();
m_sockets.insert( pair<HostID,HostRec>(id,hr) );
logf( XW_LOGINFO, "%s: remembering pair: hostid=%x, socket=%d (size=%d)",
__func__, id, socket, m_sockets.size());
HostRec hr(id, socket, nPlayersH, nPlayersT);
m_sockets.push_back( hr );
/* Now send the response */ /* Now send the response */
unsigned char buf[1 + /* cmd */ unsigned char buf[1 + /* cmd */
@ -728,11 +723,9 @@ CookieRef::sendResponse( const CRefEvent* evt, bool initial )
*bufp++ = initial ? XWRELAY_CONNECT_RESP : XWRELAY_RECONNECT_RESP; *bufp++ = initial ? XWRELAY_CONNECT_RESP : XWRELAY_RECONNECT_RESP;
putNetShort( &bufp, GetHeartbeat() ); putNetShort( &bufp, GetHeartbeat() );
putNetShort( &bufp, GetCookieID() ); putNetShort( &bufp, GetCookieID() );
logf( XW_LOGVERBOSE0, "writing hostID of %d into msg", id );
*bufp++ = (char)id;
send_with_length( socket, buf, bufp - buf, true ); send_with_length( socket, buf, bufp - buf, true );
logf( XW_LOGVERBOSE0, "sent XWRELAY_CONNECTRESP" ); logf( XW_LOGVERBOSE0, "sent %s", cmdToStr( XWRELAY_Cmd(buf[0]) ) );
} /* sendResponse */ } /* sendResponse */
void void
@ -789,15 +782,13 @@ CookieRef::notifyOthers( int socket, XWRelayMsg msg, XWREASON why )
{ {
assert( socket != 0 ); assert( socket != 0 );
/* RWReadLock ml( &m_sockets_rwlock ); */
ASSERT_LOCKED(); ASSERT_LOCKED();
map<HostID,HostRec>::iterator iter = m_sockets.begin(); vector<HostRec>::iterator iter;
while ( iter != m_sockets.end() ) { for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
int other = iter->second.m_socket; int other = iter->m_socket;
if ( other != socket ) { if ( other != socket ) {
send_msg( other, iter->first, msg, why, false ); send_msg( other, iter->m_hostID, msg, why, false );
} }
++iter;
} }
} /* notifyOthers */ } /* notifyOthers */
@ -806,38 +797,82 @@ CookieRef::sendAllHere( bool includeName )
{ {
unsigned char buf[1 + 1 + MAX_CONNNAME_LEN]; unsigned char buf[1 + 1 + MAX_CONNNAME_LEN];
unsigned char* bufp = buf; unsigned char* bufp = buf;
unsigned char* idLoc;
*bufp++ = XWRELAY_ALLHERE; *bufp++ = XWRELAY_ALLHERE;
idLoc = bufp++; /* space for hostId, remembering address */
*bufp++ = includeName? 1 : 0; *bufp++ = includeName? 1 : 0;
if ( includeName ) { if ( includeName ) {
const char* connName = ConnName(); const char* connName = ConnName();
assert( !!connName && connName[0] );
int len = strlen( connName ); int len = strlen( connName );
assert( len < MAX_CONNNAME_LEN ); assert( len < MAX_CONNNAME_LEN );
*bufp++ = (char)len; *bufp++ = (char)len;
memcpy( bufp, connName, len ); memcpy( bufp, connName, len );
bufp += len; bufp += len;
} }
ASSERT_LOCKED(); ASSERT_LOCKED();
map<HostID,HostRec>::iterator iter = m_sockets.begin(); vector<HostRec>::iterator iter = m_sockets.begin();
while ( iter != m_sockets.end() ) { while ( iter != m_sockets.end() ) {
send_with_length( iter->second.m_socket, buf, bufp-buf, logf( XW_LOGINFO, "%s: sending to hostid %d", __func__,
iter->m_hostID );
*idLoc = iter->m_hostID; /* write in this target's hostId */
send_with_length( iter->m_socket, buf, bufp-buf,
true ); true );
++iter; ++iter;
} }
} /* sendAllHere */ } /* sendAllHere */
void
CookieRef::assignConnName( void )
{
if ( !ConnName()[0] ) {
m_connName = PermID::GetNextUniqueID();
logf( XW_LOGINFO, "%s: assigning name: %s", __func__, ConnName() );
assert( GetCookieID() != 0 );
} else {
logf( XW_LOGINFO, "%s: has name: %s", __func__, ConnName() );
}
} /* assignConnName */
void
CookieRef::assignHostIds( void )
{
ASSERT_LOCKED();
HostID nextId = HOST_ID_SERVER;
unsigned int bits = 0;
vector<HostRec>::iterator iter;
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
if ( iter->m_hostID != HOST_ID_NONE ) {
bits |= 1 << iter->m_hostID;
}
}
assert( (bits & (1 << HOST_ID_SERVER)) != 0 );
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
if ( iter->m_hostID == HOST_ID_NONE ) {
while ( ((1 << nextId) & bits) != 0 ) {
++nextId;
}
iter->m_hostID = nextId++; /* ++: don't reuse */
}
}
}
void void
CookieRef::disconnectSockets( int socket, XWREASON why ) CookieRef::disconnectSockets( int socket, XWREASON why )
{ {
if ( socket == 0 ) { if ( socket == 0 ) {
/* RWReadLock ml( &m_sockets_rwlock ); */
ASSERT_LOCKED(); ASSERT_LOCKED();
map<HostID,HostRec>::iterator iter = m_sockets.begin(); vector<HostRec>::iterator iter;
while ( iter != m_sockets.end() ) { for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
assert( iter->second.m_socket != 0 ); assert( iter->m_socket != 0 );
disconnectSockets( iter->second.m_socket, why ); disconnectSockets( iter->m_socket, why );
++iter;
} }
} else { } else {
pushNotifyDisconEvent( socket, why ); pushNotifyDisconEvent( socket, why );
@ -851,21 +886,30 @@ CookieRef::noteHeartbeat( const CRefEvent* evt )
int socket = evt->u.heart.socket; int socket = evt->u.heart.socket;
HostID id = evt->u.heart.id; HostID id = evt->u.heart.id;
/* RWWriteLock rwl( &m_sockets_rwlock ); */
ASSERT_LOCKED(); ASSERT_LOCKED();
map<HostID,HostRec>::iterator iter = m_sockets.find(id); vector<HostRec>::iterator iter;
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
if ( iter->m_hostID == id ) {
break;
}
}
if ( iter == m_sockets.end() ) { if ( iter == m_sockets.end() ) {
logf( XW_LOGERROR, "no socket for HostID %x", id ); logf( XW_LOGERROR, "no socket for HostID %x", id );
} else { } else {
/* PENDING If the message came on an unexpected socket, kill the int second_socket = iter->m_socket;
connection. An attack is the most likely explanation. */ if ( second_socket == socket ) {
assert( iter->second.m_socket == socket );
/* if see this again recover from it */
logf( XW_LOGVERBOSE1, "upping m_lastHeartbeat from %d to %d", logf( XW_LOGVERBOSE1, "upping m_lastHeartbeat from %d to %d",
iter->second.m_lastHeartbeat, uptime() ); iter->m_lastHeartbeat, uptime() );
iter->second.m_lastHeartbeat = uptime(); iter->m_lastHeartbeat = uptime();
} else {
/* PENDING If the message came on an unexpected socket, kill the
connection. An attack is the most likely explanation. But:
now it's happening after a crash and clients reconnect. */
logf( XW_LOGERROR, "wrong socket record for HostID %x; wanted %d, found %d",
id, socket, second_socket );
}
} }
} /* noteHeartbeat */ } /* noteHeartbeat */
@ -882,7 +926,7 @@ CookieRef::s_checkAllConnected( void* closure )
void void
CookieRef::_CheckAllConnected() CookieRef::_CheckAllConnected()
{ {
logf( XW_LOGVERBOSE0, "checkAllConnected" ); logf( XW_LOGVERBOSE0, "%s", __func__ );
/* MutexLock ml( &m_EventsMutex ); */ /* MutexLock ml( &m_EventsMutex ); */
CRefEvent newEvt; CRefEvent newEvt;
newEvt.type = XWE_CONNTIMER; newEvt.type = XWE_CONNTIMER;
@ -943,13 +987,12 @@ CookieRef::_PrintCookieInfo( string& out )
snprintf( buf, sizeof(buf), "Hosts connected=%d; cur time = %ld\n", snprintf( buf, sizeof(buf), "Hosts connected=%d; cur time = %ld\n",
m_sockets.size(), uptime() ); m_sockets.size(), uptime() );
out += buf; out += buf;
map<HostID,HostRec>::iterator iter = m_sockets.begin(); vector<HostRec>::iterator iter;
while ( iter != m_sockets.end() ) { for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
snprintf( buf, sizeof(buf), " HostID=%d; socket=%d;last hbeat=%ld\n", snprintf( buf, sizeof(buf), " HostID=%d; socket=%d;last hbeat=%ld\n",
iter->first, iter->second.m_socket, iter->m_hostID, iter->m_socket,
iter->second.m_lastHeartbeat ); iter->m_lastHeartbeat );
out += buf; out += buf;
++iter;
} }
} /* PrintCookieInfo */ } /* PrintCookieInfo */
@ -957,20 +1000,18 @@ CookieRef::_PrintCookieInfo( string& out )
void void
CookieRef::_FormatHostInfo( string* hostIds, string* addrs ) CookieRef::_FormatHostInfo( string* hostIds, string* addrs )
{ {
logf( XW_LOGINFO, "%s", __func__ );
ASSERT_LOCKED(); ASSERT_LOCKED();
map<HostID,HostRec>::iterator iter; vector<HostRec>::iterator iter;
for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) { for ( iter = m_sockets.begin(); iter != m_sockets.end(); ++iter ) {
if ( !!hostIds ) { if ( !!hostIds ) {
char buf[8]; char buf[8];
snprintf( buf, sizeof(buf), "%d ", iter->first ); snprintf( buf, sizeof(buf), "%d ", iter->m_hostID );
*hostIds += buf; *hostIds += buf;
} }
if ( !!addrs ) { if ( !!addrs ) {
int s = iter->second.m_socket; int s = iter->m_socket;
struct sockaddr_in name; struct sockaddr_in name;
socklen_t siz = sizeof(name); socklen_t siz = sizeof(name);
if ( 0 == getpeername( s, (struct sockaddr*)&name, &siz) ) { if ( 0 == getpeername( s, (struct sockaddr*)&name, &siz) ) {

View file

@ -35,15 +35,16 @@ using namespace std;
class CookieMapIterator; /* forward */ class CookieMapIterator; /* forward */
class HostRec { struct HostRec {
public: public:
HostRec(int socket, int nPlayersH, int nPlayersT) HostRec(HostID hostID, int socket, int nPlayersH, int nPlayersT)
: m_socket(socket) : m_hostID(hostID)
, m_socket(socket)
, m_nPlayersH(nPlayersH) , m_nPlayersH(nPlayersH)
, m_nPlayersT(nPlayersT) , m_nPlayersT(nPlayersT)
, m_lastHeartbeat(uptime()) , m_lastHeartbeat(uptime())
{} {}
~HostRec() {} HostID m_hostID;
int m_socket; int m_socket;
int m_nPlayersH; int m_nPlayersH;
int m_nPlayersT; int m_nPlayersT;
@ -87,7 +88,8 @@ class CookieRef {
int SocketForHost( HostID dest ); int SocketForHost( HostID dest );
bool NeverFullyConnected(); bool NeverFullyConnected();
bool AcceptingReconnections( HostID hid, int nPlayersH, int nPlayersT ); bool AcceptingReconnections( HostID hid, const char* cookie,
int nPlayersH );
/* for console */ /* for console */
void _PrintCookieInfo( string& out ); void _PrintCookieInfo( string& out );
@ -193,6 +195,8 @@ class CookieRef {
void notifyDisconn(const CRefEvent* evt); void notifyDisconn(const CRefEvent* evt);
void removeSocket( int socket ); void removeSocket( int socket );
void sendAllHere( bool includeName ); void sendAllHere( bool includeName );
void assignConnName( void );
void assignHostIds( void );
HostID nextHostID() { return ++m_nextHostID; } HostID nextHostID() { return ++m_nextHostID; }
@ -203,8 +207,7 @@ class CookieRef {
/* timer callback */ /* timer callback */
static void s_checkAllConnected( void* closure ); static void s_checkAllConnected( void* closure );
map<HostID,HostRec> m_sockets; vector<HostRec> m_sockets;
/* pthread_rwlock_t m_sockets_rwlock; */
int m_heatbeat; /* might change per carrier or something. */ int m_heatbeat; /* might change per carrier or something. */
string m_cookie; /* cookie used for initial connections */ string m_cookie; /* cookie used for initial connections */

View file

@ -28,7 +28,6 @@
#include "crefmgr.h" #include "crefmgr.h"
#include "cref.h" #include "cref.h"
#include "mlock.h" #include "mlock.h"
#include "permid.h"
#include "configs.h" #include "configs.h"
#include "timermgr.h" #include "timermgr.h"
@ -61,6 +60,7 @@ CRefMgr::CRefMgr()
{ {
/* should be using pthread_once() here */ /* should be using pthread_once() here */
pthread_mutex_init( &m_SocketStuffMutex, NULL ); pthread_mutex_init( &m_SocketStuffMutex, NULL );
pthread_mutex_init( &m_nextCIDMutex, NULL );
pthread_mutex_init( &m_freeList_mutex, NULL ); pthread_mutex_init( &m_freeList_mutex, NULL );
pthread_rwlock_init( &m_cookieMapRWLock, NULL ); pthread_rwlock_init( &m_cookieMapRWLock, NULL );
} }
@ -103,44 +103,57 @@ CRefMgr::CloseAll()
} }
} /* CloseAll */ } /* CloseAll */
/* Matching hosts to games. If they have a connName, it's easy. That's the
* only thing we'll match on, and it must match, and the game must have room.
* If they have a cookie as well, it must match. If only a cookie is
* provided, we may be dealing with a new game *or* a reconnect from somebody
* who didn't get his connName yet (even if other participants did.)
*/
CookieRef* CookieRef*
CRefMgr::FindOpenGameFor( const char* cORn, bool isCookie, CRefMgr::FindOpenGameFor( const char* cookie, const char* connName,
HostID hid, int socket, int nPlayersH, int nPlayersT ) HostID hid, int socket, int nPlayersH, int nPlayersT )
{ {
logf( XW_LOGINFO, "%s(cORn=%s,hid=%d,socket=%d)", __func__, cORn, hid, logf( XW_LOGINFO, "%s(cookie=%s,connName=%s,hid=%d,socket=%d)", __func__,
socket ); cookie, connName, hid, socket );
CookieRef* cref = NULL; CookieRef* found = NULL;
if ( !!cookie || !!connName ) { /* drop if both are null */
RWReadLock rwl( &m_cookieMapRWLock ); RWReadLock rwl( &m_cookieMapRWLock );
CookieMap::iterator iter = m_cookieMap.begin(); CookieMap::iterator iter;
while ( iter != m_cookieMap.end() ) { for ( iter = m_cookieMap.begin();
cref = iter->second; NULL == found && iter != m_cookieMap.end();
if ( isCookie ) { ++iter ) {
if ( 0 == strcmp( cref->Cookie(), cORn ) ) { CookieRef* cref = iter->second;
if ( cref->NeverFullyConnected() ) {
break; if ( !!connName ) {
} else if ( cref->HasSocket(socket) ) { if ( 0 == strcmp( cref->ConnName(), connName ) ) {
logf( XW_LOGINFO, "%s: HasSocket case", __func__ );
break;
}
}
} else {
if ( 0 == strcmp( cref->ConnName(), cORn ) ) {
bool found = false;
if ( cref->Lock() ) { if ( cref->Lock() ) {
found = cref->AcceptingReconnections( hid, nPlayersH, assert( !cookie || 0 == strcmp( cookie, cref->Cookie() ) );
nPlayersH ); if ( cref->AcceptingReconnections( hid, cookie,
nPlayersH ) ) {
found = cref;
}
cref->Unlock(); cref->Unlock();
} }
if ( found ) { }
break; } else if ( !!cookie ) {
if ( 0 == strcmp( cref->Cookie(), cookie ) ) {
if ( cref->NeverFullyConnected() ) {
found = cref;
} else if ( cref->HasSocket(socket) ) {
logf( XW_LOGINFO, "%s: HasSocket case", __func__ );
found = cref;
}
} }
} }
} }
++iter;
} }
return (iter == m_cookieMap.end()) ? NULL : cref; logf( XW_LOGINFO, "%s=>%p", __func__, found );
return found;
} /* FindOpenGameFor */ } /* FindOpenGameFor */
CookieID CookieID
@ -148,12 +161,14 @@ CRefMgr::nextCID( const char* connName )
{ {
/* Later may want to guarantee that wrap-around doesn't cause an overlap. /* Later may want to guarantee that wrap-around doesn't cause an overlap.
But that's really only a theoretical possibility. */ But that's really only a theoretical possibility. */
MutexLock ml(&m_nextCIDMutex);
return ++m_nextCID; return ++m_nextCID;
} /* nextCID */ } /* nextCID */
int int
CRefMgr::GetNumGamesSeen( void ) CRefMgr::GetNumGamesSeen( void )
{ {
MutexLock ml(&m_nextCIDMutex);
return m_nextCID; return m_nextCID;
} }
@ -207,36 +222,23 @@ CRefMgr::getFromFreeList( void )
CookieRef* CookieRef*
CRefMgr::getMakeCookieRef_locked( const char* cORn, bool isCookie, HostID hid, CRefMgr::getMakeCookieRef_locked( const char* cookie, const char* connName,
int socket, int nPlayersH, int nPlayersT ) HostID hid, int socket, int nPlayersH,
int nPlayersT )
{ {
CookieRef* cref; CookieRef* cref;
/* We have a cookie from a new connection. This may be the first time /* We have a cookie from a new connection or from a reconnect. This may
it's been seen, or there may be a game currently in the be the first time it's been seen, or there may be a game currently in
XW_ST_CONNECTING state, or it may be a dupe of a connect packet. So we the XW_ST_CONNECTING state, or it may be a dupe of a connect packet.
need to look up the cookie first, then generate new connName and If there's a game, cool. Otherwise add a new one. Pass the connName
cookieIDs if it's not found. */ which will be used if set, but if not set we'll be generating another
later when the game is complete.
*/
cref = FindOpenGameFor( cORn, isCookie, hid, socket, nPlayersH, nPlayersT ); cref = FindOpenGameFor( cookie, connName, hid, socket, nPlayersH, nPlayersT );
if ( cref == NULL ) { if ( cref == NULL ) {
string s; cref = AddNew( cookie, connName, nextCID( NULL ) );
const char* connName;
const char* cookie = NULL;
if ( isCookie ) {
cookie = cORn;
s = PermID::GetNextUniqueID();
connName = s.c_str();
} else {
connName = cORn;
}
CookieID cid = cookieIDForConnName( connName );
if ( cid == 0 ) {
cid = nextCID( connName );
}
cref = AddNew( cookie, connName, cid );
} }
return cref; return cref;
@ -369,8 +371,6 @@ CRefMgr::AddNew( const char* cookie, const char* connName, CookieID id )
{ {
logf( XW_LOGINFO, "%s( cookie=%s, connName=%s, id=%d", __func__, logf( XW_LOGINFO, "%s( cookie=%s, connName=%s, id=%d", __func__,
cookie, connName, id ); cookie, connName, id );
CookieRef* exists = getCookieRef_impl( id );
assert( exists == NULL ); /* failed once */
CookieRef* ref = getFromFreeList(); CookieRef* ref = getFromFreeList();
@ -386,7 +386,7 @@ CRefMgr::AddNew( const char* cookie, const char* connName, CookieID id )
} }
m_cookieMap.insert( pair<CookieID, CookieRef*>(ref->GetCookieID(), ref ) ); m_cookieMap.insert( pair<CookieID, CookieRef*>(ref->GetCookieID(), ref ) );
logf( XW_LOGINFO, "paired cookie %s/connName %s with id %d", logf( XW_LOGINFO, "%s: paired cookie %s/connName %s with id %d", __func__,
(cookie?cookie:"NULL"), connName, ref->GetCookieID() ); (cookie?cookie:"NULL"), connName, ref->GetCookieID() );
#ifdef RELAY_HEARTBEAT #ifdef RELAY_HEARTBEAT
@ -529,15 +529,15 @@ CookieMapIterator::Next()
// SafeCref // SafeCref
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
SafeCref::SafeCref( const char* cORn, bool isCookie, HostID hid, int socket, SafeCref::SafeCref( const char* cookie, const char* connName, HostID hid,
int nPlayersH, int nPlayersT ) int socket, int nPlayersH, int nPlayersT )
: m_cref( NULL ) : m_cref( NULL )
, m_mgr( CRefMgr::Get() ) , m_mgr( CRefMgr::Get() )
, m_isValid( false ) , m_isValid( false )
{ {
CookieRef* cref; CookieRef* cref;
cref = m_mgr->getMakeCookieRef_locked( cORn, isCookie, hid, socket, cref = m_mgr->getMakeCookieRef_locked( cookie, connName, hid, socket,
nPlayersH, nPlayersT ); nPlayersH, nPlayersT );
if ( cref != NULL ) { if ( cref != NULL ) {
m_locked = cref->Lock(); m_locked = cref->Lock();

View file

@ -100,7 +100,7 @@ class CRefMgr {
void addToFreeList( CookieRef* cref ); void addToFreeList( CookieRef* cref );
CookieRef* getFromFreeList( void ); CookieRef* getFromFreeList( void );
CookieRef* getMakeCookieRef_locked( const char* cORn, bool isCookie, CookieRef* getMakeCookieRef_locked( const char* cookie, const char* connName,
HostID hid, int socket, HostID hid, int socket,
int nPlayersH, int nPlayersT ); int nPlayersH, int nPlayersT );
CookieRef* getCookieRef( CookieID cookieID ); CookieRef* getCookieRef( CookieID cookieID );
@ -108,8 +108,9 @@ class CRefMgr {
bool checkCookieRef_locked( CookieRef* cref ); bool checkCookieRef_locked( CookieRef* cref );
CookieRef* getCookieRef_impl( CookieID cookieID ); CookieRef* getCookieRef_impl( CookieID cookieID );
CookieRef* AddNew( const char* cookie, const char* connName, CookieID id ); CookieRef* AddNew( const char* cookie, const char* connName, CookieID id );
CookieRef* FindOpenGameFor( const char* cORn, bool isCookie, CookieRef* FindOpenGameFor( const char* cookie, const char* connName,
HostID hid, int socket, int nPlayersH, int nPlayersT ); HostID hid, int socket, int nPlayersH,
int nPlayersT );
CookieID cookieIDForConnName( const char* connName ); CookieID cookieIDForConnName( const char* connName );
CookieID nextCID( const char* connName ); CookieID nextCID( const char* connName );
@ -117,6 +118,7 @@ class CRefMgr {
static void heartbeatProc( void* closure ); static void heartbeatProc( void* closure );
void checkHeartbeats( time_t now ); void checkHeartbeats( time_t now );
pthread_mutex_t m_nextCIDMutex;
CookieID m_nextCID; CookieID m_nextCID;
pthread_rwlock_t m_cookieMapRWLock; pthread_rwlock_t m_cookieMapRWLock;
@ -137,7 +139,7 @@ class SafeCref {
CookieRef instance at a time. */ CookieRef instance at a time. */
public: public:
SafeCref( const char* cookieOrConnName, bool cookie, HostID hid, SafeCref( const char* cookie, const char* connName, HostID hid,
int socket, int nPlayersH, int nPlayersT ); int socket, int nPlayersH, int nPlayersT );
SafeCref( CookieID cid, bool failOk = false ); SafeCref( CookieID cid, bool failOk = false );
SafeCref( int socket ); SafeCref( int socket );

View file

@ -137,7 +137,7 @@ logf( XW_LogLevel level, const char* format, ... )
} }
} /* logf */ } /* logf */
static const char* const char*
cmdToStr( XWRELAY_Cmd cmd ) cmdToStr( XWRELAY_Cmd cmd )
{ {
# define CASESTR(s) case s: return #s # define CASESTR(s) case s: return #s
@ -257,6 +257,11 @@ send_with_length_unsafe( int socket, unsigned char* buf, int bufLen )
ok = true; ok = true;
} }
} }
if ( !ok ) {
logf( XW_LOGERROR, "%s(socket=%d) failed", __func__, socket );
}
return ok; return ok;
} /* send_with_length_unsafe */ } /* send_with_length_unsafe */
@ -301,7 +306,7 @@ processConnect( unsigned char* bufp, int bufLen, int socket )
static pthread_mutex_t s_newCookieLock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t s_newCookieLock = PTHREAD_MUTEX_INITIALIZER;
MutexLock ml( &s_newCookieLock ); MutexLock ml( &s_newCookieLock );
SafeCref scr( cookie, true, srcID, socket, nPlayersH, nPlayersT ); SafeCref scr( cookie, NULL, srcID, socket, nPlayersH, nPlayersT );
success = scr.Connect( socket, srcID, nPlayersH, nPlayersT ); success = scr.Connect( socket, srcID, nPlayersH, nPlayersT );
} }
@ -325,18 +330,22 @@ processReconnect( unsigned char* bufp, int bufLen, int socket )
if ( err != XWRELAY_ERROR_NONE ) { if ( err != XWRELAY_ERROR_NONE ) {
denyConnection( socket, err ); denyConnection( socket, err );
} else { } else {
char cookie[MAX_COOKIE_LEN+1];
char connName[MAX_CONNNAME_LEN+1]; char connName[MAX_CONNNAME_LEN+1];
HostID srcID; HostID srcID;
unsigned char nPlayersH; unsigned char nPlayersH;
unsigned char nPlayersT; unsigned char nPlayersT;
connName[0] = '\0'; connName[0] = '\0';
if ( getNetByte( &bufp, end, &srcID ) if ( readStr( &bufp, end, cookie, sizeof(cookie) )
&& getNetByte( &bufp, end, &srcID )
&& getNetByte( &bufp, end, &nPlayersH ) && getNetByte( &bufp, end, &nPlayersH )
&& getNetByte( &bufp, end, &nPlayersT ) && getNetByte( &bufp, end, &nPlayersT )
&& readStr( &bufp, end, connName, sizeof(connName) ) ) { && readStr( &bufp, end, connName, sizeof(connName) ) ) {
SafeCref scr( connName, false, srcID, socket, nPlayersH, SafeCref scr( cookie[0]? cookie : NULL,
connName[0]? connName : NULL,
srcID, socket, nPlayersH,
nPlayersT ); nPlayersT );
success = scr.Reconnect( socket, srcID, nPlayersH, nPlayersT ); success = scr.Reconnect( socket, srcID, nPlayersH, nPlayersT );
} }

View file

@ -38,11 +38,11 @@ enum { XWRELAY_NONE /* 0 is an illegal value */
nPlayersTotal: 1 */ nPlayersTotal: 1 */
, XWRELAY_GAME_RECONNECT , XWRELAY_GAME_RECONNECT
/* Connect using connName rather than cookie. Used by a device that's /* Connect using connName as well as cookie. Used by a device that's
lost its connection to a game in progress. Once a game is locked lost its connection to a game in progress. Once a game is locked
this is the only way a host can get (back) in. Format: flags: 1; this is the only way a host can get (back) in. Format: flags: 1;
hostID: 1; nPlayers: 1; nPlayersTotal: 1; connNameLen: 1; cookieLen: 1; cookie: <cookieLen>; hostID: 1; nPlayers: 1;
connName<connNameLen>*/ nPlayersTotal: 1; connNameLen: 1; connName<connNameLen>*/
, XWRELAY_GAME_DISCONNECT , XWRELAY_GAME_DISCONNECT
/* Tell the relay that we're gone for this game. After this message is /* Tell the relay that we're gone for this game. After this message is
@ -51,18 +51,19 @@ enum { XWRELAY_NONE /* 0 is an illegal value */
, XWRELAY_CONNECT_RESP , XWRELAY_CONNECT_RESP
/* Sent from relay to device in response to XWRELAY_CONNECT. Format: /* Sent from relay to device in response to XWRELAY_CONNECT. Format:
heartbeat_seconds: 2; connectionID: 2; assignedHostID: 1 */ heartbeat_seconds: 2; connectionID: 2; */
, XWRELAY_RECONNECT_RESP , XWRELAY_RECONNECT_RESP
/* Sent from relay to device in response to XWRELAY_RECONNECT. Format: /* Sent from relay to device in response to XWRELAY_RECONNECT. Format:
heartbeat_seconds: 2; connectionID: 2; */ heartbeat_seconds: 2; */
, XWRELAY_ALLHERE , XWRELAY_ALLHERE
/* Sent from relay when it enters the state where all expected devices /* Sent from relay when it enters the state where all expected devices
are here (at start of new game or after having been gone for a are here (at start of new game or after having been gone for a
while). Devices should not attempt to forward messages before this while). Devices should not attempt to forward messages before this
message is received or after XWRELAY_DISCONNECT_OTHER is received. message is received or after XWRELAY_DISCONNECT_OTHER is received.
Format: hasName: 1; [nameLen: 1; connName: <nameLen> */ Format: hostID: 1; hasName: 1; [connNameLen: 1; connName:
<connNameLen>]; */
, XWRELAY_DISCONNECT_YOU , XWRELAY_DISCONNECT_YOU
/* Sent from relay when existing connection is terminated. /* Sent from relay when existing connection is terminated.

View file

@ -5,6 +5,7 @@
#include <time.h> #include <time.h>
#include "lstnrmgr.h" #include "lstnrmgr.h"
#include "xwrelay.h"
typedef unsigned char HostID; /* see HOST_ID_SERVER */ typedef unsigned char HostID; /* see HOST_ID_SERVER */
@ -27,6 +28,8 @@ int GetNSpawns(void);
int make_socket( unsigned long addr, unsigned short port ); int make_socket( unsigned long addr, unsigned short port );
const char* cmdToStr( XWRELAY_Cmd cmd );
extern class ListenerMgr g_listeners; extern class ListenerMgr g_listeners;
#endif #endif