/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-28 Jason Rohrer * Added a static framework init function. * * 2001-November-13 Jason Rohrer * Changed timeout parameter to signed, since -1 is a possible argument. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-August-2 Jason Rohrer * Added functon for getting remote host address. * * 2003-February-3 Jason Rohrer * Added a function for getting the local host address from a socket. * * 2003-August-12 Jason Rohrer * Added more verbose comment about receive timeout parameter. * Added a function for flushing socket sends. * * 2003-November-20 Jason Rohrer * Made flush function robust against bogus receive return values. * * 2004-December13 Jason Rohrer * Added a breakConnection function. * * 2005-July-5 Jason Rohrer * Added port number when getting address of remote host. * * 2006-May-28 Jason Rohrer * Changed timeout behavior slightly to support emulation of non-blocking mode. * Added support for non-blocking sends. */ #include "minorGems/common.h" #ifndef SOCKET_CLASS_INCLUDED #define SOCKET_CLASS_INCLUDED #include "minorGems/network/HostAddress.h" #include "minorGems/system/Time.h" #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Network socket. Does not contain an interface for listening to connections * (see SocketServer for these interfaces) or for establishing connections * (see SocketClient for these interfaces). * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class Socket { public: /** * Constructor for a socket. * * Should not be called directly. Use SocketClient or SocketServer * to obtain an outbound or inbound socket connection. */ Socket(); // destroying a Socket closes the connection ~Socket(); /** * Initializes the socket framework. Must be called * once by program before sockets are used. Note * that SocketClient and SocketServer both should call * init automatically when they are first used. * * @return 0 on success, or -1 on failure. */ static int initSocketFramework(); /** * Gets whether the socket framework has been initialized. * * @return true if the framework has been initialized. */ static char isFrameworkInitialized(); /** * Sends bytes through this socket. * * @param inBuffer the buffer of bytes to send. * @param inNumBytes the number of bytes to send. * @param inAllowedToBlock set to false to prevent blocking. * Defaults to true. * * @return the number of bytes sent successfully, * or -1 for a socket error. * Returns -2 if not allowed to block and the operation * would block. */ int send( unsigned char *inBuffer, int inNumBytes, char inAllowedToBlock = true ); /** * Receives bytes from this socket. * * @param inBuffer the buffer where received bytes will be put. * Must be pre-allocated memory space. * @param inNumBytes the number of bytes to read from the socket. * @param inTimeout the timeout for this receive operation in * milliseconds. Set to -1 for an infinite timeout. * -2 is returned from this call in the event of a timeout. * If timeout is set, only available data is returned (up * to, but not more than inNumBytes). The socket does not * wait for all inNumBytes to become available before returning. * Thus, a timeout return value (-2) means no data was available * before the timeout. If some data is available, we never * time out. If no timeout is set (-1 for infinite), the socket * waits for all inNumBytes to arrive before returning. * Thus, setting a timeout of 0 effectively puts the socket * into non-blocking mode. * * @return the number of bytes read successfully, * or -1, -2 for a socket error or timeout, respectively. */ int receive( unsigned char *inBuffer, int inNumBytes, long inTimeout ); /** * Flushes sent data through the socket. * * Intended to be called before deleting the socket to ensure * that data goes through. The mechanism used by this function * does not guarantee that data goes through, but it works * better than simply closing the socket on many platforms. * * The method used by this call works best if the remote * host closes the connection. For example, if we send the * remote host a "connection closing" indicator, then * call writeFlushBeforeClose, and the remote host closes * the connection upon receiving the indicator, all of our * sent data will be received by the remote host after * writeFlushBeforeClose returns (assuming that the maximum * time is long enough for the remote host to actually close * the connection). * * Good maximum times should be in the several-second range, * though the Apache system uses 30 seconds. For slow connections, * this might be necessary. * * This call will cause any received data to be discarded. * * This call DOES NOT close the socket. The socket must be deleted * as usual after this call returns. * * @param inMaxTimeInMilliseconds the maximum time to wait * in milliseconds. This call may block slightly longer than * this if the remote host is still sending data. */ void sendFlushBeforeClose( int inMaxTimeInMilliseconds ); /** * Forces this socket's connection to break, causing any blocked * send or receive operations to fail. * * Note that this operation is inherently not thread-safe, so * some external mechanism must be used to ensure that the socket * is not destroyed by another thread before breakConnection is called. * The SocketManager class is one such external mechanism. */ void breakConnection(); /** * Gets the host connected to the other end of this socket. * * @return the address of the remote host, or NULL if obtaining * the address fails. The port of the returned address * will always be set to the port that the host is connecting from. * Must be destroyed by caller if non-NULL. */ HostAddress *getRemoteHostAddress(); /** * Gets the local address attached to this socket. * * Getting the local address from a socket is more * accurate than non-connected methods (for example, the methods * used in HostAddress.h implementations. * * @return the address of the local host, or NULL if obtaining * the address fails. The port of the returned address * will always be set to 0. * Must be destroyed by caller if non-NULL. */ HostAddress *getLocalHostAddress(); /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; private: static char sInitialized; char mIsConnectionBroken; }; inline Socket::Socket() : mIsConnectionBroken( false ) { } inline char Socket::isFrameworkInitialized() { return sInitialized; } inline void Socket::sendFlushBeforeClose( int inMaxTimeInMilliseconds ) { unsigned char *tempBuffer = new unsigned char[1]; int numRead = -2; int timeout = 1000; if( timeout > inMaxTimeInMilliseconds ) { timeout = inMaxTimeInMilliseconds; } long totalTimeout = 0; unsigned long startSec; unsigned long startMsec; Time::getCurrentTime( &startSec, &startMsec ); // keep reading data from socket until we get an error // or wait too long (pass our max timeout) while( numRead != -1 && totalTimeout < inMaxTimeInMilliseconds ) { numRead = this->receive( tempBuffer, 1, timeout ); // track total time whether the receive timed out or not totalTimeout = (long)( Time::getMillisecondsSince( startSec, startMsec ) ); } delete [] tempBuffer; } #endif