# portability/tcp_sockets.hpp

    #ifndef STLPLUS_TCP_SOCKET
    #define STLPLUS_TCP_SOCKET
    ////////////////////////////////////////////////////////////////////////////////

    //   Author:    Andy Rushton
    //   Copyright: (c) Southampton University 1999-2004
    //              (c) Andy Rushton           2004 onwards
    //   License:   BSD License, see ../docs/license.html

    //   A platform-independent (Unix and Windows anyway) interface to TCP sockets

    ////////////////////////////////////////////////////////////////////////////////

    #include "portability_fixes.hpp"
    #include "ip_sockets.hpp"
    #include <string>

    namespace stlplus
    {

      //////////////////////////////////////////////////////////////////////////////
      // Server Classes: A server creates a listening port which waits for incoming
      // connections. This is placed on the port number appropriate for the service
      // - for example, a Telnet server would typically use port 23. For a new
      // service you should of course use any port not allocated to a standard
      // service. I believe that RFC 1700 defines the standard service port numbers.
      // When an incoming connection is made, the server accepts it and in the
      // process creates a new connection on a different port. This leaves the
      // standard port listening for further connections. In effect, the server
      // farms out the handling of the connections themselves and only takes
      // responsibility for accepting the connection. This is reflected in the class
      // structure. A TCP_server performs the listening and accepting roles, but
      // creates a TCP_connection to handle the accepted connection.
      //////////////////////////////////////////////////////////////////////////////

      //////////////////////////////////////////////////////////////////////////////
      // connection class created by TCP_server when a connection is accepted
      // this is then used to perform any communications with the remote client

      class TCP_connection : protected IP_socket
      {
      private:
        // constructor to actually initialise the class - can only be constructed by TCP_server
        friend class TCP_server;
        TCP_connection(const IP_socket& socket);

      public:

        ////////////////////////////////////////////////////////////////////////////
        // constructors/destructors

        // create an uninitialised connection
        TCP_connection(void);

        ////////////////////////////////////////////////////////////////////////////
        // initialisation, connection control
        // Note: TCP connections can only be initialised by a TCP server

        // test whether this is an initialised socket
        // - returns whether this is initialised
        // bool initialised(void) const;
        using IP_socket::initialised;

        // close, i.e. disconnect the socket
        // - returns a success flag
        // bool close(void);
        using IP_socket::close;

        ////////////////////////////////////////////////////////////////////////////
        // sending/receiving

        // test whether a socket is connected and ready to send data, returns if ready or on timeout
        // - timeout: how long to wait in microseconds if not connected yet (blocking)
        // - returns status
        // bool send_ready(unsigned timeout = 0);
        using IP_socket::send_ready;

        // send data through the socket - if the data is long only part of it may
        // be sent. The sent part is removed from the data, so the same string can
        // be sent again and again until it is empty.
        // - data: string containing data to be sent - any data successfully sent is removed
        // - returns success flag
        // bool send (std::string& data);
        using IP_socket::send;

        // test whether a socket is connected and ready to receive data, returns if ready or on timeout
        // - timeout: how long to wait in microseconds if not connected yet (blocking)
        // - returns status
        // bool receive_ready(unsigned timeout = 0);
        using IP_socket::receive_ready;

        // receive data through the socket - if the data is long only part of it
        // may be received. The received data is appended to the string, building
        // it up in stages, so the same string can be received again and again
        // until all information has been received.
        // - data: string receiving data from socket - any data successfully received is appended
        // - returns success flag
        // bool receive (std::string& data);
        using IP_socket::receive;

        ////////////////////////////////////////////////////////////////////////////
        // informational

        // the local port number of the connection
        // - returns the port number, 0 if not bound to a port
        // unsigned short local_port(void) const;
        using IP_socket::local_port;

        // the remote address of the connection
        // - returns the address, 0 if not connected
        // unsigned long remote_address(void) const;
        using IP_socket::remote_address;

        // the remote port number of the connection
        // - returns the port number, 0 if not connected to a port
        // unsigned short remote_port(void) const;
        using IP_socket::remote_port;

        ////////////////////////////////////////////////////////////////////////////
        // error handling
        // errors are set internally
        // an error code of 0 is the test for no error, don't rely on the message being an empty string
        // an error code != 0 means an error, then there will be a message explaining the error

        // if an error is set it stays set - so you must clear it before further operations
        // void clear_error(void) const;
        using IP_socket::clear_error;

        // get the error code of the last error
        // int error(void) const;
        using IP_socket::error;

        // get the explanatory message of the last error
        // std::string message(void) const;
        using IP_socket::message;

        ////////////////////////////////////////////////////////////////////////////

        // deprecated version of remote_port
        unsigned short port(void) const;

        ////////////////////////////////////////////////////////////////////////////
      };

      //////////////////////////////////////////////////////////////////////////////
      // server class that does the listening on the designated port
      // incoming connections can be queued up to a maximum queue length specified
      // in the constructor/initialise

      class TCP_server : protected IP_socket
      {
      public:

        // create an uninitialised server
        TCP_server(void);

        // initialise a socket and set it up to be a listening port
        // - port: port to listen on
        // - queue: length of backlog queue to manage - may be zero
        // - returns success status
        TCP_server(unsigned short port, unsigned short queue = 0);

        ////////////////////////////////////////////////////////////////////////////
        // initialisation

        // initialise a socket and set it up to be a listening port
        // - port: port to listen on
        // - queue: length of backlog queue to manage - may be zero
        // - returns success status
        bool initialise(unsigned short port, unsigned short queue = 0);

        // test whether this is an initialised socket
        // - returns whether this is initialised
        // bool initialised(void) const;
        using IP_socket::initialised;

        // close, i.e. disconnect the socket
        // - returns a success flag
        // bool close(void);
        using IP_socket::close;

        //////////////////////////////////////////////////////////////////////////////
        // server operation - accepting a connection

        // test for a connection on the object's socket - only applicable if it has been set up as a listening port
        // - timeout: how long to wait in microseconds if not connected yet
        // - returns true if a connection is ready to be accepted
        // bool accept_ready(unsigned timeout = 0);
        using IP_socket::accept_ready;

        // accept a connection on the object's socket - only applicable if it has been set up as a listening port
        // - returns the connection as a new socket
        TCP_connection accept(void);

        ////////////////////////////////////////////////////////////////////////////
        // error handling
        // errors are set internally
        // an error code of 0 is the test for no error, don't rely on the message being an empty string
        // an error code != 0 means an error, then there will be a message explaining the error

        // if an error is set it stays set - so you must clear it before further operations
        // void clear_error (void) const;
        using IP_socket::clear_error;

        // get the error code of the last error
        // int error(void) const;
        using IP_socket::error;

        // get the explanatory message of the last error
        // std::string message(void) const;
        using IP_socket::message;

        //////////////////////////////////////////////////////////////////////////////

        // deprecated versions of accept_ready and accept
        bool connection_ready(unsigned timeout = 0);
        TCP_connection connection(void);

        //////////////////////////////////////////////////////////////////////////////
      };

      ////////////////////////////////////////////////////////////////////////////////
      // Client Class: a client is simpler in that there is no listening port - you
      // just create a connection and get on with it. Thus the client class does the
      // whole job - create the connection and handle communications to/from it.
      //
      // Blocking mode: To use the client in blocking mode, use non-zero timeout for
      // the initialisation method. In this mode, the connection operation must
      // complete before the call will return or an error is indicated if the
      // timeout is reached without completion. This usage was designed for
      // applications which either just to TCP and nothing else or which do TCP
      // operations in a separate thread.
      //
      // Non-blocking mode: To use the client in non-blocking mode, use a zero
      // timeout for the initialisation method. Instead, you can ask it if it has
      // connected once you've initialised it. It is not an error for it to be
      // initialised but not connected. This usage was designed so that you can poll
      // the connection periodically to implement a timeout for as long as you like for
      // the connection to occur without blocking the thread that uses the client.
      //
      // In both modes, the send_ready/receive_ready methods can be called with any
      // timeout including zero.

      class TCP_client : protected IP_socket
      {
      public:

        // create an uninitialised client
        TCP_client(void);

        // client connect to a server
        // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
        // - remote_port: port number of remote host
        // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed
        TCP_client(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0);

        // client connect to a server
        // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup
        // - remote_port: port number of remote host
        // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed
        TCP_client(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0);

        ////////////////////////////////////////////////////////////////////////////
        // initialisation, connection

        // function for performing IP lookup (i.e. gethostbyname)
        // could be standalone but making it a member means that it can use the socket's error handler
        // i.e. if this fails, the sockets error code will be set - clear it to use the socket again
        // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
        // - returns the IP address as a long integer - zero if there's an error
        // unsigned long ip_lookup(const std::string& remote_address);
        using IP_socket::ip_lookup;

        // client connect to a server
        // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
        // - remote_port: port number of remote host
        // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed
        // - returns a success flag
        bool initialise(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0);

        // client connect to a server
        // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup
        // - remote_port: port number of remote host
        // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed
        // - returns a success flag
        bool initialise(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0);

        // test whether this is an initialised socket
        // - returns whether this is initialised
        // bool initialised(void) const;
        using IP_socket::initialised;

        // test whether a socket is connected and ready to communicate, returns on successful connect or timeout
        // - timeout: how long to wait in microseconds if not connected yet
        // - returns success flag
        // bool connected(unsigned timeout = 0);
        using IP_socket::connected;

        // close, i.e. disconnect the socket
        // - returns a success flag
        // bool close(void);
        using IP_socket::close;

        ////////////////////////////////////////////////////////////////////////////
        // sending/receiving

        // test whether a socket is connected and ready to send data, returns if ready or on timeout
        // - timeout: how long to wait in microseconds if not connected yet (blocking)
        // - returns status
        // bool send_ready(unsigned timeout = 0);
        using IP_socket::send_ready;

        // send data through the socket - if the data is long only part of it may
        // be sent. The sent part is removed from the data, so the same string can
        // be sent again and again until it is empty.
        // - data: string containing data to be sent - any data successfully sent is removed
        // - returns success flag
        // bool send (std::string& data);
        using IP_socket::send;

        // test whether a socket is connected and ready to receive data, returns if ready or on timeout
        // - timeout: how long to wait in microseconds if not connected yet (blocking)
        // - returns status
        // bool receive_ready(unsigned timeout = 0);
        using IP_socket::receive_ready;

        // receive data through the socket - if the data is long only part of it
        // may be received. The received data is appended to the string, building
        // it up in stages, so the same string can be received again and again
        // until all information has been received.
        // - data: string receiving data from socket - any data successfully received is appended
        // - returns success flag
        // bool receive (std::string& data);
        using IP_socket::receive;

        ////////////////////////////////////////////////////////////////////////////
        // informational

        // the local port number of the connection
        // - returns the port number, 0 if not bound to a port
        // unsigned short local_port(void) const;
        using IP_socket::local_port;

        // the remote address of the connection
        // - returns the address, 0 if not connected
        // unsigned long remote_address(void) const;
        using IP_socket::remote_address;

        // the remote port number of the connection
        // - returns the port number, 0 if not connected to a port
        // unsigned short remote_port(void) const;
        using IP_socket::remote_port;

        ////////////////////////////////////////////////////////////////////////////
        // error handling
        // errors are set internally
        // an error code of 0 is the test for no error, don't rely on the message being an empty string
        // an error code != 0 means an error, then there will be a message explaining the error

        // if an error is set it stays set - so you must clear it before further operations
        // void clear_error (void) const;
        using IP_socket::clear_error;

        // get the error code of the last error
        // int error(void) const;
        using IP_socket::error;

        // get the explanatory message of the last error
        // std::string message(void) const;
        using IP_socket::message;

        //////////////////////////////////////////////////////////////////////////////

        // deprecated versions
        unsigned long address(void) const;
        unsigned short port(void) const;

        //////////////////////////////////////////////////////////////////////////////
      };

      ////////////////////////////////////////////////////////////////////////////////

    } // end namespace stlplus

    #endif