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/net/sock.h"
23 #include "nel/net/net_log.h"
24 #include "nel/misc/time_nl.h"
25 #include "nel/misc/hierarchical_timer.h"
28 # ifndef NL_COMP_MINGW
31 # include <winsock2.h>
33 # define socklen_t int
34 # define ERROR_NUM WSAGetLastError()
35 # define ERROR_WOULDBLOCK WSAEWOULDBLOCK
37 #elif defined NL_OS_UNIX
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>
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
59 # define FNDELAY O_NDELAY
67 using namespace NLMISC
;
76 bool CSock::_Initialized
= false;
82 ESocket::ESocket( const char *reason
, bool systemerror
, CInetAddress
*addr
)
84 /*it doesnt work on linux, should do something more cool
86 ss << "Socket error: " << reason;
89 ss << " (" << ERROR_NUM;
91 ss << ": " << ERROR_MSG;
93 ss << ") " << std::endl;
97 _Reason
= "Socket error: ";
98 uint errornum
= CSock::getLastError();
102 // Version with address
103 smprintf( str
, 256, reason
, addr
->asString().c_str() ); // reason *must* contain "%s"
108 // Version without address
114 smprintf( str
, 256, "%d", errornum
);
119 _Reason
+= CSock::errorString( errornum
);
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
)
136 WORD winsock_version
= MAKEWORD( 2, 0 );
138 if ( WSAStartup( winsock_version
, &wsaData
) != 0 )
140 throw ESocket( "Winsock initialization failed" );
143 CSock::_Initialized
= true;
148 * Releases the network engine
150 void CSock::releaseNetwork()
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
)
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";
195 #elif defined NL_OS_UNIX
196 return std::string( strerror( errorcode
) );
206 CSock::CSock( bool logging
) :
207 _Sock( INVALID_SOCKET
),
209 _NonBlocking( false ),
214 _MaxReceiveTime( 0 ),
218 nlassert( CSock::_Initialized
);
220 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
221 sync.value() = 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
) :
232 _RemoteAddr( remoteaddr
),
234 _NonBlocking( false ),
237 _MaxReceiveTime( 0 ),
240 nlassert( CSock::_Initialized
);
242 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
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
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.
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);
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" );
286 // LNETL0_DEBUG( "LNETL0: Socket %d open (TCP)", _Sock );
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.
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);
306 * Closes the listening socket
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
;
318 closesocket( sockToClose
);
319 #elif defined NL_OS_UNIX
320 ::close( sockToClose
);
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
)
337 LNETL0_DEBUG( "LNETL0: Socket %d closing for %s at %s", _Sock
, _RemoteAddr
.asString().c_str(), _LocalAddr
.asString().c_str() );
343 shutdown( _Sock
, SD_BOTH
);
345 closesocket( _Sock
);
346 #elif defined NL_OS_UNIX
347 shutdown( _Sock
, SHUT_RDWR
);
351 _Sock
= INVALID_SOCKET
;
359 void CSock::connect( const CInetAddress
& addr
)
361 LNETL0_DEBUG( "LNETL0: Socket %d connecting to %s...", _Sock
, addr
.asString().c_str() );
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)
372 if ( setsockopt( _Sock
, SOL_SOCKET
, SO_REUSEADDR
, &value
, sizeof(value
) ) == SOCKET_ERROR
)
374 throw ESocket( "ReuseAddr failed" );
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 )
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) );
391 throw ESocketConnectionFailed( addr
);
396 LNETL0_DEBUG( "LNETL0: Socket %d connected to %s (local %s)", _Sock
, addr
.asString().c_str(), _LocalAddr
.asString().c_str() );
403 /*CSynchronized<bool>::CAccessor sync( &_SyncConnected );
404 sync.value() = true;*/
410 * Checks if there is some data to receive
412 bool CSock::dataAvailable()
416 FD_SET( _Sock
, &fdset
);
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
);
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()
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);
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
);
472 //nldebug("SendWouldBlock - %s / %s Entering snooze mode",_LocalAddr.asString().c_str(),_RemoteAddr.asString().c_str());
477 if ( throw_exception
)
480 throw ESocket( NLMISC::toString( "Unable to send data: error %u", GetLastError() ).c_str() );
482 throw ESocket( "Unable to send data" );
491 //nldebug("SendWouldBlock - %s / %s Leaving snooze mode",_LocalAddr.asString().c_str(),_RemoteAddr.asString().c_str());
502 CSock::TSockResult
CSock::receive( uint8
*buffer
, uint32
& len
, bool throw_exception
)
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);
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
);
522 // Graceful disconnection
526 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
527 sync.value() = false;
530 if ( throw_exception
)
532 throw ESocketConnectionClosed();
534 return CSock::ConnectionClosed
;
537 // Socket error or call would block
541 if ( ERROR_NUM
== ERROR_WOULDBLOCK
)
544 return CSock::WouldBlock
;
549 if ( throw_exception
)
551 throw ESocket( "Unable to receive data" );
558 len
= (uint32
)retLen
;
560 else // Blocking Mode
562 // Receive incoming message, waiting until a complete message has arrived
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
);
577 // Graceful disconnection
581 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
582 sync.value() = false;
586 _BytesReceived
+= len
;
588 if ( throw_exception
)
590 throw ESocketConnectionClosed();
592 return CSock::ConnectionClosed
;
599 _BytesReceived
+= len
;
601 if ( throw_exception
)
603 throw ESocket( "Unable to receive data" );
614 LNETL0_DEBUG( "LNETL0: Socket %d received %d bytes", _Sock, len );
616 _BytesReceived
+= len
;
622 * Sets the socket in nonblocking mode
624 void CSock::setNonBlockingMode ( bool bm
)
626 if ( _NonBlocking
!= bm
)
630 if ( ioctlsocket( _Sock
, FIONBIO
, &b
) != 0 )
632 if ( fcntl( _Sock
, F_SETFL
, FNDELAY
| fcntl( _Sock
, F_GETFL
, 0 ) ) == -1 )
635 throw ESocket( "Cannot set nonblocking mode" );
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()
657 getsockopt( _Sock
, SOL_SOCKET
, SO_SNDBUF
, (char*)(&size
), &bufsize
);