# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

buf_sock.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "stdnet.h"
00027 
00028 #include "nel/misc/hierarchical_timer.h"
00029 
00030 #include "nel/net/buf_sock.h"
00031 #include "nel/net/buf_server.h"
00032 
00033 #ifdef NL_OS_WINDOWS
00034 #include <winsock2.h>
00035 #elif defined NL_OS_UNIX
00036 #include <netinet/in.h>
00037 #endif
00038 
00039 using namespace NLMISC;
00040 using namespace std;
00041 
00042 
00043 namespace NLNET {
00044 
00045 
00046 NLMISC::CMutex nettrace_mutex("nettrace_mutex");
00047 
00048 
00049 /*
00050  * Constructor
00051  */
00052 CBufSock::CBufSock( CTcpSock *sock ) :
00053         SendNextValue(0),
00054         ReceiveNextValue(0),
00055         Sock( sock ),
00056         _KnowConnected( false ),
00057         _LastFlushTime( 0 ),
00058         _TriggerTime( 20 ),
00059         _TriggerSize( -1 ),
00060         _RTSBIndex( 0 ),
00061         _AppId( 0 ),
00062         _ConnectedState( false )
00063 {
00064         nlnettrace( "CBufSock::CBufSock" ); // don't define a global object
00065 
00066         if ( Sock == NULL )
00067           {
00068                 Sock = new CTcpSock();
00069           }
00070 
00071 #ifdef NL_DEBUG
00072         _FlushTrigger = FTManual;
00073 #endif
00074         _LastFlushTime = CTime::getLocalTime();
00075 }
00076 
00077 
00078 /*
00079  * Destructor
00080  */
00081 CBufSock::~CBufSock()
00082 {
00083         nlassert (this != InvalidSockId);       // invalid bufsock
00084 
00085         nlnettrace( "CBufSock::~CBufSock" );
00086         
00087         delete Sock; // the socket disconnects automatically if needed
00088         
00089         // destroy the structur to be sure that other people will not access to this anymore
00090         AuthorizedCallback = "";
00091         Sock = NULL;
00092         _KnowConnected = false;
00093         _LastFlushTime = 0;
00094         _TriggerTime = 0;
00095         _TriggerSize = 0;
00096         _ReadyToSendBuffer.clear ();
00097         _RTSBIndex = 0;
00098         _AppId = 0;
00099         _ConnectedState = false;
00100 }
00101 
00102 
00103 /*
00104  * Returns a readable string from a vector of bytes, beginning from pos, limited to 'len' characters. '\0' are replaced by ' '
00105  */
00106 string stringFromVectorPart( const vector<uint8>& v, uint32 pos, uint32 len )
00107 {
00108         nlassertex( pos+len <= v.size(), ("pos=%u len=%u size=%u", pos, len, v.size()) );
00109 
00110         string s;
00111         if ( (! v.empty()) && (len!=0) )
00112         {
00113                 // Copy contents
00114                 s.resize( len );
00115                 memcpy( &*s.begin(), &*v.begin()+pos, len );
00116 
00117                 // Replace '\0' characters
00118                 string::iterator is;
00119                 for ( is=s.begin(); is!=s.end(); ++is )
00120                 {
00121                         if ( ! isprint((uint8)(*is)) || (*is) == '%' )
00122                         {
00123                                 (*is) = '?';
00124                         }
00125                 }
00126         }
00127 
00128         return s;
00129 }
00130 
00131 
00132 /*
00133  * Force to send all data pending in the send queue
00134  * Note: this method is used by clients (blocking sockets) and servers (non-blocking sockets)
00135  * so it works in both cases.
00136  * Precond: the send queue should not contain an empty block
00137  */
00138 bool CBufSock::flush()
00139 {
00140         nlassert (this != InvalidSockId);       // invalid bufsock
00141         //nlnettrace( "CBufSock::flush" );
00142 
00143         // Copy data from the send queue to _ReadyToSendBuffer
00144         TBlockSize netlen;
00145 //      vector<uint8> tmpbuffer;
00146 
00147         // Process each element in the send queue
00148         while ( ! SendFifo.empty() )
00149         {
00150                 uint8 *tmpbuffer;
00151                 uint32 size;
00152                 SendFifo.front( tmpbuffer, size );
00153 
00154                 // Compute the size and add it into the beginning of the buffer
00155                 netlen = htonl( (TBlockSize)size );
00156                 uint32 oldBufferSize = _ReadyToSendBuffer.size();
00157                 _ReadyToSendBuffer.resize (oldBufferSize+sizeof(TBlockSize)+size);
00158                 *(TBlockSize*)&(_ReadyToSendBuffer[oldBufferSize])=netlen;
00159 
00160                 // Append the temporary buffer to the global buffer
00161                 CFastMem::memcpy (&_ReadyToSendBuffer[oldBufferSize+sizeof(TBlockSize)], tmpbuffer, size);
00162                 SendFifo.pop();
00163         }
00164 
00165         // Actual sending of _ReadyToSendBuffer
00166         //if ( ! _ReadyToSendBuffer.empty() )
00167         if ( _ReadyToSendBuffer.size() != 0 )
00168         {               
00169                 // Send
00170                 CSock::TSockResult res;
00171                 TBlockSize len = _ReadyToSendBuffer.size() - _RTSBIndex;
00172 
00173                 res = Sock->send( _ReadyToSendBuffer.getPtr()+_RTSBIndex, len, false );
00174 
00175                 if ( res == CSock::Ok )
00176                 {
00177 /*                      // Debug display
00178                         switch ( _FlushTrigger )
00179                         {
00180                         case FTTime : nldebug( "LNETL1: Time triggered flush for %s:", asString().c_str() ); break;
00181                         case FTSize : nldebug( "LNETL1: Size triggered flush for %s:", asString().c_str() ); break;
00182                         default:          nldebug( "LNETL1: Manual flush for %s:", asString().c_str() );
00183                         }
00184                         _FlushTrigger = FTManual;
00185                         nldebug( "LNETL1: %s sent effectively a buffer (%d on %d B)", asString().c_str(), len, _ReadyToSendBuffer.size() );
00186 */                      
00187                         
00188                         // TODO OPTIM remove the nldebug for speed
00189                         //commented for optimisation nldebug( "LNETL1: %s sent effectively %u/%u bytes (pos %u wantedsend %u)", asString().c_str(), len, _ReadyToSendBuffer.size(), _RTSBIndex, realLen/*, stringFromVectorPart(_ReadyToSendBuffer,_RTSBIndex,len).c_str()*/ );
00190 
00191                         if ( _RTSBIndex+len == _ReadyToSendBuffer.size() ) // for non-blocking mode (server)
00192                         {
00193                                 // If sending is ok, clear the global buffer
00194                                 _ReadyToSendBuffer.clear();
00195                                 _RTSBIndex = 0;
00196                         }
00197                         else
00198                         {
00199                                 // Or clear only the data that was actually sent
00200                                 nlassertex( _RTSBIndex+len < _ReadyToSendBuffer.size(), ("index=%u len=%u size=%u", _RTSBIndex, len, _ReadyToSendBuffer.size()) );
00201                                 _RTSBIndex += len;
00202                                 if ( _ReadyToSendBuffer.size() > 20480 ) // if big, clear data already sent
00203                                 {
00204                                         uint nbcpy = _ReadyToSendBuffer.size() - len;
00205                                         for (uint i = 0; i < nbcpy; i++)
00206                                         {
00207                                                 _ReadyToSendBuffer[i] = _ReadyToSendBuffer[i+len];
00208                                         }
00209                                         _ReadyToSendBuffer.resize(nbcpy);
00210                                         //_ReadyToSendBuffer.erase( _ReadyToSendBuffer.begin(), _ReadyToSendBuffer.begin()+len );
00211                                         _RTSBIndex = 0;
00212                                 }
00213                         }
00214                 }
00215                 else
00216                 {
00217 #ifdef NL_DEBUG
00218                         // can happen in a normal behavior if, for example, the other side is not connect anymore
00219                         nldebug( "LNETL1: %s failed to send effectively a buffer of %d bytes", asString().c_str(), _ReadyToSendBuffer.size() );
00220 #endif
00221                         return false;
00222                 }
00223         }
00224 
00225         return true;
00226 }
00227 
00228 
00229 /* Sets the time flush trigger (in millisecond). When this time is elapsed,
00230  * all data in the send queue is automatically sent (-1 to disable this trigger)
00231  */
00232 void CBufSock::setTimeFlushTrigger( sint32 ms )
00233 {
00234         nlassert (this != InvalidSockId);       // invalid bufsock
00235         _TriggerTime = ms;
00236         _LastFlushTime = CTime::getLocalTime();
00237 }
00238 
00239 
00240 /*
00241  * Update the network sending (call this method evenly). Returns false if an error occured.
00242  */
00243 bool CBufSock::update()
00244 {
00245         nlassert (this != InvalidSockId);       // invalid bufsock
00246 //      nlnettrace( "CBufSock::update-BEGIN" );
00247         // Time trigger
00248 
00249         if ( _TriggerTime != -1 )
00250         {
00251                 TTime now = CTime::getLocalTime();
00252                 if ( (sint32)(now-_LastFlushTime) >= _TriggerTime )
00253                 {
00254 #ifdef NL_DEBUG
00255                         _FlushTrigger = FTTime;
00256 #endif
00257                         if ( flush() )
00258                         {
00259                                 _LastFlushTime = now;
00260 //                              nlnettrace ( "CBufSock::update-END time 1" );
00261                                 return true;
00262                         }
00263                         else
00264                         {
00265 //                              nlnettrace ( "CBufSock::update-END time 0" );
00266                                 return false;
00267                         }
00268                 }
00269         }
00270         // Size trigger
00271         if ( _TriggerSize != -1 )
00272         {
00273                 if ( (sint32)SendFifo.size() > _TriggerSize )
00274                 {
00275 #ifdef NL_DEBUG
00276                         _FlushTrigger = FTSize;
00277 #endif
00278 //                      nlnettrace( "CBufSock::update-END size" );
00279                         return flush();
00280                 }
00281         }
00282 //      nlnettrace( "CBufSock::update-END nosend" );
00283         return true;
00284 }
00285 
00286 
00287 /*
00288  * Connects to the specified addr; set connectedstate to true if no connection advertising is needed
00289  * Precond: not connected
00290  */
00291 void CBufSock::connect( const CInetAddress& addr, bool nodelay, bool connectedstate )
00292 {
00293         nlassert (this != InvalidSockId);       // invalid bufsock
00294         nlassert( ! Sock->connected() );
00295 
00296         SendNextValue = ReceiveNextValue = 0;
00297         
00298         Sock->connect( addr );
00299         _ConnectedState = connectedstate;
00300         _KnowConnected = connectedstate;
00301         if ( nodelay )
00302         {
00303                 Sock->setNoDelay( true );
00304         }
00305 }
00306 
00307 
00308 /*
00309  * Disconnects; set connectedstate to false if no disconnection advertising is needed
00310  */
00311 void CBufSock::disconnect( bool connectedstate )
00312 {
00313         nlassert (this != InvalidSockId);       // invalid bufsock
00314         Sock->disconnect();
00315         _ConnectedState = connectedstate;
00316         _KnowConnected = connectedstate;
00317 
00318         SendNextValue = ReceiveNextValue = 0;
00319 }
00320 
00321 
00322 /*
00323  * Returns a string with the characteristics of the object
00324  */
00325 string CBufSock::asString() const
00326 {
00327         stringstream ss;
00328         if (this == InvalidSockId) // tricky
00329                 ss << "<Null>";
00330         else
00331         {
00332                 // if it crashs here, it means that the CBufSock was deleted and you try to access to the virtual table that is empty
00333                 // because the object is destroyed.
00334                 ss << typeStr();
00335                 ss << hex << this << dec << " (socket ";
00336                 
00337                 if (Sock == NULL)
00338                         ss << "<Null>";
00339                 else
00340                         ss << Sock->descriptor();
00341 
00342                 ss << ")";
00343         }
00344         return ss.str();
00345 }
00346 
00347 
00348 /*
00349  * Constructor with an existing socket (created by an accept())
00350  */
00351 CServerBufSock::CServerBufSock( CTcpSock *sock ) :
00352         CBufSock( sock ),
00353         _Advertised( false ),
00354         _NowReadingBuffer( false ),
00355         _BytesRead( 0 ),
00356         _Length( 0 ),
00357         _OwnerTask( NULL )
00358 {
00359         nlassert (this != InvalidSockId);       // invalid bufsock
00360         nlnettrace( "CServerBufSock::CServerBufSock" );
00361 }
00362 
00363 
00364 // In Receive Threads:
00365 
00366 
00367 /*
00368  * Receives a part of a message (nonblocking socket only)
00369  */
00370 bool CServerBufSock::receivePart()
00371 {
00372         nlassert (this != InvalidSockId);       // invalid bufsock
00373         nlnettrace( "CServerBufSock::receivePart" );
00374 
00375         TBlockSize actuallen;
00376         if ( ! _NowReadingBuffer )
00377         {
00378                 // Receiving length prefix
00379                 actuallen = sizeof(_Length)-_BytesRead;
00380                 Sock->receive( (uint8*)(&_Length)+_BytesRead, actuallen );
00381                 _BytesRead += actuallen;
00382                 if ( _BytesRead == sizeof(_Length ) )
00383                 {
00384                         if ( _Length != 0 )
00385                         {
00386                                 _Length = ntohl( _Length );
00387 
00388                                 // Test size limit
00389                                 if ( _Length > _OwnerTask->server()->maxExpectedBlockSize() )
00390                                 {
00391                                         nlwarning( "LNETL1: Socket %s received length exceeding max expected, in block header... Disconnecting", asString().c_str() );
00392                                         throw ESocket( "Received length exceeding max expected", false );
00393                                 }
00394 
00395                                 _NowReadingBuffer = true;
00396                                 _ReceiveBuffer.resize( _Length );
00397                         }
00398                         else
00399                         {
00400                                 nlwarning( "LNETL1: Socket %s received null length in block header", asString().c_str() );
00401                         }
00402                         _BytesRead = 0;
00403                 }
00404         }
00405 
00406         if ( _NowReadingBuffer )
00407         {
00408                 // Receiving payload buffer
00409                 actuallen = _Length-_BytesRead;
00410                 Sock->receive( &*_ReceiveBuffer.begin()+_BytesRead, actuallen );
00411                 _BytesRead += actuallen;
00412 
00413                 if ( _BytesRead == _Length )
00414                 {
00415 #ifdef NL_DEBUG
00416                         nldebug( "LNETL1: %s received buffer (%u bytes): [%s]", asString().c_str(), _ReceiveBuffer.size(), stringFromVector(_ReceiveBuffer).c_str() );
00417 #endif
00418                         _NowReadingBuffer = false;
00419                         _BytesRead = 0;
00420                         return true;
00421                 }
00422         }
00423 
00424         return false;
00425 }
00426 
00427 } // NLNET