1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/misc/hierarchical_timer.h"
23 #include "nel/misc/variable.h"
24 #include "nel/net/buf_sock.h"
25 #include "nel/net/buf_server.h"
26 #include "nel/net/net_log.h"
31 # ifndef NL_COMP_MINGW
35 #elif defined NL_OS_UNIX
36 # include <netinet/in.h>
39 using namespace NLMISC
;
42 NLMISC::CVariable
<uint32
> MaxTCPPacketSize("nel", "MaxTCPPacketSize", "Maximum size of TCP packets created by cumulating small packets", 10240, 0, true);
48 NLMISC::CMutex
nettrace_mutex("nettrace_mutex");
54 CBufSock::CBufSock( CTcpSock
*sock
) :
56 _KnowConnected( false ),
62 _ConnectedState( false )
64 nlnettrace( "CBufSock::CBufSock" ); // don't define a global object
68 Sock
= new CTcpSock();
72 _FlushTrigger
= FTManual
;
74 _LastFlushTime
= CTime::getLocalTime();
83 nlassert (this != InvalidSockId
); // invalid bufsock
85 nlnettrace( "CBufSock::~CBufSock" );
87 delete Sock
; // the socket disconnects automatically if needed
89 // destroy the structur to be sure that other people will not access to this anymore
90 AuthorizedCallback
.clear();
92 _KnowConnected
= false;
96 _ReadyToSendBuffer
.clear ();
99 _ConnectedState
= false;
104 * Returns a readable string from a vector of bytes, beginning from pos, limited to 'len' characters. '\0' are replaced by ' '
106 string
stringFromVectorPart( const vector
<uint8
>& v
, uint32 pos
, uint32 len
)
108 nlassertex( pos
+len
<= v
.size(), ("pos=%u len=%u size=%u", pos
, len
, v
.size()) );
111 if ( (! v
.empty()) && (len
!=0) )
115 memcpy( &*s
.begin(), &*v
.begin()+pos
, len
);
117 // Replace '\0' characters
119 for ( is
=s
.begin(); is
!=s
.end(); ++is
)
121 if ( ! isprint((uint8
)(*is
)) || (*is
) == '%' )
133 * Force to send data pending in the send queue now. In the case of a non-blocking socket
134 * (see CNonBlockingBufSock), if all the data could not be sent immediately,
135 * the returned nbBytesRemaining value is non-zero.
136 * \param nbBytesRemaining If the pointer is not NULL, the method sets the number of bytes still pending after the flush attempt.
137 * \returns False if an error has occurred (e.g. the remote host is disconnected).
138 * To retrieve the reason of the error, call CSock::getLastError() and/or CSock::errorString()
140 * Note: this method works with both blocking and non-blocking sockets
141 * Precond: the send queue should not contain an empty block
143 bool CBufSock::flush( uint
*nbBytesRemaining
)
145 nlassert (this != InvalidSockId
); // invalid bufsock
146 //nlnettrace( "CBufSock::flush" );
148 // Copy data from the send queue to _ReadyToSendBuffer
150 // vector<uint8> tmpbuffer;
154 // Process each element in the send queue
155 uint8
*tmpbuffer
= NULL
;
157 if (! SendFifo
.empty())
159 SendFifo
.front( tmpbuffer
, size
);
161 while ( ! SendFifo
.empty() && ( _ReadyToSendBuffer
.empty() || (_ReadyToSendBuffer
.size() +size
< MaxTCPPacketSize
) ) )
163 // Compute the size and add it into the beginning of the buffer
164 netlen
= htonl( (TBlockSize
)size
);
165 uint32 oldBufferSize
= _ReadyToSendBuffer
.size();
166 _ReadyToSendBuffer
.resize (oldBufferSize
+sizeof(TBlockSize
)+size
);
167 *(TBlockSize
*)&(_ReadyToSendBuffer
[oldBufferSize
])=netlen
;
168 //nldebug( "O-%u %u+L%u (0x%x)", Sock->descriptor(), oldBufferSize, size, size );
171 // Append the temporary buffer to the global buffer
172 CFastMem::memcpy (&_ReadyToSendBuffer
[oldBufferSize
+sizeof(TBlockSize
)], tmpbuffer
, size
);
174 if (! SendFifo
.empty())
176 SendFifo
.front( tmpbuffer
, size
);
180 // Actual sending of _ReadyToSendBuffer
181 //if ( ! _ReadyToSendBuffer.empty() )
182 if ( _ReadyToSendBuffer
.size() != 0 )
185 CSock::TSockResult res
;
186 TBlockSize len
= _ReadyToSendBuffer
.size() - _RTSBIndex
;
188 res
= Sock
->send( _ReadyToSendBuffer
.getPtr()+_RTSBIndex
, len
, false );
190 if ( res
== CSock::Ok
)
193 switch ( _FlushTrigger )
195 case FTTime : LNETL1_DEBUG( "LNETL1: Time triggered flush for %s:", asString().c_str() ); break;
196 case FTSize : LNETL1_DEBUG( "LNETL1: Size triggered flush for %s:", asString().c_str() ); break;
197 default: LNETL1_DEBUG( "LNETL1: Manual flush for %s:", asString().c_str() );
199 _FlushTrigger = FTManual;
200 LNETL1_DEBUG( "LNETL1: %s sent effectively a buffer (%d on %d B)", asString().c_str(), len, _ReadyToSendBuffer.size() );
203 // TODO OPTIM remove the nldebug for speed
204 //commented for optimisation LNETL1_DEBUG( "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()*/ );
206 if ( _RTSBIndex
+len
== _ReadyToSendBuffer
.size() ) // for non-blocking mode
208 // If sending is ok, clear the global buffer
209 //nldebug( "O-%u all %u bytes (%u to %u) sent", Sock->descriptor(), len, _RTSBIndex, _ReadyToSendBuffer.size() );
210 _ReadyToSendBuffer
.clear();
212 if ( nbBytesRemaining
)
213 *nbBytesRemaining
= 0;
217 // Or clear only the data that was actually sent
218 nlassertex( _RTSBIndex
+len
< _ReadyToSendBuffer
.size(), ("index=%u len=%u size=%u", _RTSBIndex
, len
, _ReadyToSendBuffer
.size()) );
219 //nldebug( "O-%u only %u B on %u (%u to %u) sent", Sock->descriptor(), len, _ReadyToSendBuffer.size()-_RTSBIndex, _RTSBIndex, _ReadyToSendBuffer.size() );
221 if ( nbBytesRemaining
)
222 *nbBytesRemaining
= _ReadyToSendBuffer
.size() - _RTSBIndex
;
223 if ( _ReadyToSendBuffer
.size() > 20480 ) // if big, clear data already sent
225 uint nbcpy
= _ReadyToSendBuffer
.size() - _RTSBIndex
;
226 for (uint i
= 0; i
< nbcpy
; i
++)
228 _ReadyToSendBuffer
[i
] = _ReadyToSendBuffer
[i
+_RTSBIndex
];
230 _ReadyToSendBuffer
.resize(nbcpy
);
231 //_ReadyToSendBuffer.erase( _ReadyToSendBuffer.begin(), _ReadyToSendBuffer.begin()+_RTSBIndex );
233 //nldebug( "O-%u Cleared data already sent, %u B remain", Sock->descriptor(), nbcpy );
240 // Can happen in a normal behavior if, for example, the other side is not connected anymore
241 LNETL1_DEBUG( "LNETL1: %s failed to send effectively a buffer of %d bytes", asString().c_str(), _ReadyToSendBuffer
.size() );
243 // NEW: Clearing (loosing) the buffer if the sending can't be performed at all
244 _ReadyToSendBuffer
.clear();
246 if ( nbBytesRemaining
)
247 *nbBytesRemaining
= 0;
253 if ( nbBytesRemaining
)
254 *nbBytesRemaining
= 0;
258 while ( !SendFifo
.empty() && _ReadyToSendBuffer
.empty() );
264 /* Sets the time flush trigger (in millisecond). When this time is elapsed,
265 * all data in the send queue is automatically sent (-1 to disable this trigger)
267 void CBufSock::setTimeFlushTrigger( sint32 ms
)
269 nlassert (this != InvalidSockId
); // invalid bufsock
271 _LastFlushTime
= CTime::getLocalTime();
276 * Update the network sending (call this method evenly). Returns false if an error occurred.
278 bool CBufSock::update()
280 nlassert (this != InvalidSockId
); // invalid bufsock
281 // nlnettrace( "CBufSock::update-BEGIN" );
284 if ( _TriggerTime
!= -1 )
286 TTime now
= CTime::getLocalTime();
287 if ( (sint32
)(now
-_LastFlushTime
) >= _TriggerTime
)
290 _FlushTrigger
= FTTime
;
294 _LastFlushTime
= now
;
295 // nlnettrace ( "CBufSock::update-END time 1" );
300 // nlnettrace ( "CBufSock::update-END time 0" );
306 if ( _TriggerSize
!= -1 )
308 if ( (sint32
)SendFifo
.size() > _TriggerSize
)
311 _FlushTrigger
= FTSize
;
313 // nlnettrace( "CBufSock::update-END size" );
317 // nlnettrace( "CBufSock::update-END nosend" );
323 * Connects to the specified addr; set connectedstate to true if no connection advertising is needed
324 * Precond: not connected
326 void CBufSock::connect( const CInetAddress
& addr
, bool nodelay
, bool connectedstate
)
328 nlassert (this != InvalidSockId
); // invalid bufsock
329 nlassert( ! Sock
->connected() );
331 Sock
->connect( addr
);
332 _ConnectedState
= connectedstate
;
333 _KnowConnected
= connectedstate
;
336 Sock
->setNoDelay( true );
338 _ReadyToSendBuffer
.clear();
344 * Disconnects; set connectedstate to false if no disconnection advertising is needed
346 void CBufSock::disconnect( bool connectedstate
)
348 nlassert (this != InvalidSockId
); // invalid bufsock
350 _ConnectedState
= connectedstate
;
351 _KnowConnected
= connectedstate
;
356 * Returns a string with the characteristics of the object
358 string
CBufSock::asString() const
362 if (this == InvalidSockId
) // tricky
366 // if it crashs here, it means that the CBufSock was deleted and you try to access to the virtual table that is empty
367 // because the object is destroyed.
369 str
+= NLMISC::toStringPtr(this) + " (socket ";
374 str
+= NLMISC::toString(Sock
->descriptor());
385 CNonBlockingBufSock::CNonBlockingBufSock( CTcpSock
*sock
, uint32 maxExpectedBlockSize
) :
387 _MaxExpectedBlockSize( maxExpectedBlockSize
),
388 _NowReadingBuffer( false ),
392 nlnettrace( "CNonBlockingBufSock::CNonBlockingBufSock" );
397 * Constructor with an existing socket (created by an accept())
399 CServerBufSock::CServerBufSock( CTcpSock
*sock
) :
400 CNonBlockingBufSock( sock
),
401 _Advertised( false ),
404 nlassert (this != InvalidSockId
); // invalid bufsock
405 nlnettrace( "CServerBufSock::CServerBufSock" );
409 // In Receive Threads:
413 * Receives a part of a message (nonblocking socket only)
415 bool CNonBlockingBufSock::receivePart( uint32 nbExtraBytes
)
417 nlassert (this != InvalidSockId
); // invalid bufsock
418 nlnettrace( "CNonBlockingBufSock::receivePart" );
420 TBlockSize actuallen
;
421 if ( ! _NowReadingBuffer
)
423 // Receiving length prefix
424 actuallen
= sizeof(_Length
)-_BytesRead
;
425 CSock :: TSockResult ret
= Sock
->receive( (uint8
*)(&_Length
)+_BytesRead
, actuallen
, false );
426 if (ret
== CSock::ConnectionClosed
)
428 LNETL1_DEBUG( "LNETL1: Connection %s closed", asString().c_str() );
431 else if (ret
== CSock::Error
)
433 LNETL1_DEBUG( "LNETL1: Socket error for %s", asString().c_str() );
438 _BytesRead
+= actuallen
;
439 if ( _BytesRead
== sizeof(_Length
) )
443 _Length
= ntohl( _Length
);
444 //nldebug( "I-%u L%u (0x%x) a%u", Sock->descriptor(), _Length, _Length, actuallen );
447 if ( _Length
> _MaxExpectedBlockSize
)
449 nlwarning( "LNETL1: Socket %s received header length %u exceeding max expected %u... Disconnecting", asString().c_str(), _Length
, _MaxExpectedBlockSize
);
450 throw ESocket( toString( "Received length %u exceeding max expected %u from %s", _Length
, _MaxExpectedBlockSize
, Sock
->remoteAddr().asString().c_str() ).c_str(), false );
453 _NowReadingBuffer
= true;
454 _ReceiveBuffer
.resize( _Length
+ nbExtraBytes
);
458 nlwarning( "LNETL1: Socket %s received null length in block header", asString().c_str() );
464 if ( _NowReadingBuffer
)
466 // Receiving payload buffer
467 actuallen
= _Length
-_BytesRead
;
468 Sock
->receive( &*_ReceiveBuffer
.begin()+_BytesRead
, actuallen
);
469 _BytesRead
+= actuallen
;
471 if ( _BytesRead
== _Length
)
474 LNETL1_DEBUG( "LNETL1: %s received buffer (%u bytes): [%s]", asString().c_str(), _ReceiveBuffer
.size(), stringFromVector(_ReceiveBuffer
).c_str() );
476 _NowReadingBuffer
= false;
477 //nldebug( "I-%u all %u B on %u", Sock->descriptor(), actuallen );
483 // nldebug( "I-%u only %u B on %u", actuallen, Sock->descriptor(), _Length-(_BytesRead-actuallen) );