SockIt
Public Member Functions | Protected Member Functions | Private Member Functions | Private Attributes

TcpClient Class Reference

#include <TcpClient.h>

Inheritance diagram for TcpClient:
Tcp Client NetworkObject

List of all members.

Public Member Functions

 TcpClient (const string &host, int port, boost::asio::io_service &io_service)
 TcpClient (const string &host, int port, boost::asio::io_service &io_service, map< string, string > options)
virtual ~TcpClient ()
virtual void send (const string &data)
virtual void send_bytes (const vector< byte > &bytes)
virtual void shutdown ()
virtual int get_port ()
virtual string get_host ()
 FB_JSAPI_EVENT (disconnect, 1,(const string &))
 FB_JSAPI_EVENT (connect, 0,())

Protected Member Functions

virtual void fire_error_event (const string &message)
virtual void fire_disconnect_event (const string &message)
virtual void fire_data_event (const string data, boost::shared_ptr< tcp::socket > connection)
virtual void close ()

Private Member Functions

 TcpClient (const TcpClient &other)
void init ()
void init_socket ()
void resolve_handler (const boost::system::error_code &error_code, tcp::resolver::iterator endpoint_iterator)
void connect_handler (const boost::system::error_code &error_code, tcp::resolver::iterator endpoint_iterator)
void listen ()
void flush ()

Private Attributes

boost::shared_ptr< tcp::socket > connection
boost::shared_ptr< tcp::resolver > resolver
bool connected
boost::mutex connected_mutex
queue< string > data_queue
boost::mutex data_queue_mutex

Detailed Description

This class represents a TCP client, which inherits basic TCP handling functionality from Tcp, and defines additional functionality to resolve and connect to a remote host.

Definition at line 26 of file TcpClient.h.


Constructor & Destructor Documentation

TcpClient::TcpClient ( const string &  host,
int  port,
boost::asio::io_service &  io_service 
)

Builds a TCP client to connect to a specified remote host and port, and begins asynchronously resolving and connecting to the remote host, given an I/O service to asynchronously perform I/O requests.

Parameters:
hostThe hostname of the remote host to which this client will connect
portThe port of the remote host to which this client will connect
io_serviceThe I/O service to use to perform asynchronous I/O requests

Definition at line 10 of file TcpClient.cpp.

References connection, Logger::error(), Tcp::failed, init(), and resolver.

                                                                                    :
    Tcp(host, port, io_service), resolver(new tcp::resolver(io_service)), connection(new tcp::socket(io_service))
{
    // Check that the connection and resolver are valid, and fail gracefully if they are not
    if (!resolver.get() || !connection.get())
    {
        failed = true;
        string message("Failed to initialize TCP client, failed to initialize properly");
        Logger::error(message, port, host);
        fire_error(message);
        return;
    }

    init();
}
TcpClient::TcpClient ( const string &  host,
int  port,
boost::asio::io_service &  io_service,
map< string, string >  options 
)

Builds a TCP client to connect to a specified remote host and port, and begins asynchronously resolving and connecting to the remote host, given an I/O service to asynchronously perform I/O requests.

Parameters:
hostThe hostname of the remote host to which this client will connect
portThe port of the remote host to which this client will connect
io_serviceThe I/O service to use to perform asynchronous I/O requests
optionsA map of options specifying the behavior of the socket

Definition at line 26 of file TcpClient.cpp.

References connection, Logger::error(), Tcp::failed, init(), Tcp::parse_args(), and resolver.

                                                                                                                 :
    Tcp(host, port, io_service), resolver(new tcp::resolver(io_service)), connection(new tcp::socket(io_service))
{
    // Check that the connection and resolver are valid, and fail gracefully if they are not
    if (!resolver.get() || !connection.get())
    {
        failed = true;
        string message("Failed to initialize TCP client, failed to initialize properly");
        Logger::error(message, port, host);
        fire_error(message);
        return;
    }

    parse_args(options);

    init();
}
TcpClient::~TcpClient ( ) [virtual]

Deconstructs a TCP client, immediately calling close to shutdown this client's socket and stop listening for responses.

Definition at line 114 of file TcpClient.cpp.

References close().

{
    close();
}
TcpClient::TcpClient ( const TcpClient other) [private]

Disallows copying a TCP client


Member Function Documentation

void TcpClient::close ( ) [protected, virtual]

Immediately cancels any pending operations and closes this client's socket.

Implements Tcp.

Definition at line 119 of file TcpClient.cpp.

References connection, Tcp::host, Tcp::port, resolver, Tcp::waiting_to_shutdown, and Logger::warn().

Referenced by shutdown(), and ~TcpClient().

{
    waiting_to_shutdown = true;
    resolver->cancel();

    // Shutdown the IO service, cancel any transfers on the socket, and close the socket
    if (connection->is_open())
    {
        try
        {
            connection->shutdown(connection->shutdown_both);
        }
        catch (...)
        {
            Logger::warn("Socket shutdown improperly, proceeding anyway", port, host);
        }

        connection->close();
    }
    else
    {
        Logger::warn("Failed to cleanly shutdown TCP client connection, continuing anyways", port, host);
    }
}
void TcpClient::connect_handler ( const boost::system::error_code &  error_code,
tcp::resolver::iterator  endpoint_iterator 
) [private]

I/O handler invoked when this client has attempted to connect to its remote host. If the connection failed, this handler will reattempt the connection by using another resolver if possible, and otherwise fail permanently.

Parameters:
error_codeThe error code encountered when trying to receive data, if any occurred. On success, this value is zero, and nonzero on error.
endpoint_iteratorAllows the client to iterate through resolvers to retry the connection if it initially fails to connect.

Definition at line 278 of file TcpClient.cpp.

References connected, connected_mutex, connection, Tcp::disconnect_errors, Logger::error(), flush(), Tcp::host, Logger::info(), Tcp::port, Tcp::receive_buffer, Tcp::receive_handler(), resolve_handler(), resolver, and Logger::warn().

Referenced by resolve_handler().

{
    // If there was an error to connect, log the error and abort connection
    if (error_code)
    {
        // Check for disconnection errors
        std::set<boost::system::error_code>::iterator find_result = disconnect_errors.find(error_code);
        if (find_result != disconnect_errors.end())
        {
            if (error_code == boost::asio::error::operation_aborted)
            {
                Logger::warn("TCP connect was aborted");
                return;
            }
            else
            {
                string message("TCP connect failed, disconnected: '" + error_code.message() + "'");
                Logger::warn(message, port, host);
                fire_disconnect(message);
                return;
            }
        }

        // Check if this was not the last possible connection in the iterator
        tcp::resolver::iterator end;
        if (endpoint_iterator != end && error_code == boost::asio::error::host_not_found)
        {
            // Create a query to resolve this host & port
            tcp::resolver::query query(host, boost::lexical_cast<string>(port));

            // Try the next possible endpoint
            resolver->async_resolve(query, boost::bind(&TcpClient::resolve_handler, this, _1, endpoint_iterator++));
        }
        else
        {
            // If we've tried every possible endpoint, fail permanently
            string message("Failed to connect to host, with message: '" + error_code.message() + "'");
            Logger::error(message, port, host);
            fire_error(message);
            return;
        }
    }

    // Log success, and record that we are now connected
    Logger::info("Connection established to host", port, host);
    fire_connect();

    // Start receiving data on this connection
    connection->async_receive(boost::asio::buffer(receive_buffer),
            boost::bind(&TcpClient::receive_handler, this, _1, _2, connection, host, port));

    // Flush data from the queue if necessary
    flush();

    connected_mutex.lock();

    connected = true;
    connected_mutex.unlock();
}
TcpClient::FB_JSAPI_EVENT ( disconnect  ,
,
(const string &)   
)

The javascript event fired on a disconnect-type network error. This is fired on the following boost errors:

  • boost::asio::error::connection_reset
  • boost::asio::error::eof
  • boost::asio::error::connection_aborted
  • boost::asio::error::operation_aborted
TcpClient::FB_JSAPI_EVENT ( connect  ,
,
()   
)

The javascript event fired once a client has connected to its remote endpoint, or once a server has accepted a connection successfully (but has not yet receieved data), in this class, the former.

void TcpClient::fire_data_event ( const string  data,
boost::shared_ptr< tcp::socket >  connection 
) [protected, virtual]

Helper to fire data event to javascript.

Parameters:
dataThe data received
connectionThe connection on which to reply to the data

Implements Tcp.

Definition at line 378 of file TcpClient.cpp.

{
    fire_data(boost::make_shared<TcpEvent>(this, connection, data));
}
void TcpClient::fire_disconnect_event ( const string &  message) [protected, virtual]

Helper to fire an disconnect error event to javascript.

Parameters:
messageThe error message

Implements Tcp.

Definition at line 373 of file TcpClient.cpp.

{
    fire_disconnect(message);
}
void TcpClient::fire_error_event ( const string &  message) [protected, virtual]

Helper to fire an error event to javascript.

Parameters:
messageThe error message

Implements Tcp.

Definition at line 368 of file TcpClient.cpp.

{
    fire_error(message);
}
void TcpClient::flush ( ) [private]

Helper function to flush the queue of data if sends are requested before the client is connected

Definition at line 338 of file TcpClient.cpp.

References connection, TcpEvent::data, data_queue, data_queue_mutex, Tcp::host, Tcp::port, and Tcp::send_handler().

Referenced by connect_handler(), and send().

{
    data_queue_mutex.lock();
    while (!data_queue.empty())
    {
        data_queue_mutex.unlock();

        // Pull the next data chunk to be sent off the queue
        data_queue_mutex.lock();
        string data = data_queue.front();
        data_queue.pop();
        data_queue_mutex.unlock();

        // Asynchronously send the data across the connection
        connection->async_send(boost::asio::buffer(data.data(), data.size()),
                boost::bind(&TcpClient::send_handler, this, _1, _2, data, host, port, connection));
    }
    data_queue_mutex.unlock();
}
string TcpClient::get_host ( void  ) [virtual]

Returns the host to which this client connects

Implements Client.

Definition at line 363 of file TcpClient.cpp.

References Tcp::host.

{
    return host;
}
int TcpClient::get_port ( void  ) [virtual]

Returns the port of the remote host on which this client connects

Implements Client.

Definition at line 358 of file TcpClient.cpp.

References Tcp::port.

{
    return port;
}
void TcpClient::init ( ) [private]

Helper function to initialize this client

Definition at line 44 of file TcpClient.cpp.

References connected, Logger::error(), Tcp::failed, Tcp::host, Logger::info(), Tcp::log_options(), Tcp::port, resolve_handler(), resolver, and Tcp::using_ipv6.

Referenced by TcpClient().

{
    connected = false;

    Logger::info(
            "Initializing TCP client to host '" + boost::lexical_cast<string>(host) + "' on port " + boost::lexical_cast<string>(port),
            port, host);
    log_options();
    Logger::info(
            "Trying to resolve DNS information for host " + boost::lexical_cast<string>(host) + "', port " + boost::lexical_cast<string>(
                    port), port, host);

    // Create a query to resolve this host & port
    if (using_ipv6 && *using_ipv6)
    {
        // Asynchronously resolve the remote host, and once the host is resolved, create a connection
        tcp::resolver::query query(tcp::v6(), host, boost::lexical_cast<string>(port),
                boost::asio::ip::resolver_query_base::numeric_service);
        if (resolver.get())
            resolver->async_resolve(query, boost::bind(&TcpClient::resolve_handler, this, _1, _2));
        else
        {
            failed = true;
            string message("TCP client failed to resolve, invalid resolver");
            Logger::error(message, port, host);
            fire_error(message);
        }
    }
    else
    {
        // Asynchronously resolve the remote host, and once the host is resolved, create a connection
        tcp::resolver::query query(tcp::v4(), host, boost::lexical_cast<string>(port),
                boost::asio::ip::resolver_query_base::numeric_service);
        if (resolver.get())
            resolver->async_resolve(query, boost::bind(&TcpClient::resolve_handler, this, _1, _2));
        else
        {
            failed = true;
            string message("TCP client failed to resolve, invalid resolver");
            Logger::error(message, port, host);
            fire_error(message);
        }
    }
}
void TcpClient::init_socket ( ) [private]

Initialize the properties of this socket

Definition at line 89 of file TcpClient.cpp.

References connection, Tcp::do_not_route, Tcp::keep_alive, Tcp::keep_alive_timeout, Tcp::no_delay, and Tcp::set_tcp_keepalive().

Referenced by resolve_handler().

{
    // Set the socket options for this client's TCP socket
    if (do_not_route)
    {
        boost::asio::socket_base::do_not_route option(*do_not_route);
        connection->set_option(option);
    }
    if (keep_alive)
    {
        boost::asio::socket_base::keep_alive option(*keep_alive);
        connection->set_option(option);
    }
    if (no_delay)
    {
        boost::asio::ip::tcp::no_delay option(*no_delay);
        connection->set_option(option);
    }
    if (keep_alive_timeout)
    {
        // Set the TCP keep-alive timeout - ignores return value
        set_tcp_keepalive(connection);
    }
}
void TcpClient::listen ( ) [private]

Helper function that will listen for incoming data on the TCP connection for the client, specifically responses to data already sent.

void TcpClient::resolve_handler ( const boost::system::error_code &  error_code,
tcp::resolver::iterator  endpoint_iterator 
) [private]

I/O handler invoked when the remote host is resolved. This handler attempts to asynchronously establish a TCP connection with the remote host.

Parameters:
error_codeThe error code encountered when trying to receive data, if any occurred. On success, this value is zero, and nonzero on error.
endpoint_iteratorAllows the client to iterate through resolvers to retry the connection if it initially fails to connect.

Definition at line 215 of file TcpClient.cpp.

References connect_handler(), connection, Tcp::disconnect_errors, Logger::error(), Tcp::failed, Tcp::host, Logger::info(), init_socket(), Tcp::port, Tcp::using_ipv6, and Logger::warn().

Referenced by connect_handler(), and init().

{
    // If we encountered an error
    if (error_code)
    {
        // Check for disconnection errors
        std::set<boost::system::error_code>::iterator find_result = disconnect_errors.find(error_code);
        if (find_result != disconnect_errors.end())
        {
            if (error_code == boost::asio::error::operation_aborted)
            {
                Logger::warn("TCP resolve was aborted");
                return;
            }
            else
            {
                string message("TCP resolve failed, disconnected: '" + error_code.message() + "'");
                Logger::warn(message, port, host);
                fire_disconnect(message);
                return;
            }
        }

        // We failed permanently, fail permanently and log it
        string message("Failed to resolve host with error: " + error_code.message());
        Logger::error(message, port, host);
        fire_error(message);
        return;
    }

    if (connection.get())
    {
        // Attempt to connect to the endpoint, using IPv6 if specified
        tcp::endpoint receiver_endpoint = *endpoint_iterator;
        if (using_ipv6 && *using_ipv6)
        {
            connection->open(tcp::v6());
        }
        else
        {
            connection->open(tcp::v4());
        }

        // Initialize the socket if it's open
        if (!connection->is_open())
            init_socket();

        // Log success
        Logger::info("Host has been resolved, attempting to connect to host", port, host);
        fire_resolve();

        // Try to asynchronously establish a connection to the host
        connection->async_connect(receiver_endpoint, boost::bind(&TcpClient::connect_handler, this, _1, endpoint_iterator));
    }
    else
    {
        failed = true;
        string message("TCP client failed to resolve, invalid connection");
        Logger::error(message, port, host);
        fire_error(message);
    }
}
void TcpClient::send ( const string &  data) [virtual]

Asynchronously sends data to the remote host to which this client is connected.

Parameters:
dataThe data to send across the wire

Implements Client.

Definition at line 177 of file TcpClient.cpp.

References Tcp::active_jobs, Tcp::active_jobs_mutex, connected, connected_mutex, connection, data_queue, data_queue_mutex, Logger::error(), Tcp::failed, flush(), Tcp::host, Tcp::port, and Tcp::send_handler().

Referenced by send_bytes().

{
    if (failed)
    {
        // Log & fire an error
        string message("Trying to send data on a TCP client that has permanently failed!");
        Logger::error(message, port, host);
        return;
    }

    // If we're not already connected, then queue this data to be send
    connected_mutex.lock();
    bool connected_now = connected;
    connected_mutex.unlock();

    if (!connected_now)
    {
        active_jobs_mutex.lock();
        active_jobs++;
        active_jobs_mutex.unlock();
        data_queue_mutex.lock();
        data_queue.push(data);
        data_queue_mutex.unlock();
    }
    else
    {
        // Check if the queue needs to be flushed, and if so, flush it
        flush();

        // Send the data, and record that we've started a new send job
        active_jobs_mutex.lock();
        active_jobs++;
        active_jobs_mutex.unlock();
        connection->async_send(boost::asio::buffer(data.data(), data.size()),
                boost::bind(&TcpClient::send_handler, this, _1, _2, data, host, port, connection));
    }
}
void TcpClient::send_bytes ( const vector< byte > &  bytes) [virtual]

Asynchronously sends bytes to the remote host to which this client is connected.

Parameters:
bytesThe bytes of data to send across the wire

Implements Client.

Definition at line 165 of file TcpClient.cpp.

References TcpEvent::data, and send().

{
    string data;

    for (int i = 0; i < bytes.size(); i++)
    {
        data.push_back((unsigned char) bytes[i]);
    }

    send(data);
}
void TcpClient::shutdown ( ) [virtual]

Gracefully shutdown this TCP client, waiting until all sends have completed before freeing all resources for this TCP client and shutting down any open connections. This function is exposed the javascript API.

Implements NetworkObject.

Definition at line 144 of file TcpClient.cpp.

References Tcp::active_jobs, Tcp::active_jobs_mutex, close(), Logger::error(), Tcp::failed, Tcp::host, Tcp::port, and Tcp::waiting_to_shutdown.

{
    if (!failed)
    {
        active_jobs_mutex.lock();
        int current_jobs = active_jobs;
        active_jobs_mutex.unlock();
        waiting_to_shutdown = true;
        if (current_jobs == 0)
        {
            fire_close();
            close();
        }
    }
    else
    {
        // Log & fire an error
        Logger::error("Trying to start the server listening, but the server has permanently failed!", port, host);
    }
}

Member Data Documentation

bool TcpClient::connected [private]

A flag recording whether this client is connected yet or not, used to queue send requests made before this client is connected if necessary.

Definition at line 197 of file TcpClient.h.

Referenced by connect_handler(), init(), and send().

boost::mutex TcpClient::connected_mutex [private]

A mutex used to access the queue for pending jobs

Definition at line 202 of file TcpClient.h.

Referenced by connect_handler(), and send().

boost::shared_ptr<tcp::socket> TcpClient::connection [private]

A shared reference to the socket used to connect to the remote host

Definition at line 186 of file TcpClient.h.

Referenced by close(), connect_handler(), flush(), init_socket(), resolve_handler(), send(), and TcpClient().

queue<string> TcpClient::data_queue [private]

A queue of data to be sent, which fills as 'send' or 'send_bytes' commands occur before the client is connected

Definition at line 207 of file TcpClient.h.

Referenced by flush(), and send().

boost::mutex TcpClient::data_queue_mutex [private]

A mutex used to access the queue for pending jobs

Definition at line 212 of file TcpClient.h.

Referenced by flush(), and send().

boost::shared_ptr<tcp::resolver> TcpClient::resolver [private]

Resolver object provided by boost to resolve the remote hostname and port

Definition at line 191 of file TcpClient.h.

Referenced by close(), connect_handler(), init(), and TcpClient().


The documentation for this class was generated from the following files:
 All Classes Files Functions Variables Typedefs Friends Defines