2012-02-07 07:49:00 +00:00
// cSocketThreads.cpp
// Implements the cSocketThreads class representing the heart of MCS's client networking.
// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support
// For more detail, see http://forum.mc-server.org/showthread.php?tid=327
#include "Globals.h"
#include "cSocketThreads.h"
#include "cClientHandle.h"
2012-02-13 21:47:03 +00:00
// #include "packets/cPacket_RelativeEntityMoveLook.h"
2012-02-07 07:49:00 +00:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cSocketThreads:
cSocketThreads :: cSocketThreads ( void )
{
2012-02-13 21:47:03 +00:00
LOG ( "cSocketThreads startup" );
2012-02-07 07:49:00 +00:00
}
cSocketThreads ::~ cSocketThreads ()
{
for ( cSocketThreadList :: iterator itr = m_Threads . begin (); itr != m_Threads . end (); ++ itr )
{
delete * itr ;
} // for itr - m_Threads[]
m_Threads . clear ();
}
2012-02-08 10:02:46 +00:00
bool cSocketThreads :: AddClient ( cSocket * a_Socket , cCallback * a_Client )
2012-02-07 07:49:00 +00:00
{
// Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client
// Try to add to existing threads:
cCSLock Lock ( m_CS );
for ( cSocketThreadList :: iterator itr = m_Threads . begin (); itr != m_Threads . end (); ++ itr )
{
2012-02-08 10:02:46 +00:00
if (( * itr ) -> IsValid () && ( * itr ) -> HasEmptySlot ())
2012-02-07 07:49:00 +00:00
{
( * itr ) -> AddClient ( a_Socket , a_Client );
2012-02-08 10:02:46 +00:00
return true ;
2012-02-07 07:49:00 +00:00
}
}
// No thread has free space, create a new one:
2012-02-08 10:02:46 +00:00
LOG ( "Creating a new cSocketThread (currently have %d)" , m_Threads . size ());
2012-02-07 07:49:00 +00:00
cSocketThread * Thread = new cSocketThread ( this );
2012-02-08 10:02:46 +00:00
if ( ! Thread -> Start ())
{
// There was an error launching the thread (but it was already logged along with the reason)
2012-02-13 21:47:03 +00:00
LOGERROR ( "A new cSocketThread failed to start" );
2012-02-08 10:02:46 +00:00
delete Thread ;
return false ;
}
2012-02-07 07:49:00 +00:00
Thread -> AddClient ( a_Socket , a_Client );
m_Threads . push_back ( Thread );
2012-02-08 10:02:46 +00:00
return true ;
2012-02-07 07:49:00 +00:00
}
2012-02-08 10:02:46 +00:00
void cSocketThreads :: RemoveClient ( const cSocket * a_Socket )
2012-02-07 07:49:00 +00:00
{
// Remove the socket (and associated client) from processing
cCSLock Lock ( m_CS );
for ( cSocketThreadList :: iterator itr = m_Threads . begin (); itr != m_Threads . end (); ++ itr )
{
if (( * itr ) -> RemoveSocket ( a_Socket ))
{
return ;
}
2012-02-25 23:48:28 +00:00
} // for itr - m_Threads[]
2012-02-26 12:55:42 +00:00
// Cannot assert here, this may actually happen legally, since cClientHandle has to clean up the socket and it may have already closed in the meantime
// ASSERT(!"Removing an unknown socket");
2012-02-07 07:49:00 +00:00
}
2012-02-08 10:02:46 +00:00
void cSocketThreads :: RemoveClient ( const cCallback * a_Client )
2012-02-07 07:49:00 +00:00
{
// Remove the associated socket and the client from processing
cCSLock Lock ( m_CS );
for ( cSocketThreadList :: iterator itr = m_Threads . begin (); itr != m_Threads . end (); ++ itr )
{
if (( * itr ) -> RemoveClient ( a_Client ))
{
return ;
}
2012-02-25 23:48:28 +00:00
} // for itr - m_Threads[]
ASSERT ( ! "Removing an unknown client" );
2012-02-07 07:49:00 +00:00
}
2012-02-08 10:02:46 +00:00
void cSocketThreads :: NotifyWrite ( const cCallback * a_Client )
2012-02-07 07:49:00 +00:00
{
// Notifies the thread responsible for a_Client that the client has something to write
cCSLock Lock ( m_CS );
for ( cSocketThreadList :: iterator itr = m_Threads . begin (); itr != m_Threads . end (); ++ itr )
{
if (( * itr ) -> NotifyWrite ( a_Client ))
{
return ;
}
2012-02-25 23:48:28 +00:00
} // for itr - m_Threads[]
// Cannot assert - this normally happens if a client disconnects and has pending packets, the cServer::cNotifyWriteThread will call this on invalid clients too
// ASSERT(!"Notifying write to an unknown client");
}
void cSocketThreads :: Write ( const cSocket * a_Socket , const AString & a_Data )
{
// Puts a_Data into outgoing data queue for a_Socket
if ( ! a_Socket -> IsValid ())
{
// Socket already closed, ignore the request
return ;
}
cCSLock Lock ( m_CS );
for ( cSocketThreadList :: iterator itr = m_Threads . begin (); itr != m_Threads . end (); ++ itr )
{
if (( * itr ) -> Write ( a_Socket , a_Data ))
{
return ;
}
} // for itr - m_Threads[]
2012-03-10 17:37:00 +00:00
// This may be perfectly legal, if the socket has been destroyed and the client is finishing up
// ASSERT(!"Writing to an unknown socket");
2012-02-25 23:48:28 +00:00
}
/// Stops reading from the socket - when this call returns, no more calls to the callbacks are made
void cSocketThreads :: StopReading ( const cCallback * a_Client )
{
cCSLock Lock ( m_CS );
for ( cSocketThreadList :: iterator itr = m_Threads . begin (); itr != m_Threads . end (); ++ itr )
{
if (( * itr ) -> StopReading ( a_Client ))
{
return ;
}
} // for itr - m_Threads[]
// Cannot assert, this normally happens if the socket is closed before the client deinitializes
// ASSERT(!"Stopping reading on an unknown client");
}
/// Queues the socket for closing, as soon as its outgoing data is sent
void cSocketThreads :: QueueClose ( const cSocket * a_Socket )
{
if ( ! a_Socket -> IsValid ())
{
// Already closed, ignore the request
return ;
2012-02-07 07:49:00 +00:00
}
2012-02-25 23:48:28 +00:00
cCSLock Lock ( m_CS );
for ( cSocketThreadList :: iterator itr = m_Threads . begin (); itr != m_Threads . end (); ++ itr )
{
if (( * itr ) -> QueueClose ( a_Socket ))
{
return ;
}
} // for itr - m_Threads[]
ASSERT ( ! "Queueing close of an unknown socket" );
2012-02-07 07:49:00 +00:00
}
////////////////////////////////////////////////////////////////////////////////
// cSocketThreads::cSocketThread:
cSocketThreads :: cSocketThread :: cSocketThread ( cSocketThreads * a_Parent ) :
cIsThread ( "cSocketThread" ),
m_Parent ( a_Parent ),
m_NumSlots ( 0 )
{
// Nothing needed yet
}
2012-02-13 21:47:03 +00:00
cSocketThreads :: cSocketThread ::~ cSocketThread ()
{
2012-03-10 17:37:00 +00:00
m_ShouldTerminate = true ;
2012-03-19 09:36:34 +00:00
// Notify the thread:
ASSERT ( m_ControlSocket2 . IsValid ());
m_ControlSocket2 . Send ( "a" , 1 );
// Wait for the thread to finish:
Wait ();
// Close the control sockets:
2012-02-13 21:47:03 +00:00
m_ControlSocket1 . CloseSocket ();
m_ControlSocket2 . CloseSocket ();
}
2012-02-07 07:49:00 +00:00
void cSocketThreads :: cSocketThread :: AddClient ( cSocket * a_Socket , cCallback * a_Client )
{
2012-02-19 23:00:00 +00:00
ASSERT ( m_NumSlots < MAX_SLOTS ); // Use HasEmptySlot() to check before adding
2012-02-07 07:49:00 +00:00
m_Slots [ m_NumSlots ]. m_Client = a_Client ;
m_Slots [ m_NumSlots ]. m_Socket = a_Socket ;
m_Slots [ m_NumSlots ]. m_Outgoing . clear ();
m_NumSlots ++ ;
// Notify the thread of the change:
2012-02-19 23:00:00 +00:00
ASSERT ( m_ControlSocket2 . IsValid ());
2012-02-07 07:49:00 +00:00
m_ControlSocket2 . Send ( "a" , 1 );
}
2012-02-08 10:02:46 +00:00
bool cSocketThreads :: cSocketThread :: RemoveClient ( const cCallback * a_Client )
2012-02-07 07:49:00 +00:00
{
// Returns true if removed, false if not found
if ( m_NumSlots == 0 )
{
return false ;
}
2012-02-08 10:02:46 +00:00
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
2012-02-07 07:49:00 +00:00
{
if ( m_Slots [ i ]. m_Client != a_Client )
{
continue ;
}
// Found, remove it:
2012-02-25 23:48:28 +00:00
m_Slots [ i ] = m_Slots [ -- m_NumSlots ];
2012-02-07 07:49:00 +00:00
// Notify the thread of the change:
2012-02-19 23:00:00 +00:00
ASSERT ( m_ControlSocket2 . IsValid ());
2012-02-07 07:49:00 +00:00
m_ControlSocket2 . Send ( "r" , 1 );
return true ;
} // for i - m_Slots[]
// Not found
return false ;
}
2012-02-08 10:02:46 +00:00
bool cSocketThreads :: cSocketThread :: RemoveSocket ( const cSocket * a_Socket )
2012-02-07 07:49:00 +00:00
{
// Returns true if removed, false if not found
2012-02-08 10:02:46 +00:00
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
2012-02-07 07:49:00 +00:00
{
if ( m_Slots [ i ]. m_Socket != a_Socket )
{
continue ;
}
// Found, remove it:
2012-02-25 23:48:28 +00:00
m_Slots [ i ] = m_Slots [ -- m_NumSlots ];
2012-02-07 07:49:00 +00:00
// Notify the thread of the change:
2012-02-19 23:00:00 +00:00
ASSERT ( m_ControlSocket2 . IsValid ());
2012-02-07 07:49:00 +00:00
m_ControlSocket2 . Send ( "r" , 1 );
return true ;
} // for i - m_Slots[]
// Not found
return false ;
}
2012-02-25 23:48:28 +00:00
bool cSocketThreads :: cSocketThread :: HasClient ( const cCallback * a_Client ) const
{
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
{
if ( m_Slots [ i ]. m_Client == a_Client )
{
return true ;
}
} // for i - m_Slots[]
return false ;
}
bool cSocketThreads :: cSocketThread :: HasSocket ( const cSocket * a_Socket ) const
{
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
{
if ( m_Slots [ i ]. m_Socket -> GetSocket () == a_Socket -> GetSocket ())
{
return true ;
}
} // for i - m_Slots[]
return false ;
}
2012-02-08 10:02:46 +00:00
bool cSocketThreads :: cSocketThread :: NotifyWrite ( const cCallback * a_Client )
2012-02-07 07:49:00 +00:00
{
if ( HasClient ( a_Client ))
{
// Notify the thread that there's another packet in the queue:
2012-02-19 23:00:00 +00:00
ASSERT ( m_ControlSocket2 . IsValid ());
2012-02-07 07:49:00 +00:00
m_ControlSocket2 . Send ( "q" , 1 );
return true ;
}
return false ;
}
2012-02-25 23:48:28 +00:00
bool cSocketThreads :: cSocketThread :: Write ( const cSocket * a_Socket , const AString & a_Data )
2012-02-07 07:49:00 +00:00
{
2012-02-25 23:48:28 +00:00
// Returns true if socket handled by this thread
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
{
if ( m_Slots [ i ]. m_Socket == a_Socket )
{
m_Slots [ i ]. m_Outgoing . append ( a_Data );
// Notify the thread that there's data in the queue:
ASSERT ( m_ControlSocket2 . IsValid ());
m_ControlSocket2 . Send ( "q" , 1 );
return true ;
}
} // for i - m_Slots[]
return false ;
}
bool cSocketThreads :: cSocketThread :: StopReading ( const cCallback * a_Client )
{
// Returns true if client handled by this thread
2012-02-07 07:49:00 +00:00
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
{
if ( m_Slots [ i ]. m_Client == a_Client )
{
2012-02-25 23:48:28 +00:00
m_Slots [ i ]. m_Client = NULL ;
m_Slots [ i ]. m_ShouldClose = false ;
// Notify the thread that there's a stop reading request:
ASSERT ( m_ControlSocket2 . IsValid ());
m_ControlSocket2 . Send ( "s" , 1 );
2012-02-07 07:49:00 +00:00
return true ;
}
} // for i - m_Slots[]
return false ;
}
2012-02-25 23:48:28 +00:00
bool cSocketThreads :: cSocketThread :: QueueClose ( const cSocket * a_Socket )
2012-02-07 07:49:00 +00:00
{
2012-02-25 23:48:28 +00:00
// Returns true if socket handled by this thread
2012-02-07 07:49:00 +00:00
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
{
2012-02-25 23:48:28 +00:00
if ( m_Slots [ i ]. m_Socket == a_Socket )
2012-02-07 07:49:00 +00:00
{
2012-02-25 23:48:28 +00:00
ASSERT ( m_Slots [ i ]. m_Client == NULL ); // Should have stopped reading first
m_Slots [ i ]. m_ShouldClose = true ;
// Notify the thread that there's a close queued (in case its conditions are already met):
ASSERT ( m_ControlSocket2 . IsValid ());
m_ControlSocket2 . Send ( "c" , 1 );
2012-02-07 07:49:00 +00:00
return true ;
}
} // for i - m_Slots[]
return false ;
}
bool cSocketThreads :: cSocketThread :: Start ( void )
{
// Create the control socket listener
2012-02-08 10:02:46 +00:00
m_ControlSocket2 = cSocket :: CreateSocket ();
if ( ! m_ControlSocket2 . IsValid ())
2012-02-07 07:49:00 +00:00
{
LOGERROR ( "Cannot create a Control socket for a cSocketThread ( \" %s \" ); continuing, but server may be unreachable from now on." , cSocket :: GetLastErrorString (). c_str ());
return false ;
}
cSocket :: SockAddr_In Addr ;
Addr . Family = cSocket :: ADDRESS_FAMILY_INTERNET ;
2012-02-07 21:16:34 +00:00
Addr . Address = cSocket :: INTERNET_ADDRESS_LOCALHOST ();
2012-02-07 07:49:00 +00:00
Addr . Port = 0 ; // Any free port is okay
2012-02-08 10:02:46 +00:00
if ( m_ControlSocket2 . Bind ( Addr ) != 0 )
2012-02-07 07:49:00 +00:00
{
LOGERROR ( "Cannot bind a Control socket for a cSocketThread ( \" %s \" ); continuing, but server may be unreachable from now on." , cSocket :: GetLastErrorString (). c_str ());
2012-02-08 10:02:46 +00:00
m_ControlSocket2 . CloseSocket ();
2012-02-07 07:49:00 +00:00
return false ;
}
2012-02-08 10:02:46 +00:00
if ( m_ControlSocket2 . Listen ( 1 ) != 0 )
{
LOGERROR ( "Cannot listen on a Control socket for a cSocketThread ( \" %s \" ); continuing, but server may be unreachable from now on." , cSocket :: GetLastErrorString (). c_str ());
m_ControlSocket2 . CloseSocket ();
return false ;
}
if ( m_ControlSocket2 . GetPort () == 0 )
2012-02-07 07:49:00 +00:00
{
LOGERROR ( "Cannot determine Control socket port ( \" %s \" ); conitnuing, but the server may be unreachable from now on." , cSocket :: GetLastErrorString (). c_str ());
2012-02-08 10:02:46 +00:00
m_ControlSocket2 . CloseSocket ();
2012-02-07 07:49:00 +00:00
return false ;
}
// Start the thread
if ( ! super :: Start ())
{
2012-02-13 21:47:03 +00:00
LOGERROR ( "Cannot start new cSocketThread" );
2012-02-08 10:02:46 +00:00
m_ControlSocket2 . CloseSocket ();
2012-02-07 07:49:00 +00:00
return false ;
}
// Finish connecting the control socket by accepting connection from the thread's socket
2012-02-08 10:02:46 +00:00
cSocket tmp = m_ControlSocket2 . Accept ();
2012-02-07 07:49:00 +00:00
if ( ! tmp . IsValid ())
{
LOGERROR ( "Cannot link Control sockets for a cSocketThread ( \" %s \" ); continuing, but server may be unreachable from now on." , cSocket :: GetLastErrorString (). c_str ());
2012-02-08 10:02:46 +00:00
m_ControlSocket2 . CloseSocket ();
2012-02-07 07:49:00 +00:00
return false ;
}
2012-02-08 10:02:46 +00:00
m_ControlSocket2 . CloseSocket ();
m_ControlSocket2 = tmp ;
2012-02-07 07:49:00 +00:00
return true ;
}
void cSocketThreads :: cSocketThread :: Execute ( void )
{
// Connect the "client" part of the Control socket:
2012-02-08 10:02:46 +00:00
m_ControlSocket1 = cSocket :: CreateSocket ();
2012-02-07 07:49:00 +00:00
cSocket :: SockAddr_In Addr ;
Addr . Family = cSocket :: ADDRESS_FAMILY_INTERNET ;
2012-02-07 21:16:34 +00:00
Addr . Address = cSocket :: INTERNET_ADDRESS_LOCALHOST ();
2012-02-08 10:02:46 +00:00
Addr . Port = m_ControlSocket2 . GetPort ();
2012-02-19 23:00:00 +00:00
ASSERT ( Addr . Port != 0 ); // We checked in the Start() method, but let's be sure
2012-02-08 10:02:46 +00:00
if ( m_ControlSocket1 . Connect ( Addr ) != 0 )
2012-02-07 07:49:00 +00:00
{
LOGERROR ( "Cannot connect Control sockets for a cSocketThread ( \" %s \" ); continuing, but the server may be unreachable from now on." , cSocket :: GetLastErrorString (). c_str ());
2012-02-08 10:02:46 +00:00
m_ControlSocket2 . CloseSocket ();
2012-02-07 07:49:00 +00:00
return ;
}
// The main thread loop:
2012-03-10 17:37:00 +00:00
while ( ! m_ShouldTerminate )
2012-02-07 07:49:00 +00:00
{
// Put all sockets into the Read set:
fd_set fdRead ;
2012-02-08 10:02:46 +00:00
cSocket :: xSocket Highest = m_ControlSocket1 . GetSocket ();
2012-02-07 07:49:00 +00:00
PrepareSet ( & fdRead , Highest );
// Wait for the sockets:
if ( select ( Highest + 1 , & fdRead , NULL , NULL , NULL ) == - 1 )
{
2012-02-08 10:02:46 +00:00
LOG ( "select(R) call failed in cSocketThread: \" %s \" " , cSocket :: GetLastErrorString (). c_str ());
continue ;
2012-02-07 07:49:00 +00:00
}
ReadFromSockets ( & fdRead );
// Test sockets for writing:
fd_set fdWrite ;
2012-02-08 10:02:46 +00:00
Highest = m_ControlSocket1 . GetSocket ();
2012-02-07 07:49:00 +00:00
PrepareSet ( & fdWrite , Highest );
timeval Timeout ;
Timeout . tv_sec = 0 ;
Timeout . tv_usec = 0 ;
if ( select ( Highest + 1 , NULL , & fdWrite , NULL , & Timeout ) == - 1 )
{
2012-02-08 10:02:46 +00:00
LOG ( "select(W) call failed in cSocketThread: \" %s \" " , cSocket :: GetLastErrorString (). c_str ());
continue ;
2012-02-07 07:49:00 +00:00
}
WriteToSockets ( & fdWrite );
2012-02-08 10:02:46 +00:00
RemoveClosedSockets ();
2012-02-07 07:49:00 +00:00
} // while (!mShouldTerminate)
}
void cSocketThreads :: cSocketThread :: PrepareSet ( fd_set * a_Set , cSocket :: xSocket & a_Highest )
{
FD_ZERO ( a_Set );
FD_SET ( m_ControlSocket1 . GetSocket (), a_Set );
cCSLock Lock ( m_Parent -> m_CS );
2012-02-08 10:02:46 +00:00
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
2012-02-07 07:49:00 +00:00
{
if ( ! m_Slots [ i ]. m_Socket -> IsValid ())
{
continue ;
}
cSocket :: xSocket s = m_Slots [ i ]. m_Socket -> GetSocket ();
FD_SET ( s , a_Set );
if ( s > a_Highest )
{
a_Highest = s ;
}
} // for i - m_Slots[]
}
void cSocketThreads :: cSocketThread :: ReadFromSockets ( fd_set * a_Read )
{
// Read on available sockets:
2012-02-08 10:02:46 +00:00
// Reset Control socket state:
if ( FD_ISSET ( m_ControlSocket1 . GetSocket (), a_Read ))
{
char Dummy [ 128 ];
m_ControlSocket1 . Receive ( Dummy , sizeof ( Dummy ), 0 );
}
// Read from clients:
2012-02-07 07:49:00 +00:00
cCSLock Lock ( m_Parent -> m_CS );
2012-02-08 10:02:46 +00:00
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
2012-02-07 07:49:00 +00:00
{
if ( ! FD_ISSET ( m_Slots [ i ]. m_Socket -> GetSocket (), a_Read ))
{
continue ;
}
char Buffer [ 1024 ];
int Received = m_Slots [ i ]. m_Socket -> Receive ( Buffer , ARRAYCOUNT ( Buffer ), 0 );
if ( Received == 0 )
{
// The socket has been closed by the remote party, close our socket and let it be removed after we process all reading
m_Slots [ i ]. m_Socket -> CloseSocket ();
2012-02-25 23:48:28 +00:00
if ( m_Slots [ i ]. m_Client != NULL )
{
m_Slots [ i ]. m_Client -> SocketClosed ();
}
2012-02-07 07:49:00 +00:00
}
else if ( Received > 0 )
{
2012-02-25 23:48:28 +00:00
if ( m_Slots [ i ]. m_Client != NULL )
{
m_Slots [ i ]. m_Client -> DataReceived ( Buffer , Received );
}
2012-02-07 07:49:00 +00:00
}
else
{
// The socket has encountered an error, close it and let it be removed after we process all reading
m_Slots [ i ]. m_Socket -> CloseSocket ();
2012-02-25 23:48:28 +00:00
if ( m_Slots [ i ]. m_Client != NULL )
{
m_Slots [ i ]. m_Client -> SocketClosed ();
}
2012-02-07 07:49:00 +00:00
}
} // for i - m_Slots[]
}
void cSocketThreads :: cSocketThread :: WriteToSockets ( fd_set * a_Write )
{
2012-02-08 10:02:46 +00:00
// Write to available client sockets:
2012-02-07 07:49:00 +00:00
cCSLock Lock ( m_Parent -> m_CS );
2012-02-08 10:02:46 +00:00
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
2012-02-07 07:49:00 +00:00
{
if ( ! FD_ISSET ( m_Slots [ i ]. m_Socket -> GetSocket (), a_Write ))
{
continue ;
}
if ( m_Slots [ i ]. m_Outgoing . empty ())
{
// Request another chunk of outgoing data:
2012-02-25 23:48:28 +00:00
if ( m_Slots [ i ]. m_Client != NULL )
{
m_Slots [ i ]. m_Client -> GetOutgoingData ( m_Slots [ i ]. m_Outgoing );
}
2012-02-07 07:49:00 +00:00
if ( m_Slots [ i ]. m_Outgoing . empty ())
{
2012-02-25 23:48:28 +00:00
// Nothing ready
if (( m_Slots [ i ]. m_Client == NULL ) && m_Slots [ i ]. m_ShouldClose )
{
// Socket was queued for closing and there's no more data to send, close it now:
m_Slots [ i ]. m_Socket -> CloseSocket ();
m_Slots [ i ] = m_Slots [ -- m_NumSlots ];
}
2012-02-07 07:49:00 +00:00
continue ;
}
} // if (outgoing data is empty)
int Sent = m_Slots [ i ]. m_Socket -> Send ( m_Slots [ i ]. m_Outgoing . data (), m_Slots [ i ]. m_Outgoing . size ());
if ( Sent < 0 )
{
2012-02-25 23:48:28 +00:00
int Err = cSocket :: GetLastError ();
LOGWARNING ( "Error %d while writing to client \" %s \" , disconnecting. \" %s \" " , Err , m_Slots [ i ]. m_Socket -> GetIPString (). c_str (), cSocket :: GetErrorString ( Err ). c_str ());
2012-02-07 07:49:00 +00:00
m_Slots [ i ]. m_Socket -> CloseSocket ();
2012-02-25 23:48:28 +00:00
if ( m_Slots [ i ]. m_Client != NULL )
{
m_Slots [ i ]. m_Client -> SocketClosed ();
}
2012-02-07 07:49:00 +00:00
return ;
}
m_Slots [ i ]. m_Outgoing . erase ( 0 , Sent );
2012-02-08 10:02:46 +00:00
// _X: If there's data left, it means the client is not reading fast enough, the server would unnecessarily spin in the main loop with zero actions taken; so signalling is disabled
// This means that if there's data left, it will be sent only when there's incoming data or someone queues another packet (for any socket handled by this thread)
/*
// If there's any data left, signalize the Control socket:
if (!m_Slots[i].m_Outgoing.empty())
{
2012-02-19 23:00:00 +00:00
ASSERT(m_ControlSocket2.IsValid());
2012-02-08 10:02:46 +00:00
m_ControlSocket2.Send("q", 1);
}
*/
2012-02-07 21:16:34 +00:00
} // for i - m_Slots[i]
2012-02-07 07:49:00 +00:00
}
2012-02-08 10:02:46 +00:00
void cSocketThreads :: cSocketThread :: RemoveClosedSockets ( void )
{
// Removes sockets that have closed from m_Slots[]
cCSLock Lock ( m_Parent -> m_CS );
for ( int i = m_NumSlots - 1 ; i >= 0 ; -- i )
{
if ( m_Slots [ i ]. m_Socket -> IsValid ())
{
continue ;
}
2012-02-25 23:48:28 +00:00
m_Slots [ i ] = m_Slots [ -- m_NumSlots ];
2012-02-08 10:02:46 +00:00
} // for i - m_Slots[]
}