// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdnet.h" #include "nel/net/tcp_sock.h" #include "nel/net/net_log.h" #ifdef NL_OS_WINDOWS # include # ifndef NL_COMP_MINGW # define NOMINMAX # endif # include # define socklen_t int # define ERROR_NUM WSAGetLastError() #elif defined NL_OS_UNIX # include # include # include # include # include # include # include # include # include # define SOCKET_ERROR -1 # define INVALID_SOCKET -1 # define ERROR_NUM errno # define ERROR_MSG strerror(errno) typedef int SOCKET; #endif using namespace NLMISC; namespace NLNET { /* * Constructor */ CTcpSock::CTcpSock( bool logging ) : CSock( logging ) {} /* * Construct a CTcpSocket object using an already connected socket */ CTcpSock::CTcpSock( SOCKET sock, const CInetAddress& remoteaddr ) : CSock( sock, remoteaddr ) {} /* Connection. You can reconnect a socket after being disconnected. * This method does not return a boolean, otherwise a programmer could ignore the result and no * exception would be thrown if connection fails : * - If addr is not valid, an exception ESocket is thrown * - If connect() fails for another reason, an exception ESocketConnectionFailed is thrown */ void CTcpSock::connect( const CInetAddress& addr ) { // Create a new socket if ( _Sock != INVALID_SOCKET ) { if ( _Logging ) { // LNETL0_DEBUG( "LNETL0: Closing socket %d before reconnecting", _Sock ); } close(); } createSocket( SOCK_STREAM, IPPROTO_TCP ); // activate keep alive setKeepAlive(true); // Connection CSock::connect( addr ); } /* * Active disconnection. After disconnecting, you can't connect back with the same socket. */ void CTcpSock::disconnect() { LNETL0_DEBUG( "LNETL0: Socket %d disconnecting from %s...", _Sock, _RemoteAddr.asString().c_str() ); // This shutdown resets the connection immediately (not a graceful closure) #ifdef NL_OS_WINDOWS ::shutdown( _Sock, SD_BOTH ); #elif defined NL_OS_UNIX ::shutdown( _Sock, SHUT_RDWR ); #endif /*CSynchronized::CAccessor sync( &_SyncConnected ); sync.value() = false;*/ _Connected = false; } /* * Active disconnection for download way only */ void CTcpSock::shutdownReceiving() { #ifdef NL_OS_WINDOWS ::shutdown( _Sock, SD_RECEIVE ); #elif defined NL_OS_UNIX ::shutdown( _Sock, SHUT_RD ); #endif } /* * Active disconnection for upload way only */ void CTcpSock::shutdownSending() { #ifdef NL_OS_WINDOWS ::shutdown( _Sock, SD_SEND ); #elif defined NL_OS_UNIX ::shutdown( _Sock, SHUT_WR ); #endif } void CTcpSock::setKeepAlive( bool keepAlive) { nlassert(_Sock != INVALID_SOCKET); int b = keepAlive?1:0; if ( setsockopt( _Sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&b, sizeof(b) ) != 0 ) { throw ESocket( "setKeepAlive failed" ); } } /* * Sets/unsets TCP_NODELAY */ void CTcpSock::setNoDelay( bool value ) { int b = value?1:0; if ( setsockopt( _Sock, IPPROTO_TCP, TCP_NODELAY, (char*)&b, sizeof(b) ) != 0 ) { throw ESocket( "setNoDelay failed" ); } } /* Sets a custom TCP Window size (SO_RCVBUF and SO_SNDBUF). * You must close the socket is necessary, before calling this method. * * See http://www.ncsa.uiuc.edu/People/vwelch/net_perf/tcp_windows.html */ void CTcpSock::connectWithCustomWindowSize( const CInetAddress& addr, int windowsize ) { // Create socket if ( _Sock != INVALID_SOCKET ) { nlerror( "Cannot connect with custom window size when already connected" ); } createSocket( SOCK_STREAM, IPPROTO_TCP ); // Change window size if ( setsockopt( _Sock, SOL_SOCKET, SO_SNDBUF, (char*)&windowsize, sizeof(windowsize) ) != 0 || setsockopt( _Sock, SOL_SOCKET, SO_RCVBUF, (char*)&windowsize, sizeof(windowsize) ) != 0 ) { throw ESocket( "setWindowSize failed" ); } // Connection CSock::connect( addr ); } /* * Returns the TCP Window Size for the current socket */ uint32 CTcpSock::getWindowSize() { int windowsize = 0; socklen_t len = sizeof( windowsize ); /* send buffer -- query for buffer size */ if ( getsockopt( _Sock, SOL_SOCKET, SO_SNDBUF, (char*) &windowsize, &len ) == 0 ) { return windowsize; } else { return 0; } } } // NLNET