# portability/ip_sockets.hpp

    #ifndef STLPLUS_IP_SOCKET
    #define STLPLUS_IP_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 Internet-Protocol sockets

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

    namespace stlplus
    {

      //////////////////////////////////////////////////////////////////////////////
      // internals
      // use a PIMPL interface to hide the platform-specifics in the implementation

      class IP_socket_internals;

      ////////////////////////////////////////////////////////////////////////////
      // Types of socket supported

      enum IP_socket_type {undefined_socket_type = -1, TCP = 0, UDP = 1};

      //////////////////////////////////////////////////////////////////////////////
      // Socket class

      class IP_socket
      {
      public:

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

        // create an uninitialised socket
        IP_socket(void);

        // create an initialised socket
        // - type: create either a TCP or UDP socket
        IP_socket(IP_socket_type type);

        // destroy the socket, closing it if open
        ~IP_socket(void);

        // copying is implemented as aliasing
        IP_socket(const IP_socket&);
        IP_socket& operator=(const IP_socket&);

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

        // initialise the socket
        // - type: create either a TCP or UDP socket
        // - returns success status
        bool initialise(IP_socket_type type);

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

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

        //////////////////////////////////////////////////////////////////////////////
        // Socket configuration

        // 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);

        // test whether a socket is ready to communicate
        // - readable: test whether socket is ready to read
        // - writeable: test whether a socket is ready to write
        // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait
        // returns false if not ready or error - use error() method to tell - true if ready
        bool select(bool readable, bool writeable, unsigned timeout = 0);

        // bind the socket to a port so that it can receive from specific address - typically used by a client
        // - remote_address: IP number of remote server to send/receive to/from
        // - local_port: port on local machine to bind to the address
        // - returns success flag
        bool bind(unsigned long remote_address, unsigned short local_port);

        // bind the socket to a port so that it can receive from any address - typically used by a server
        // - local_port: port on local machine to bind to the address
        // - returns success flag
        bool bind_any(unsigned short local_port);

        // set this socket up to be a listening port
        // socket must be bound to a port already
        // - queue: length of backlog queue to manage - may be zero meaning no queue
        // - returns success status
        bool listen(unsigned short queue = 0);

        // test for a connection on the 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) const;

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

        // create a connection - usually used by a client
        // TCP: client connect to a remote server
        // UDP: setup remote address and port for sends
        // - remote_address: IP number already looked up using ip_lookup
        // - remote_port: port to connect to
        // - returns a success flag
        bool connect(unsigned long remote_address, unsigned short remote_port);

        // 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 true if connected and ready to communicate, false if not ready or error
        bool connected(unsigned timeout = 0);

        ////////////////////////////////////////////////////////////////////////////
        // 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);

        // send data through a connection-based (TCP) 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);

        // send data through a connectionless (UDP) socket
        // the data will be sent as a single packet
        // - packet: string containing data to be sent - any data successfully sent is removed
        // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote
        // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote
        // - returns success flag
        bool send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port);

        // send data through a connectionless (UDP) socket
        // the data will be sent as a single packet
        // only works if the socket has been connected to remote
        // - packet: string containing data to be sent - any data successfully sent is removed
        // - returns success flag
        bool send_packet(std::string& packet);

        // 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 wait = 0);

        // receive data through a connection-based (TCP) 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);

        // receive data through a connectionless (UDP) socket
        // - packet: string receiving data from socket - any data successfully received is appended
        // - remote_address: returns the address of the remote host received from
        // - remote_port: returns the port of the remote host received from
        // - returns success flag
        bool receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port);

        // variant of above which does not give back the address and port of the sender
        // receive data through a connectionless (UDP) socket
        // - packet: string receiving data from socket - any data successfully received is appended
        // - returns success flag
        bool receive_packet(std::string& packet);

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

        // gets the type of socket
        // - returns undefined_socket_type, TCP or UDP
        IP_socket_type type(void) const;

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

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

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

        ////////////////////////////////////////////////////////////////////////////
        // 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

        // indicate an error - mostly used internally, you can set your own errors - use a negative code
        void set_error (int error, const std::string& message) const;

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

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

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

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

      private:
        friend class IP_socket_internals;
        IP_socket_internals* m_impl;
      };

    } // end namespace stlplus

    #endif