# 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  

sock.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2000 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/net/sock.h"
00029 #include "nel/misc/time_nl.h"
00030 
00031 #ifdef NL_OS_WINDOWS
00032 
00033 #       include <winsock2.h>
00034 //#     include <windows.h>
00035 #       define socklen_t int
00036 #       define ERROR_NUM WSAGetLastError()
00037 #       define ERROR_WOULDBLOCK WSAEWOULDBLOCK
00038 
00039 #elif defined NL_OS_UNIX
00040 
00041 #       include <unistd.h>
00042 #       include <sys/types.h>
00043 #       include <sys/time.h>
00044 #       include <sys/socket.h>
00045 #       include <netinet/in.h>
00046 #       include <netinet/tcp.h>
00047 #       include <arpa/inet.h>
00048 #       include <netdb.h>
00049 #       include <fcntl.h>
00050 #       include <errno.h>
00051 #       define SOCKET_ERROR -1
00052 #       define INVALID_SOCKET -1
00053 #       define ERROR_NUM errno
00054 #       define ERROR_WOULDBLOCK EWOULDBLOCK
00055 #       define ERROR_MSG strerror(errno)
00056 typedef int SOCKET;
00057 
00058 #endif
00059 
00060 using namespace std;
00061 using namespace NLMISC;
00062 
00063 namespace NLNET {
00064 
00065 
00066 bool CSock::_Initialized = false;
00067 
00068 long CSock::_TimeoutS = 0;
00069 
00070 long CSock::_TimeoutMs = 0;
00071 
00072 
00073 /*
00074  * ESocket constructor
00075  */
00076 ESocket::ESocket( const char *reason, bool systemerror, CInetAddress *addr )
00077 {
00078 /*it doesnt work on linux, should do something more cool
00079         std::stringstream ss;
00080         ss << "Socket error: " << reason;
00081         if ( systemerror )
00082         {
00083                 ss << " (" << ERROR_NUM;
00084 #ifdef NL_OS_UNIX
00085                 ss << ": " << ERROR_MSG;
00086 #endif
00087                 ss << ") " << std::endl;
00088         }
00089         _Reason = ss.str();
00090   */
00091         _Reason = "Socket error: ";
00092         uint errornum = CSock::getLastError();
00093         char str[256];
00094         if ( addr != NULL )
00095         {
00096                 // Version with address
00097                 smprintf( str, 256, reason, addr->asString().c_str() ); // reason *must* contain "%s"
00098                 _Reason += str;
00099         }
00100         else
00101         {
00102                 // Version without address
00103                 _Reason += reason;
00104         }
00105         if ( systemerror )
00106         {
00107                 _Reason += " (";
00108                 smprintf( str, 256, "%d", errornum );
00109                 _Reason += str;
00110                 if ( errornum != 0 )
00111                 {
00112                         _Reason += ": ";
00113                         _Reason += CSock::errorString( errornum );
00114                 }
00115                 _Reason += ")";
00116         }
00117         nlwarning( "Exception will be launched: %s", _Reason.c_str() );
00118 }
00119 
00120 
00121 /*
00122  * Initializes the network engine if it is not already done (under Windows, calls WSAStartup()).
00123  */
00124 void CSock::initNetwork()
00125 {
00126         if ( ! CSock::_Initialized )
00127         {
00128 #ifdef NL_OS_WINDOWS
00129                 WORD winsock_version = MAKEWORD( 2, 0 ); 
00130                 WSADATA wsaData;
00131                 if ( WSAStartup( winsock_version, &wsaData ) != 0 )
00132                 {
00133                         throw ESocket( "Winsock initialization failed" );
00134                 }
00135 #endif
00136                 CSock::_Initialized = true;
00137         }
00138 }
00139 
00140 /*
00141  * Releases the network engine
00142  */
00143 void CSock::releaseNetwork()
00144 {
00145 #ifdef NL_OS_WINDOWS
00146         WSACleanup();
00147 #endif
00148         CSock::_Initialized = false;
00149 }
00150 
00151 
00152 /* Returns the code of the last error that has occured.
00153  * Note: This code is platform-dependant. On Unix, it is errno; on Windows it is the Winsock error code.
00154  * See also errorString()
00155  */
00156 uint CSock::getLastError()
00157 {
00158         return (uint)ERROR_NUM;
00159 }
00160 
00161 
00162 /*
00163  * Returns a string explaining the network error (see getLastError())
00164  */
00165 std::string CSock::errorString( uint errorcode )
00166 {
00167 #ifdef NL_OS_WINDOWS
00168         switch( errorcode )
00169         {
00170         case WSAEINTR            /*10004*/: return "Blocking operation interrupted";
00171         case WSAEINVAL           /*10022*/: return "Invalid socket (maybe not bound) or argument";
00172         case WSAEMFILE           /*10024*/: return "Too many open sockets";
00173         case WSAENOTSOCK         /*10038*/: return "Socket operation on nonsocket (maybe invalid select descriptor)";
00174         case WSAEMSGSIZE         /*10040*/: return "Message too long";
00175         case WSAEADDRINUSE   /*10048*/: return "Address already in use (is this service already running in this computer?)";
00176         case WSAEADDRNOTAVAIL/*10049*/: return "Address not available";
00177         case WSAENETDOWN         /*10050*/: return "Network is down";
00178         case WSAENETUNREACH  /*10051*/: return "Network is unreachable";
00179         case WSAECONNRESET   /*10054*/: return "Connection reset by peer";
00180         case WSAENOBUFS          /*10055*/: return "No buffer space available; please close applications or reboot";
00181         case WSAESHUTDOWN        /*10058*/: return "Cannot send/receive after socket shutdown";
00182         case WSAETIMEDOUT        /*10060*/: return "Connection timed-out";
00183         case WSAECONNREFUSED /*10061*/: return "Connection refused, the server may be offline";
00184         case WSAEHOSTUNREACH /*10065*/: return "Remote host is unreachable";
00185         case WSANOTINITIALISED /*093*/: return "'Windows Sockets' not initialized";
00186         default:                                                return "";
00187         }
00188 #elif defined NL_OS_UNIX
00189         return std::string( strerror( errorcode ) );
00190 #endif
00191 
00192 }
00193 
00194 
00195 
00196 /*
00197  * Constructor
00198  */
00199 CSock::CSock( bool logging ) :
00200         _Sock( INVALID_SOCKET ),
00201         _Logging( logging ),
00202         _BytesReceived( 0 ),
00203         _BytesSent( 0 ),
00204         _NonBlocking( false ),
00205         _MaxReceiveTime( 0 ),
00206         _MaxSendTime( 0 )
00207 {
00208         nlassert( CSock::_Initialized );
00209         /*{
00210                 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
00211                 sync.value() = false;
00212         }*/
00213         _Connected = false;
00214 }
00215 
00216 
00217 /*
00218  * Construct a CSock object using an existing connected socket descriptor and its associated remote address
00219  */
00220 CSock::CSock( SOCKET sock, const CInetAddress& remoteaddr ) :
00221         _Sock( sock ),
00222         _Logging( true ),
00223         _BytesReceived( 0 ),
00224         _BytesSent( 0 ),
00225         _RemoteAddr( remoteaddr ),
00226         _NonBlocking( false ),
00227         _MaxReceiveTime( 0 ),
00228         _MaxSendTime( 0 )
00229 {
00230         nlassert( CSock::_Initialized );
00231         /*{
00232                 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
00233                 sync.value() = true;
00234         }*/
00235         _Connected = true;
00236 
00237         // Check remote address
00238         if ( ! _RemoteAddr.isValid() )
00239         {
00240                 throw ESocket( "Could not init a socket object with an invalid address", false );
00241         }
00242 
00243         // Get local socket name
00244         setLocalAddress();
00245 }
00246 
00247 
00248 /*
00249  * Creates the socket and get a valid descriptor
00250  */
00251 void CSock::createSocket( int type, int protocol )
00252 {
00253         nlassert( _Sock == INVALID_SOCKET );
00254 
00255         _Sock = socket( AF_INET, type, protocol ); // or IPPROTO_IP (=0) ?
00256         if ( _Sock == INVALID_SOCKET )
00257         {
00258                 throw ESocket( "Socket creation failed" );
00259         }
00260 
00261         if ( _Logging )
00262         {
00263 //              nldebug( "LNETL0: Socket %d open (TCP)", _Sock );
00264         }
00265 }
00266 
00267 
00268 /*
00269  * Closes the listening socket
00270  */
00271 void CSock::close()
00272 {
00273         if ( _Logging )
00274         {
00275                 nldebug( "LNETL0: Socket %d closing for %s at %s", _Sock, _RemoteAddr.asString().c_str(), _LocalAddr.asString().c_str() );
00276         }
00277 #ifdef NL_OS_WINDOWS
00278         closesocket( _Sock );
00279 #elif defined NL_OS_UNIX
00280         ::close( _Sock );
00281 #endif
00282         _Sock = INVALID_SOCKET;
00283 }
00284 
00285 
00286 /*
00287  * Destructor
00288  */
00289 CSock::~CSock()
00290 {
00291         nlinfo( "Report for %s socket %s: Max send time: %u Max recv time: %u", _NonBlocking?"non-blocking":"blocking", remoteAddr().asString().c_str(), _MaxSendTime, _MaxReceiveTime );
00292         nlinfo( "Max send time: %u", _MaxSendTime);
00293         if ( _Sock != INVALID_SOCKET )
00294         {
00295                 if ( _Logging )
00296                 {
00297                         nldebug( "LNETL0: Socket %d closing for %s at %s", _Sock, _RemoteAddr.asString().c_str(), _LocalAddr.asString().c_str() );
00298                 }
00299 
00300                 if ( connected() )
00301                 {
00302 #ifdef NL_OS_WINDOWS
00303                         shutdown( _Sock, SD_BOTH );
00304                 }
00305                 closesocket( _Sock );
00306 #elif defined NL_OS_UNIX
00307                         shutdown( _Sock, SHUT_RDWR );
00308                 }
00309                 ::close( _Sock );
00310 #endif
00311                 _Sock = INVALID_SOCKET;
00312         }
00313 }
00314 
00315 
00316 /*
00317  * Connection
00318  */
00319 void CSock::connect( const CInetAddress& addr )
00320 {
00321         nldebug( "LNETL0: Socket %d connecting to %s...", _Sock, addr.asString().c_str() );
00322 
00323         // Check address
00324         if ( ! addr.isValid() )
00325         {
00326                 throw ESocket( "Unable to connect to invalid address", false );
00327         }
00328 
00329 #ifndef NL_OS_WINDOWS
00330         // Set Reuse Address On (does not work on Win98 and is useless on Win2000)
00331         int value = true;
00332         if ( setsockopt( _Sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value) ) == SOCKET_ERROR )
00333         {
00334                 throw ESocket( "ReuseAddr failed" );
00335         }
00336 #endif
00337 
00338         // Connection (when _Sock is a datagram socket, connect establishes a default destination address)
00339         if ( ::connect( _Sock, (const sockaddr *)(addr.sockAddr()), sizeof(sockaddr_in) ) != 0 )
00340         {
00341 /*              if ( _Logging )
00342                 {
00343 #ifdef NL_OS_WINDOWS
00344                         nldebug( "Impossible to connect socket %d to %s %s (%d)", _Sock, addr.hostName().c_str(), addr.asIPString().c_str(), ERROR_NUM );
00345 #elif defined NL_OS_UNIX
00346                         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) );
00347 #endif
00348                 }
00349 */
00350                 throw ESocketConnectionFailed( addr );
00351         }
00352         setLocalAddress();
00353         if ( _Logging )
00354         {
00355                 nldebug( "LNETL0: Socket %d connected to %s (local %s)", _Sock, addr.asString().c_str(), _LocalAddr.asString().c_str() );
00356         }       
00357         _RemoteAddr = addr;
00358 
00359         _BytesReceived = 0;
00360         _BytesSent = 0;
00361 
00362         /*CSynchronized<bool>::CAccessor sync( &_SyncConnected );
00363         sync.value() = true;*/
00364         _Connected = true;
00365 }
00366 
00367 
00368 /*
00369  * Checks if there is some data to receive
00370  */
00371 bool CSock::dataAvailable()
00372 {
00373         fd_set fdset;
00374         FD_ZERO( &fdset );
00375         FD_SET( _Sock, &fdset );
00376         timeval tv;
00377         tv.tv_sec = CSock::_TimeoutS;
00378         tv.tv_usec = CSock::_TimeoutMs;
00379 
00380         // Test for message received.
00381         int res = select( _Sock+1, &fdset, NULL, NULL, &tv );
00382         switch ( res  )
00383         {
00384                 case  0 : return false;
00385                 case -1 : throw ESocket( "CSock::dataAvailable(): select failed" ); return false;
00386                 default : return true;
00387         }
00388 }
00389 
00390 
00391 /*
00392  * Sets the local address
00393  */
00394 void CSock::setLocalAddress()
00395 {
00396         sockaddr saddr;
00397         socklen_t saddrlen = sizeof(saddr);
00398         if ( getsockname( _Sock, &saddr, &saddrlen ) != 0 )
00399         {
00400                 throw ESocket( "Unable to find local address" );
00401         }
00402         _LocalAddr.setSockAddr( (const sockaddr_in *)&saddr );
00403 }
00404 
00405 
00406 /*
00407  * Sends data, or returns false if it would block
00408  */
00409 CSock::TSockResult CSock::send( const uint8 *buffer, uint32& len, bool throw_exception )
00410 {
00411         uint32 realLen = len;
00412         TTicks before = CTime::getPerformanceTime();
00413         len = ::send( _Sock, (const char*)buffer, len, 0 );
00414         _MaxSendTime = max( (uint32)(CTime::ticksToSecond(CTime::getPerformanceTime()-before)*1000.0f), _MaxSendTime );
00415 
00416         if ( _Logging )
00417         {
00418 //              nldebug ("LNETL0: CSock::send(): Sent %d bytes to %d res: %d (%d)", realLen, _Sock, len, ERROR_NUM);
00419         }
00420         
00421         if ( len == SOCKET_ERROR )
00422         {
00423                 if ( ERROR_NUM == ERROR_WOULDBLOCK )
00424                 {
00425                         len = 0;
00426                         return Ok;
00427                 }
00428                 if ( throw_exception )
00429                 {
00430                         throw ESocket( "Unable to send data" );
00431                 }
00432                 return Error;
00433         }
00434         _BytesSent += len;
00435         
00436         return Ok;
00437 }
00438 
00439 
00440 
00441 /*
00442  * Receives data
00443  */
00444 CSock::TSockResult CSock::receive( uint8 *buffer, uint32& len, bool throw_exception )
00445 {
00446         if ( _NonBlocking )
00447         {
00448                 // Receive incoming message (only the received part)
00449 
00450                 uint32 realLen = len;
00451 
00452                 TTicks before = CTime::getPerformanceTime();
00453                 len = ::recv( _Sock, (char*)buffer, len, 0 );
00454 
00455                 if ( _Logging )
00456                 {
00457 //                      nldebug ("LNETL0: CSock::receive(): NBM Received %d bytes to %d res: %d (%d)", realLen, _Sock, len, ERROR_NUM);
00458                 }
00459 
00460                 _MaxReceiveTime = max( (uint32)(CTime::ticksToSecond(CTime::getPerformanceTime()-before)*1000.0f), _MaxReceiveTime );
00461                 switch ( len )
00462                 {
00463                         // Graceful disconnection
00464                         case 0 :
00465                         {
00466                                 /*{
00467                                         CSynchronized<bool>::CAccessor sync( &_SyncConnected );
00468                                         sync.value() = false;
00469                                 }*/
00470                                 _Connected = false;
00471                                 if ( throw_exception )
00472                                 {
00473                                         throw ESocketConnectionClosed();
00474                                 }
00475                                 return CSock::ConnectionClosed;
00476                         }
00477 
00478                         // Socket error or call would block
00479                         case SOCKET_ERROR :
00480                         {
00481                                 len = 0;
00482                                 if ( ERROR_NUM == ERROR_WOULDBLOCK )
00483                                 {
00484                                         // Call would block
00485                                         return CSock::WouldBlock;
00486                                 }
00487                                 else
00488                                 {
00489                                         // Socket error
00490                                         if ( throw_exception )
00491                                         {
00492                                                 throw ESocket( "Unable to receive data" );
00493                                         }
00494                                         return CSock::Error;
00495                                 }
00496                         }
00497                 }
00498         }
00499         else // Blocking Mode
00500         {
00501                 // Receive incoming message, waiting until a complete message has arrived
00502                 uint total = 0;
00503                 uint brecvd;
00504                 
00505                 uint32 realLen = len;
00506                 
00507                 while ( total < len )
00508                 {
00509                         TTicks before = CTime::getPerformanceTime();
00510                         brecvd = ::recv( _Sock, (char*)(buffer+total), len-total, 0 );
00511 
00512 //                      nldebug ("LNETL0: CSock::receive(): BM Received %d bytes to %d res: %d (%d)", realLen, _Sock, len, ERROR_NUM);
00513 
00514                         _MaxReceiveTime = max( (uint32)(CTime::ticksToSecond(CTime::getPerformanceTime()-before)*1000.0f), _MaxReceiveTime );
00515                         
00516                         switch ( brecvd )
00517                         {
00518                                 // Graceful disconnection
00519                                 case 0 : 
00520                                 {
00521                                         /*{
00522                                                 CSynchronized<bool>::CAccessor sync( &_SyncConnected );
00523                                                 sync.value() = false;
00524                                         }*/
00525                                         _Connected = false;
00526                                         if ( throw_exception )
00527                                         {
00528                                                 throw ESocketConnectionClosed();
00529                                         }
00530                                         return CSock::ConnectionClosed;
00531                                 }
00532 
00533                                 // Socket error
00534                                 case SOCKET_ERROR :
00535                                 {
00536                                         if ( throw_exception )
00537                                         {
00538                                                 throw ESocket( "Unable to receive data" );
00539                                         }
00540                                         return CSock::Error;
00541                                 }
00542                         }
00543                         total += brecvd;
00544                 }
00545         }
00546 
00547         /*if ( _Logging )
00548         {
00549                 nldebug( "LNETL0: Socket %d received %d bytes", _Sock, len );
00550         }*/
00551         _BytesReceived += len;
00552         return CSock::Ok;
00553 }
00554 
00555 
00556 /*
00557  * Returns if the socket is connected
00558  */
00559 bool CSock::connected()
00560 {
00561   /*bool b;
00562   {
00563     //nldebug( "LNETL0: CSock::connected-BEGIN (socket %u)", descriptor() );
00564     CSynchronized<bool>::CAccessor sync( &_SyncConnected );
00565     b = sync.value();
00566   }
00567   //nldebug( "LNETL0: CSock::connected-END" );
00568   return b;*/
00569         return _Connected;
00570 }
00571 
00572 
00573 /*
00574  * Sets the socket in nonblocking mode
00575  */
00576 void CSock::setNonBlockingMode ( bool bm )
00577 {
00578         if ( _NonBlocking != bm )
00579         {
00580 #ifdef NL_OS_WINDOWS
00581                 u_long b = bm;
00582                 if ( ioctlsocket( _Sock, FIONBIO, &b ) != 0 )
00583 #else
00584                 if ( fcntl( _Sock, F_SETFL, FNDELAY | fcntl( _Sock, F_GETFL, 0 ) ) == -1 )
00585 #endif
00586                 {
00587                         throw ESocket( "Cannot set nonblocking mode" );
00588                 }
00589                 _NonBlocking = bm;
00590         }
00591 }
00592 
00593 
00594 } // NLNET