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