You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ryzom-core/code/nel/include/nel/net/sock.h

325 lines
10 KiB
C

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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 <http://www.gnu.org/licenses/>.
#ifndef NL_SOCK_H
#define NL_SOCK_H
#include "nel/misc/common.h"
#include "nel/misc/mutex.h"
#include "inet_address.h"
//#include <sstream>
/// This namespace contains all network class
namespace NLNET {
/**
* Network exceptions
* \author Olivier Cado
* \author Nevrax France
* \date 2000
*/
struct ESocket : public NLMISC::Exception
{
/** Constructor
* You can provide an internet address. If so, reason *must* contain "%s"
* where the address should be written. Moreover, the length of reason plus
* the length of the address when displayed by asString() should no exceed 256.
*/
ESocket( const char *reason="", bool systemerror=true, CInetAddress *addr=NULL );
};
/// Exception raised when connect() fails
struct ESocketConnectionFailed : public ESocket
{
ESocketConnectionFailed( CInetAddress addr ) : ESocket( "Connection to %s failed", true, &addr ) {}
};
/// Exception raised when a connection is gracefully closed by peer
struct ESocketConnectionClosed : public ESocket
{
ESocketConnectionClosed() : ESocket( "Connection closed" ) {}
};
/// Exception raised when an unauthorized access has been done
struct EAccessDenied : public ESocket
{
EAccessDenied( std::string s ): ESocket( (std::string("Access denied: ")+s).c_str(), false ) {}
};
/// Exception raised when a the NS does not find the service looked-up
struct EServiceNotFound : public ESocket
{
EServiceNotFound( std::string s ): ESocket( (std::string("Service not found: ")+s).c_str(), false ) {}
};
//typedef SOCKET;
#ifdef NL_OS_WINDOWS
typedef uint SOCKET;
#elif defined NL_OS_UNIX
typedef int SOCKET;
#endif
/**
* CSock: base socket class.
* One CSock object represents a communication between two hosts, the local one and the remote one.
* This class implements layer 0 of the NeL Network Engine.
* This class does not handle conversion between big endian and little endian ; the provided
* buffers are sent raw.
*
* The "logging" boolean value is necessary because in this implementation we always log
* to one single global CLog object : there is not one CLog object per socket. Therefore
* we must prevent the socket used in CNetDisplayer from logging itself... otherwise we
* would have an infinite recursion.
*
* The "connected" property may have a different meaning whether the socket is a stream socket
* (e.g. using TCP) or it is a connectionless datagram socket (e.g. using UDP). In the latter,
* "connected" only means that the local and the remote addresses have been set.
*
* Important note: this class is thread-safe, meaning you can access to a CSock object
* from multiple threads BUT the only things you are allow to do in parallel are
* receive/send and read the connected() property.
*
* You must call CSock::initNetwork() before using any network class (even CInetAddress).
* You must call CSock::releaseNetwork() at the end of your program.
*
* By default, a socket is in blocking mode. Call setNonBlockingMode() to change this
* behaviour.
* \author Olivier Cado
* \author Nevrax France
* \date 2000-2001
*/
class CSock
{
public:
enum TSockResult { Ok, WouldBlock, ConnectionClosed, Error };
/// Initialize the network engine, if it is not already done
static void initNetwork();
/// Releases the network engine
static void releaseNetwork();
/** Returns the code of the last error that has occured.
* Note: This code is platform-dependant. On Unix, it is errno; on Windows it is the Winsock error code.
* See also errorString()
*/
static uint getLastError();
/// Returns a string explaining the network error (see getLastError())
static std::string errorString( uint errorcode );
/// Change the time out value used in getDataAvailable(), which is 0 by default
void setTimeOutValue( long sec, long ms )
{
_TimeoutS = sec;
if ( ms > 999 )
ms = 999;
_TimeoutUs = ms * 1000;
}
/// @name Socket setup
//@{
/** Connection.
* 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
*/
virtual void connect( const CInetAddress& addr );
/** Sets the socket in nonblocking mode. Call this method *after* connect(), otherwise you will get
* an "would block" error (10035 on Windows). In nonblocking mode, use received() and sent() instead of receive() and send()
*/
void setNonBlockingMode ( bool bm );
/// Returns the nonblocking mode
bool nonBlockingMode() const { return _NonBlocking; }
/** Closes the socket (without shutdown)
* In general you don't need to call this method. But you can call it to:
* - close a listening socket (i.e. stop accepting connections), or
* - stop a select() in progress in another thread (in this case, just calling the destructor is not enough)
*/
virtual void close();
/// Destructor (shutdown + close)
virtual ~CSock();
//@}
/// @name Receiving data
//@{
/// Checks if there is some data to receive, waiting (blocking) at most for the time out value.
bool dataAvailable();
/** Receive a partial or an entire block of data, depending on nonblocking mode.
*
* In blocking mode: the method waits until 'len' bytes have been received.
*
* In nonblocking mode: the method reads the bytes that have already been received only, and
* resets 'len' to the number of bytes read. The actual length may be smaller than the demanded
* length. In no data is available, the return value is CSock::WouldBlock. If dataAvailable()
* returns true, you are sure that receive() will not return CSock::WouldBlock.
*
* In case of graceful disconnection:
* - connected() become false
* - the return value is CSock::ConnectionClosed or an ESocketConnectionClosed exception is thrown.
*
* In case of failure (e.g. connection reset by peer) :
* - the return value is CSock::Error or an ESocket exception is thrown.
* You may want to close the connection manually.
*/
CSock::TSockResult receive( uint8 *buffer, uint32& len, bool throw_exception=true );
//@}
/// @name Sending data
//@{
/** Sends a message.
*
* In blocking mode: the method waits until 'len' bytes have been sent.
*
* In nonblocking mode : the method resets len to the actual number of bytes sent.
* Even if less bytes than expected have been sent, it returns CSock::Ok. The caller
* is expected to test the actual len to check if the remaining data must be resent.
*
* \return CSock::Ok or CSock::Error (in case of failure).
* When throw_exception is true, the method throws an ESocket exception in case of failure.
*/
CSock::TSockResult send( const uint8 *buffer, uint32& len, bool throw_exception=true );
//@}
/// @name Properties
//@{
/// Returns if the socket is connected (volatile)
bool connected() { return _Connected; }
/// Returns a const reference on the local address
const CInetAddress& localAddr() const { return _LocalAddr; }
/// Returns the address of the remote host
const CInetAddress& remoteAddr() const { return _RemoteAddr; }
/// Returns the socket descriptor
SOCKET descriptor() const { return _Sock; }
/// Returns the time out value in millisecond
uint32 timeOutValue() const { return _TimeoutS*1000 + _TimeoutUs/1000; }
//@}
/// Returns the number of bytes received since the latest connection
uint64 bytesReceived() const { return _BytesReceived; }
/// Returns the number of bytes sent since the latest connection
uint64 bytesSent() const { return _BytesSent; }
/// Sets the send buffer size
void setSendBufferSize( sint32 size );
/// Gets the send buffer size
sint32 getSendBufferSize();
/// Returns true if the network engine is initialized
static bool initialized() { return CSock::_Initialized; }
protected:
/**
* Constructor.
* \param logging Disable logging if the server socket object is used by the logging system, to avoid infinite recursion
*/
CSock( bool logging = true );
/// Construct a CSock object using an existing connected socket descriptor and its associated remote address
CSock( SOCKET sock, const CInetAddress& remoteaddr );
/// Creates the socket and get a valid descriptor
void createSocket( int type, int protocol );
/// Sets the local address
void setLocalAddress();
/// Socket descriptor
SOCKET _Sock;
/// Address of local host (valid if connected)
CInetAddress _LocalAddr;
/// Address of the remote host (valid if connected)
CInetAddress _RemoteAddr;
/// If false, do not log any information
bool _Logging;
/// If true, the socket is in nonblocking mode
bool _NonBlocking;
/// True after calling connect()
//NLMISC::CSynchronized<bool> _SyncConnected;
volatile bool _Connected;
/// Number of bytes received on this socket
uint64 _BytesReceived;
/// Number of bytes sent on this socket
uint64 _BytesSent;
/// Main time out value (sec) for select in dataAvailable()
long _TimeoutS;
/// Secondary time out value (microsec) for select in dataAvailable()
long _TimeoutUs;
private:
/// True if the network library has been initialized
static bool _Initialized;
// Test: send & receive duration (ms)
uint32 _MaxReceiveTime;
uint32 _MaxSendTime;
/// Flag used to determine the moments at which sends atrta an stop blocking
bool _Blocking;
};
} // NLNET
#endif // NL_SOCK_H
/* End of sock.h */