www.pudn.com > BTSERVER.rar > server.cpp


/***
*
* RainbowBT Beta 7.7 - A C++ BitTorrent Tracker
* Copyright (C) 2003 Trevor Hogan
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
***/

#include "bnbt.h"
#include "client.h"
#include "config.h"
#include "server.h"
#include "tracker.h"
#include "util.h"

#ifdef WIN32
 #include "util_ntservice.h"
#endif

CServer :: CServer( )
{
	m_bKill = false;

	m_iSocketTimeOut = CFG_GetInt( "socket_timeout", 15 );
	m_strBind = CFG_GetString( "bind", string( ) );
	m_iCompression = CFG_GetInt( "RainbowBT_compression_level", 6 );

	// clamp

	if( m_iCompression > 9 )
		m_iCompression = 9;

	struct sockaddr_in sin;

	memset( &sin, 0, sizeof( sin ) );

	sin.sin_family = AF_INET;

	if( !m_strBind.empty( ) )
	{
		// bind to m_strBind

		if( gbDebug )
			UTIL_LogPrint( "server - binding to %s\n", m_strBind.c_str( ) );

		if( ( sin.sin_addr.s_addr = inet_addr( m_strBind.c_str( ) ) ) == INADDR_NONE )
		{
			UTIL_LogPrint( "server error - unable to bind to %s\n", m_strBind.c_str( ) );

			Kill( );
		}
	}
	else
	{
		// bind to all available addresses

		if( gbDebug )
			UTIL_LogPrint( "server - binding to all available addresses\n" );

		sin.sin_addr.s_addr = INADDR_ANY;
	}

	if( ( sin.sin_port = htons( (u_short)CFG_GetInt( "port", 6969 ) ) ) == 0 )
	{
		UTIL_LogPrint( "server error - invalid port %d\n", CFG_GetInt( "port", 6969 ) );

		Kill( );
	}

	// map protocol name to protocol number

	struct protoent *pPE;

	if( ( pPE = getprotobyname( "tcp" ) ) == 0 )
	{
		UTIL_LogPrint( "server error - unable to get tcp protocol entry (error %d)\n", GetLastError( ) );

		Kill( );
	}

	// allocate socket

	if( ( m_sckServer = socket( PF_INET, SOCK_STREAM, pPE->p_proto ) ) == INVALID_SOCKET )
	{
		UTIL_LogPrint( "server error - unable to allocate socket (error %d)\n", GetLastError( ) );

		Kill( );
	}

	// bind socket

	int optval = 1;

#ifdef WIN32
	setsockopt( m_sckServer, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( int ) );
#else
	setsockopt( m_sckServer, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof( int ) );
#endif

	if( bind( m_sckServer, (struct sockaddr *)&sin, sizeof( sin ) ) == SOCKET_ERROR )
	{
		UTIL_LogPrint( "server error - unable to bind socket (error %d)\n", GetLastError( ) );

		Kill( );
	}

	// listen, queue length 8

	if( listen( m_sckServer, 8 ) == SOCKET_ERROR )
	{
		UTIL_LogPrint( "server error - unable to listen (error %d)\n", GetLastError( ) );

		Kill( );
	}

	m_pTracker = new CTracker( );

	UTIL_LogPrint( "server - start\n" );
}

CServer :: ~CServer( )
{
	closesocket( m_sckServer );

	unsigned long iStart = GetTime( );

	while( 1 )
	{
		for( vector :: iterator i = m_vecClients.begin( ); i != m_vecClients.end( ); )
		{
			if( (*i)->m_iState == CS_WAITING1 || (*i)->m_iState == CS_WAITING2 || (*i)->m_iState == CS_DEAD )
			{
				delete *i;

				i = m_vecClients.erase( i );
			}
			else
				i++;
		}

		if( m_vecClients.size( ) > 0 )
		{
			UTIL_LogPrint( "server - waiting for %d clients to disconnect\n", m_vecClients.size( ) );

			MILLISLEEP( 1000 );
		}
		else
			break;

		if( GetTime( ) - iStart > 60 )
		{
			UTIL_LogPrint( "server - waited 60 seconds, exiting anyway\n" );

			break;
		}
	}

	if( m_pTracker )
		delete m_pTracker;

	m_pTracker = NULL;

	UTIL_LogPrint( "server - exit\n" );
}

void CServer :: Kill( )
{
	m_bKill = true;
}

bool CServer :: isDying( )
{
	return m_bKill;
}

bool CServer :: Update( bool bBlock )
{
	if( m_vecClients.size( ) < (unsigned int)giMaxConns )
	{
		fd_set fdServer;

		FD_ZERO( &fdServer );
		FD_SET( m_sckServer, &fdServer );

		struct timeval tv;

		if( bBlock )
		{
			// block for 100 ms to keep from eating up all cpu time

			tv.tv_sec = 0;
			tv.tv_usec = 100000;
		}
		else
		{
			tv.tv_sec = 0;
			tv.tv_usec = 0;
		}

#ifdef WIN32
		if( select( 1, &fdServer, NULL, NULL, &tv ) == SOCKET_ERROR )
#else
		if( select( m_sckServer + 1, &fdServer, NULL, NULL, &tv ) == SOCKET_ERROR )
#endif
		{
			UTIL_LogPrint( "server warning - select error (error %d)\n", GetLastError( ) );

			FD_ZERO( &fdServer );

			MILLISLEEP( 100 );
		}

		if( FD_ISSET( m_sckServer, &fdServer ) )
		{
			struct sockaddr_in adrFrom;

			int iAddrLen = sizeof( adrFrom );

			SOCKET sckClient;

#ifdef WIN32
			if( ( sckClient = accept( m_sckServer, (struct sockaddr *)&adrFrom, &iAddrLen ) ) == INVALID_SOCKET )
#else
			if( ( sckClient = accept( m_sckServer, (struct sockaddr *)&adrFrom, (socklen_t *)&iAddrLen ) ) == INVALID_SOCKET )
#endif
				UTIL_LogPrint( "server warning - accept error (error %d)\n", GetLastError( ) );
			else
			{
				// reuse the old timeval structure just because

				tv.tv_sec = m_iSocketTimeOut;
				tv.tv_usec = 0;

				CClient *pClient = new CClient( sckClient, adrFrom, tv, m_iCompression );

#ifdef WIN32
				if( _beginthread( ( void (*)(void *) )StartReceiving, 0, (void *)pClient ) == -1 )
				{
					UTIL_LogPrint( "server warning - unable to spawn receiver thread\n" );

					pClient->m_iState = CS_DEAD;
				}
#else
				pthread_t thread;

				// set detached state since we don't need to join with any threads

				pthread_attr_t attr;
				pthread_attr_init( &attr );
				pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );

				int c = pthread_create( &thread, &attr, ( void * (*)(void *) )StartReceiving, (void *)pClient );

				if( c != 0 )
				{
					UTIL_LogPrint( "server warning - unable to spawn receiver thread (error %s)\n", strerror( c ) );

					pClient->m_iState = CS_DEAD;
				}
#endif

				m_vecClients.push_back( pClient );
			}
		}
	}
	else
	{
		// maximum connections reached

		MILLISLEEP( 100 );
	}

	// process

	for( vector :: iterator i = m_vecClients.begin( ); i != m_vecClients.end( ); )
	{
		if( (*i)->m_iState == CS_WAITING1 )
		{
			(*i)->Process( );

			i++;
		}
		else if( (*i)->m_iState == CS_WAITING2 )
		{
			(*i)->m_iState = CS_SENDING;

#ifdef WIN32
			if( _beginthread( ( void (*)(void *) )StartSending, 0, (void *)(*i) ) == -1 )
			{
				UTIL_LogPrint( "server warning - unable to spawn sender thread\n" );

				(*i)->m_iState = CS_DEAD;
			}
#else
			pthread_t thread;

			// set detached state since we don't need to join with any threads

			pthread_attr_t attr;
			pthread_attr_init( &attr );
			pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );

			int c = pthread_create( &thread, &attr, ( void * (*)(void *) )StartSending, (void *)(*i) );

			if( c != 0 )
			{
				UTIL_LogPrint( "server warning - unable to spawn sender thread (error %s)\n", strerror( c ) );

				(*i)->m_iState = CS_DEAD;
			}
#endif

			i++;
		}
		else if( (*i)->m_iState == CS_DEAD )
		{
			delete *i;

			i = m_vecClients.erase( i );
		}
		else
			i++;
	}

	if( m_pTracker )
		m_pTracker->Update( );

	return m_bKill;
}

CTracker *CServer :: getTracker( )
{
	return m_pTracker;
}