Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / net / sock.cpp
blob1638a63dc8dca6016cfd9c588445d868443ee657
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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/>.
20 #include "stdnet.h"
22 #include "nel/net/sock.h"
23 #include "nel/net/net_log.h"
24 #include "nel/misc/time_nl.h"
25 #include "nel/misc/hierarchical_timer.h"
27 #ifdef NL_OS_WINDOWS
28 # ifndef NL_COMP_MINGW
29 # define NOMINMAX
30 # endif
31 # include <winsock2.h>
32 # include <windows.h>
33 # define socklen_t int
34 # define ERROR_NUM WSAGetLastError()
35 # define ERROR_WOULDBLOCK WSAEWOULDBLOCK
37 #elif defined NL_OS_UNIX
39 # include <unistd.h>
40 # include <sys/types.h>
41 # include <sys/time.h>
42 # include <sys/socket.h>
43 # include <sys/ioctl.h>
44 # include <netinet/in.h>
45 # include <netinet/tcp.h>
46 # include <arpa/inet.h>
47 # include <netdb.h>
48 # include <fcntl.h>
49 # include <cerrno>
51 # define SOCKET_ERROR -1
52 # define INVALID_SOCKET -1
53 # define ERROR_NUM errno
54 # define ERROR_WOULDBLOCK EWOULDBLOCK
55 # define ERROR_MSG strerror(errno)
57 // BSD compatible constant
58 # ifndef FNDELAY
59 # define FNDELAY O_NDELAY
60 # endif
62 typedef int SOCKET;
64 #endif
66 using namespace std;
67 using namespace NLMISC;
69 #ifdef DEBUG_NEW
70 #define new DEBUG_NEW
71 #endif
73 namespace NLNET {
76 bool CSock::_Initialized = false;
80 * ESocket constructor
82 ESocket::ESocket( const char *reason, bool systemerror, CInetAddress *addr )
84 /*it doesnt work on linux, should do something more cool
85 std::stringstream ss;
86 ss << "Socket error: " << reason;
87 if ( systemerror )
89 ss << " (" << ERROR_NUM;
90 #ifdef NL_OS_UNIX
91 ss << ": " << ERROR_MSG;
92 #endif
93 ss << ") " << std::endl;
95 _Reason = ss.str();
97 _Reason = "Socket error: ";
98 uint errornum = CSock::getLastError();
99 char str[256];
100 if ( addr != NULL )
102 // Version with address
103 smprintf( str, 256, reason, addr->asString().c_str() ); // reason *must* contain "%s"
104 _Reason += str;
106 else
108 // Version without address
109 _Reason += reason;
111 if ( systemerror )
113 _Reason += " (";
114 smprintf( str, 256, "%d", errornum );
115 _Reason += str;
116 if ( errornum != 0 )
118 _Reason += ": ";
119 _Reason += CSock::errorString( errornum );
121 _Reason += ")";
123 LNETL0_INFO( "LNETL0: Exception will be launched: %s", _Reason.c_str() );
129 * Initializes the network engine if it is not already done (under Windows, calls WSAStartup()).
131 void CSock::initNetwork()
133 if ( ! CSock::_Initialized )
135 #ifdef NL_OS_WINDOWS
136 WORD winsock_version = MAKEWORD( 2, 0 );
137 WSADATA wsaData;
138 if ( WSAStartup( winsock_version, &wsaData ) != 0 )
140 throw ESocket( "Winsock initialization failed" );
142 #endif
143 CSock::_Initialized = true;
148 * Releases the network engine
150 void CSock::releaseNetwork()
152 #ifdef NL_OS_WINDOWS
153 WSACleanup();
154 #endif
155 CSock::_Initialized = false;
159 /* Returns the code of the last error that has occurred.
160 * Note: This code is platform-dependant. On Unix, it is errno; on Windows it is the Winsock error code.
161 * See also errorString()
163 uint CSock::getLastError()
165 return (uint)ERROR_NUM;
170 * Returns a string explaining the network error (see getLastError())
172 std::string CSock::errorString( uint errorcode )
174 #ifdef NL_OS_WINDOWS
175 switch( errorcode )
177 case WSAEINTR /*10004*/: return "Blocking operation interrupted";
178 case WSAEINVAL /*10022*/: return "Invalid socket (maybe not bound) or argument";
179 case WSAEMFILE /*10024*/: return "Too many open sockets";
180 case WSAENOTSOCK /*10038*/: return "Socket operation on nonsocket (maybe invalid select descriptor)";
181 case WSAEMSGSIZE /*10040*/: return "Message too long";
182 case WSAEADDRINUSE /*10048*/: return "Address already in use (is this service already running in this computer?)";
183 case WSAEADDRNOTAVAIL/*10049*/: return "Address not available";
184 case WSAENETDOWN /*10050*/: return "Network is down";
185 case WSAENETUNREACH /*10051*/: return "Network is unreachable";
186 case WSAECONNRESET /*10054*/: return "Connection reset by peer";
187 case WSAENOBUFS /*10055*/: return "No buffer space available; please close applications or reboot";
188 case WSAESHUTDOWN /*10058*/: return "Cannot send/receive after socket shutdown";
189 case WSAETIMEDOUT /*10060*/: return "Connection timed-out";
190 case WSAECONNREFUSED /*10061*/: return "Connection refused, the server may be offline";
191 case WSAEHOSTUNREACH /*10065*/: return "Remote host is unreachable";
192 case WSANOTINITIALISED /*093*/: return "'Windows Sockets' not initialized";
193 default: return "";
195 #elif defined NL_OS_UNIX
196 return std::string( strerror( errorcode ) );
197 #endif
204 * Constructor
206 CSock::CSock( bool logging ) :
207 _Sock( INVALID_SOCKET ),
208 _Logging( logging ),
209 _NonBlocking( false ),
210 _BytesReceived( 0 ),
211 _BytesSent( 0 ),
212 _TimeoutS( 0 ),
213 _TimeoutUs( 0 ),
214 _MaxReceiveTime( 0 ),
215 _MaxSendTime( 0 ),
216 _Blocking( false )
218 nlassert( CSock::_Initialized );
220 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
221 sync.value() = false;
223 _Connected = false;
228 * Construct a CSock object using an existing connected socket descriptor and its associated remote address
230 CSock::CSock( SOCKET sock, const CInetAddress& remoteaddr ) :
231 _Sock( sock ),
232 _RemoteAddr( remoteaddr ),
233 _Logging( true ),
234 _NonBlocking( false ),
235 _BytesReceived( 0 ),
236 _BytesSent( 0 ),
237 _MaxReceiveTime( 0 ),
238 _MaxSendTime( 0 )
240 nlassert( CSock::_Initialized );
242 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
243 sync.value() = true;
245 _Connected = true;
247 // Check remote address
248 if ( ! _RemoteAddr.isValid() )
250 throw ESocket( "Could not init a socket object with an invalid address", false );
253 // Get local socket name
254 setLocalAddress();
256 #ifdef NL_OS_UNIX
257 // We set the close-on-exec flag on the socket to be sure that when
258 // we call the exec() to spawn an application in the AES for example,
259 // that the AES listen socket will be close and not given to the child.
260 // From google:
261 // Manipulate the close-on-exec flag to determine if a file descriptor
262 // should be closed as part of the normal processing of the exec subroutine.
263 // If the flag is set, the file descriptor is closed.
264 // If the flag is clear, the file descriptor is left open
265 ioctl(_Sock, FIOCLEX, NULL);
266 // fcntl should be more portable but not tested fcntl(_Sock, F_SETFD, FD_CLOEXEC);
267 #endif
272 * Creates the socket and get a valid descriptor
274 void CSock::createSocket( int type, int protocol )
276 nlassert( _Sock == INVALID_SOCKET );
278 _Sock = (SOCKET)socket( AF_INET, type, protocol ); // or IPPROTO_IP (=0) ?
279 if ( _Sock == INVALID_SOCKET )
281 throw ESocket( "Socket creation failed" );
284 if ( _Logging )
286 // LNETL0_DEBUG( "LNETL0: Socket %d open (TCP)", _Sock );
289 #ifdef NL_OS_UNIX
290 // We set the close-on-exec flag on the socket to be sure that when
291 // we call the exec() to spawn an application in the AES for example,
292 // that the AES listen socket will be close and not given the to child.
293 // From google:
294 // Manipulate the close-on-exec flag to determine if a file descriptor
295 // should be closed as part of the normal processing of the exec subroutine.
296 // If the flag is set, the file descriptor is closed.
297 // If the flag is clear, the file descriptor is left open
298 ioctl(_Sock, FIOCLEX, NULL);
299 // fcntl should be more portable but not tested fcntl(_Sock, F_SETFD, FD_CLOEXEC);
300 #endif
306 * Closes the listening socket
308 void CSock::close()
310 if ( _Logging )
312 LNETL0_DEBUG( "LNETL0: Socket %d closing for %s at %s", _Sock, _RemoteAddr.asString().c_str(), _LocalAddr.asString().c_str() );
314 SOCKET sockToClose = _Sock;
315 // preset to invalid to bypass exception in listen thread
316 _Sock = INVALID_SOCKET;
317 #ifdef NL_OS_WINDOWS
318 closesocket( sockToClose );
319 #elif defined NL_OS_UNIX
320 ::close( sockToClose );
321 #endif
322 _Connected = false;
327 * Destructor
329 CSock::~CSock()
331 //nlinfo( "Report for %s socket %s: Max send time: %u Max recv time: %u", _NonBlocking?"non-blocking":"blocking", remoteAddr().asString().c_str(), _MaxSendTime, _MaxReceiveTime );
332 //nlinfo( "Max send time: %u", _MaxSendTime);
333 if ( _Sock != INVALID_SOCKET )
335 if ( _Logging )
337 LNETL0_DEBUG( "LNETL0: Socket %d closing for %s at %s", _Sock, _RemoteAddr.asString().c_str(), _LocalAddr.asString().c_str() );
340 if ( connected() )
342 #ifdef NL_OS_WINDOWS
343 shutdown( _Sock, SD_BOTH );
345 closesocket( _Sock );
346 #elif defined NL_OS_UNIX
347 shutdown( _Sock, SHUT_RDWR );
349 ::close( _Sock );
350 #endif
351 _Sock = INVALID_SOCKET;
357 * Connection
359 void CSock::connect( const CInetAddress& addr )
361 LNETL0_DEBUG( "LNETL0: Socket %d connecting to %s...", _Sock, addr.asString().c_str() );
363 // Check address
364 if ( ! addr.isValid() )
366 throw ESocket( "Unable to connect to invalid address", false );
369 #ifndef NL_OS_WINDOWS
370 // Set Reuse Address On (does not work on Win98 and is useless on Win2000)
371 int value = true;
372 if ( setsockopt( _Sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value) ) == SOCKET_ERROR )
374 throw ESocket( "ReuseAddr failed" );
376 #endif
378 // Connection (when _Sock is a datagram socket, connect establishes a default destination address)
379 if ( ::connect( _Sock, (const sockaddr *)(addr.sockAddr()), sizeof(sockaddr_in) ) != 0 )
381 /* if ( _Logging )
383 #ifdef NL_OS_WINDOWS
384 nldebug( "Impossible to connect socket %d to %s %s (%d)", _Sock, addr.hostName().c_str(), addr.asIPString().c_str(), ERROR_NUM );
385 #elif defined NL_OS_UNIX
386 nldebug( "Impossible to connect socket %d to %s %s (%d:%s)", _Sock, addr.hostName().c_str(), addr.asIPString().c_str(), ERROR_NUM, strerror(ERROR_NUM) );
387 #endif
391 throw ESocketConnectionFailed( addr );
393 setLocalAddress();
394 if ( _Logging )
396 LNETL0_DEBUG( "LNETL0: Socket %d connected to %s (local %s)", _Sock, addr.asString().c_str(), _LocalAddr.asString().c_str() );
398 _RemoteAddr = addr;
400 _BytesReceived = 0;
401 _BytesSent = 0;
403 /*CSynchronized<bool>::CAccessor sync( &_SyncConnected );
404 sync.value() = true;*/
405 _Connected = true;
410 * Checks if there is some data to receive
412 bool CSock::dataAvailable()
414 fd_set fdset;
415 FD_ZERO( &fdset );
416 FD_SET( _Sock, &fdset );
417 timeval tv;
418 tv.tv_sec = _TimeoutS;
419 tv.tv_usec = _TimeoutUs;
421 // Test for message received.
422 int res = select( _Sock+1, &fdset, NULL, NULL, &tv );
423 switch ( res )
425 case 0 : return false;
426 case -1 : throw ESocket( "CSock::dataAvailable(): select failed" );
427 default : return true;
433 * Sets the local address
435 void CSock::setLocalAddress()
437 sockaddr saddr;
438 socklen_t saddrlen = sizeof(saddr);
439 if ( getsockname( _Sock, &saddr, &saddrlen ) != 0 )
441 throw ESocket( "Unable to find local address" );
443 _LocalAddr.setSockAddr( (const sockaddr_in *)&saddr );
448 * Sends data, or returns false if it would block
450 CSock::TSockResult CSock::send( const uint8 *buffer, uint32& len, bool throw_exception )
452 TTicks before = CTime::getPerformanceTime();
453 len = ::send( _Sock, (const char*)buffer, len, 0 );
454 _MaxSendTime = max( (uint32)(CTime::ticksToSecond(CTime::getPerformanceTime()-before)*1000.0f), _MaxSendTime );
456 // nldebug ("CSock::send(): Sent %d bytes to %d (%d)", len, _Sock, ERROR_NUM);
458 if ( _Logging )
460 // LNETL0_DEBUG ("LNETL0: CSock::send(): Sent %d bytes to %d res: %d (%d)", realLen, _Sock, len, ERROR_NUM);
463 if ( ((int)len) == SOCKET_ERROR )
465 if ( ERROR_NUM == ERROR_WOULDBLOCK )
467 H_AUTO(L0SendWouldBlock);
468 len = 0;
469 //nlSleep(10);
470 if (!_Blocking)
472 //nldebug("SendWouldBlock - %s / %s Entering snooze mode",_LocalAddr.asString().c_str(),_RemoteAddr.asString().c_str());
473 _Blocking= true;
475 return Ok;
477 if ( throw_exception )
479 #ifdef NL_OS_WINDOWS
480 throw ESocket( NLMISC::toString( "Unable to send data: error %u", GetLastError() ).c_str() );
481 #else
482 throw ESocket( "Unable to send data" );
483 #endif
485 return Error;
487 _BytesSent += len;
489 if (_Blocking)
491 //nldebug("SendWouldBlock - %s / %s Leaving snooze mode",_LocalAddr.asString().c_str(),_RemoteAddr.asString().c_str());
492 _Blocking= false;
494 return Ok;
500 * Receives data
502 CSock::TSockResult CSock::receive( uint8 *buffer, uint32& len, bool throw_exception )
504 if ( _NonBlocking )
506 // Receive incoming message (only the received part)
507 TTicks before = CTime::getPerformanceTime();
509 sint retLen = ::recv( _Sock, (char*)buffer, len, 0 );
511 //nlinfo ("CSock::receive(): NBM Received %d bytes to %d res: %d (%d)", realLen, _Sock, len, ERROR_NUM);
513 if ( _Logging )
515 // LNETL0_DEBUG ("LNETL0: CSock::receive(): NBM Received %d bytes to %d res: %d (%d)", realLen, _Sock, len, ERROR_NUM);
518 _MaxReceiveTime = max( (uint32)(CTime::ticksToSecond(CTime::getPerformanceTime()-before)*1000.0f), _MaxReceiveTime );
520 switch (retLen)
522 // Graceful disconnection
523 case 0 :
526 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
527 sync.value() = false;
529 _Connected = false;
530 if ( throw_exception )
532 throw ESocketConnectionClosed();
534 return CSock::ConnectionClosed;
537 // Socket error or call would block
538 case SOCKET_ERROR :
540 len = 0;
541 if ( ERROR_NUM == ERROR_WOULDBLOCK )
543 // Call would block
544 return CSock::WouldBlock;
546 else
548 // Socket error
549 if ( throw_exception )
551 throw ESocket( "Unable to receive data" );
553 return CSock::Error;
558 len = (uint32)retLen;
560 else // Blocking Mode
562 // Receive incoming message, waiting until a complete message has arrived
563 uint total = 0;
564 sint brecvd;
566 while ( total < len )
568 TTicks before = CTime::getPerformanceTime();
569 brecvd = ::recv( _Sock, (char*)(buffer+total), len-total, 0 );
571 // nlinfo ("CSock::receive(): BM Received %d bytes to %d res: %d (%d) total %d", len, _Sock, brecvd, ERROR_NUM, total);
573 _MaxReceiveTime = max( (uint32)(CTime::ticksToSecond(CTime::getPerformanceTime()-before)*1000.0f), _MaxReceiveTime );
575 switch ( brecvd )
577 // Graceful disconnection
578 case 0 :
581 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
582 sync.value() = false;
584 _Connected = false;
585 len = total;
586 _BytesReceived += len;
588 if ( throw_exception )
590 throw ESocketConnectionClosed();
592 return CSock::ConnectionClosed;
595 // Socket error
596 case SOCKET_ERROR :
598 len = total;
599 _BytesReceived += len;
601 if ( throw_exception )
603 throw ESocket( "Unable to receive data" );
605 return CSock::Error;
608 total += brecvd;
612 /*if ( _Logging )
614 LNETL0_DEBUG( "LNETL0: Socket %d received %d bytes", _Sock, len );
616 _BytesReceived += len;
617 return CSock::Ok;
622 * Sets the socket in nonblocking mode
624 void CSock::setNonBlockingMode ( bool bm )
626 if ( _NonBlocking != bm )
628 #ifdef NL_OS_WINDOWS
629 u_long b = bm;
630 if ( ioctlsocket( _Sock, FIONBIO, &b ) != 0 )
631 #else
632 if ( fcntl( _Sock, F_SETFL, FNDELAY | fcntl( _Sock, F_GETFL, 0 ) ) == -1 )
633 #endif
635 throw ESocket( "Cannot set nonblocking mode" );
637 _NonBlocking = bm;
643 * Sets the send buffer size
645 void CSock::setSendBufferSize( sint32 size )
647 setsockopt( _Sock, SOL_SOCKET, SO_SNDBUF, (char*)(&size), (socklen_t)sizeof(size) );
651 * Gets the send buffer size
653 sint32 CSock::getSendBufferSize()
655 int size = -1;
656 socklen_t bufsize;
657 getsockopt( _Sock, SOL_SOCKET, SO_SNDBUF, (char*)(&size), &bufsize );
658 return size;
661 } // NLNET