diff --git a/xwords4/relay/Makefile b/xwords4/relay/Makefile index 83e0f9382..57053e054 100644 --- a/xwords4/relay/Makefile +++ b/xwords4/relay/Makefile @@ -34,6 +34,7 @@ SRC = \ devmgr.cpp \ udpqueue.cpp \ udpack.cpp \ + udpager.cpp \ xwrelay.cpp \ # STATIC ?= -static diff --git a/xwords4/relay/udpager.cpp b/xwords4/relay/udpager.cpp new file mode 100644 index 000000000..31b3bf67a --- /dev/null +++ b/xwords4/relay/udpager.cpp @@ -0,0 +1,89 @@ +/* -*- compile-command: "make -k -j3"; -*- */ + +/* + * Copyright 2013 by Eric House (xwords@eehouse.org). All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "udpager.h" +#include "configs.h" +#include "mlock.h" + +static UDPAger* s_instance = NULL; + +/* static */ UDPAger* +UDPAger::Get() +{ + if ( NULL == s_instance ) { + s_instance = new UDPAger(); + } + return s_instance; +} /* Get */ + +UDPAger::UDPAger() +{ + if ( !RelayConfigs::GetConfigs()-> GetValueFor( "UDP_RECYLE_INTERVAL", + &m_maxInterval ) ) { + assert(0); + } + logf( XW_LOGINFO, "read %d from configs for UDP_RECYLE_INTERVAL", + m_maxInterval ); + m_maxInterval *= 1000; // make it milliseconds + + pthread_mutex_init( &m_addrTimeMapLock, NULL ); +} + +// An address is valid as long as we keep hearing from it within a certain +// frequency. When we hear from it but it's been too long, assume it's new +// and give it a new timestamp. +void +UDPAger::Refresh( const AddrInfo* addr ) +{ + const AddrInfo::AddrUnion* saddr = addr->saddr(); + uint32_t readWhen = addr->created(); + gchar* b64 = g_base64_encode( (unsigned char*)&saddr->u.addr, + sizeof(saddr->u.addr) ); + + MutexLock ml( &m_addrTimeMapLock ); + + map::iterator iter = + m_addrTimeMap.find( *saddr ); + if ( m_addrTimeMap.end() == iter ) { // it's new; just insert + AgePair* ap = new AgePair( readWhen, readWhen ); + m_addrTimeMap.insert( pair(*saddr, ap ) ); + logf( XW_LOGINFO, "%s: adding '%s'", __func__, b64 ); + } else { + AgePair* ap = iter->second; + assert( ap->lastSeen() <= readWhen ); + int interval = readWhen - ap->lastSeen(); + if ( m_maxInterval >= interval ) { + logf( XW_LOGINFO, "%s: refreshing '%s'; last seen %d " + "milliseconds ago", __func__, b64, interval ); + ap->update( readWhen ); + } else { + logf( XW_LOGINFO, "%s: RESETTING '%s'; last seen %d " + "milliseconds ago", __func__, b64, interval ); + delete ap; + iter->second = new AgePair( readWhen, readWhen ); + } + } + + g_free( b64 ); +} + diff --git a/xwords4/relay/udpager.h b/xwords4/relay/udpager.h new file mode 100644 index 000000000..7b8915f13 --- /dev/null +++ b/xwords4/relay/udpager.h @@ -0,0 +1,60 @@ +/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ +/* + * Copyright 2013 by Eric House (xwords@eehouse.org). All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _UDPAGER_H_ +#define _UDPAGER_H_ + +#include + +#include "addrinfo.h" + +using namespace std; + +class UDPAger { + public: + static UDPAger* Get(); + UDPAger(); + void Refresh( const AddrInfo* addr ); + + private: + + class AgePair { + public: + AgePair( uint32_t created, uint32_t lastSeen ) { + m_created = created; + m_lastSeen = lastSeen; + } + void update( uint32_t lastSeen ) { m_lastSeen = lastSeen; } + uint32_t lastSeen() { return m_lastSeen; } + private: + uint32_t m_created; + uint32_t m_lastSeen; + }; + + /* Map socket addresses against times, moving the time forward only + when it's been too long since we saw it. */ + int m_maxInterval; /* config: how long since we heard */ + + map m_addrTimeMap; + pthread_mutex_t m_addrTimeMapLock; + + +}; + +#endif diff --git a/xwords4/relay/xwrelay.conf_tmplate b/xwords4/relay/xwrelay.conf_tmplate index 2a491c0c2..d9157acdb 100644 --- a/xwords4/relay/xwrelay.conf_tmplate +++ b/xwords4/relay/xwrelay.conf_tmplate @@ -27,7 +27,9 @@ GAME_PORTS=10997 DEVICE_PORTS=10998 # Port for per-device UDP interface (experimental) -UDPPORT=10997 +UDP_PORT=10997 +# How long after we've read from an address before we assume it's recycled +UDP_RECYLE_INTERVAL=60 # default 5 SOCK_TIMEOUT_SECONDS=5 diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index b07f3b05e..ac5f823ed 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -80,6 +80,7 @@ #include "devmgr.h" #include "udpqueue.h" #include "udpack.h" +#include "udpager.h" typedef struct _UDPHeader { uint32_t packetID; @@ -1499,6 +1500,7 @@ read_udp_packet( int udpsock ) #endif AddrInfo addr( udpsock, &saddr, false ); + UDPAger::Get()->Refresh( &addr ); UdpQueue::get()->handle( &addr, buf, nRead, handle_udp_packet ); } } @@ -1744,7 +1746,7 @@ main( int argc, char** argv ) (void)cfg->GetValueFor( "CTLPORT", &ctrlport ); } if ( -1 == udpport ) { - (void)cfg->GetValueFor( "UDPPORT", &udpport ); + (void)cfg->GetValueFor( "UDP_PORT", &udpport ); } #ifdef DO_HTTP if ( httpport == 0 ) {