Initial implementation of the client side of the new network API.

Also added a method to get the ip address of the server in ANA's client.
This commit is contained in:
Guillermo Biset 2010-07-29 19:10:54 +00:00
parent 830022f221
commit a572690b5d
6 changed files with 214 additions and 42 deletions

View file

@ -4,7 +4,7 @@ PROJECT_NAME = ana
PROJECT_NUMBER =
OUTPUT_DIRECTORY = doc/code
OUTPUT_DIRECTORY = code
CREATE_SUBDIRS = YES

View file

@ -743,6 +743,9 @@ namespace ana
*/
virtual void set_connect_timeout( size_t ms ) = 0;
/** Returns the string representing the ip address of the connected client. */
virtual std::string ip_address() const = 0;
/** Standard destructor. */
virtual ~client() {}
};

View file

@ -268,6 +268,11 @@ void asio_client::expecting_message( size_t ms_until_timeout )
wait_for_incoming_message( ms_until_timeout );
}
std::string asio_client::ip_address() const
{
return socket_.remote_endpoint().address().to_string();
}
void asio_client::disconnect_listener()
{
io_service_.stop();

View file

@ -106,6 +106,8 @@ class asio_client : public ana::client,
virtual void expecting_message( size_t ms_until_timeout );
virtual std::string ip_address() const;
void handle_connect(const boost::system::error_code& ec,
tcp::resolver::iterator endpoint_iterator,
ana::connection_handler*,

View file

@ -17,82 +17,196 @@
* See the COPYING file for more details.
*/
/* STL headers ----------------------------------- */
#include <string>
#include <stringstream>
/* Boost headers --------------------------------- */
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
/* Local headers --------------------------------- */
#include "network_askync.hpp"
#include "serialization/parser.hpp"
/* ----------------------------------------------- */
using namespace network;
/* ----------------------------------- Utility Functions ----------------------------------- */
namespace utils
{
std::string compress_config( const config& cfg )
{
std::ostringstream out;
compress_config( cfg, out );
return out.str( );
}
void compress_config( const config& cfg, std::ostringstream& out)
{
boost::iostreams::filtering_stream<boost::iostreams::output> filter;
filter.push(boost::iostreams::gzip_compressor());
filter.push(out);
write(filter, cfg);
out.flush();
}
void read_config( const ana::detail::read_buffer& buffer, config& cfg)
{
std::istringstream input( buffer->string() );
read_gz(cfg, input);
}
}
/* --------------------------------- Client Implementation --------------------------------- */
client::client( handler& handler ) :
client_( ... )
client::client( handler& handler,
const std::string& address = "server.wesnoth.org",
const std::string& port = "15000" )
:
status_( DISCONNECTED ),
client_( address, port ),
handler_( handler ),
wesnoth_id_( 0 )
{
client_->set_listener_handler( this );
client_->set_raw_data_mode();
client_->run();
client_->start_logging();
}
client::~client()
{
client_->disconnect();
delete client_;
}
void client::set_send_timeout( ana::timeout_policy type, size_t ms )
{
client_->set_timeouts( type, ms );
}
void client::set_connect_timeout( size_t ms )
void client::async_connect( size_t timeout )
{
//TODO: Deal with the case that the client is not disconnected.
client_->set_connect_timeout( timeout );
client_->connect();
}
void client::async_connect( const std::string& address = "server.wesnoth.org",
const std::string& port = "15000" )
void client::async_connect_through_proxy( size_t timeout,
const std::string& proxy_addr,
const std::string& proxy_port,
const std::string& user_name,
const std::string& password)
{
client_->set_connect_timeout( timeout );
client_->connect_trough_proxy(proxy_addr, proxy_port, this, user_name, password);
}
void client::async_connect_through_proxy( const std::string& proxy_addr = "server.wesnoth.org",
const std::string& proxy_port = "15000",
const std::string& user_name = "",
const std::string& password = "")
operation_id client::async_send( const config& )
{
return client_->send( ana::buffer( compress_config( config ) ) );
}
operation_id client::async_send( const config&, ana::send_type = ana::COPY_BUFFER)
void client::waiting_for_message( size_t time )
{
client_->expecting_message( time );
}
void client::waiting_for_message( time )
{
}
ana::stats* client::get_stats( ana::stat_type = ana::ACCUMULATED )
ana::stats* client::get_stats( ana::stat_type type )
{
return client_->get_stats( type );
}
void client::cancel_pending( )
{
client_->cancel_pending();
}
void client::disconnect()
{
client_->disconnect();
}
std::string client::ip_address_of_server() const
{
return client_->ip_address();
}
network::wesnoth_id client::get_wesnoth_id() const
{
return wesnoth_id_;
}
/*------- Private methods ---------------------------------*/
void client::set_wesnoth_id( wesnoth_id id)
{
wesnoth_id_ = id;
}
/*------- Client handlers for ANA's network events. -------*/
void client::handle_connect( ana::error_code error, net_id server_id )
{
ana::serializer::bostream bos;
uint32_t handshake( 0 );
bos << handshake;
client_->send( ana::buffer( bos.str()), this );
status_ = PENDING_HANDSHAKE;
}
void client::handle_disconnect( ana::error_code error, net_id server_id)
{
handler_.handle_disconnect( error, server_id );
}
void client::handle_receive( ana::error_code error, ana::net_id id, ana::detail::read_buffer buf)
{
if ( status_ == CONNECTED )
{
config cfg;
if ( ! error )
read_config( buf, cfg)
handler_.handle_receive( error, id, cfg );
}
else if ( status_ == PENDING_HANDSHAKE )
{
wesnoth_id my_id;
ana::serializer::bistream bis;
client->wait_raw_object(bis, sizeof(my_id) );
bis >> my_id;
ana::network_to_host_long( my_id );
set_wesnoth_id( my_id );
client_->set_header_first_mode();
client_->run_listener();
}
else
throw std::runtime_error("Can't receive a message while disconnected.");
}
void client::handle_send( ana::error_code error, ana::net_id client, ana::operation_id op_id)
{
handler_.handle_receive( error, client, op_id );
}
/* --------------------------------- Server Implementation --------------------------------- */

View file

@ -68,6 +68,14 @@ Feature Requests:
/** Namespace of the asynchronous network API. */
namespace network
{
/**
* A wesnoth ID of a network component.
* For instance, when a client connects to a server, it is a assigned a server side
* wesnoth_id from the server.
*/
typedef uint32_t wesnoth_id;
/**
* Main interface for handlers of Wesnoth network events.
*
@ -151,11 +159,19 @@ namespace network
/**
* Create a client, and associate a handler object to it.
*
* Note: The parameter is a reference because it can't be a NULL pointer.
* @param handler : The handler of the network events of this client.
* @param address : The address of the server you are trying to connect to. Defaults
* to Wesnoth's official server.
* @param port : The port you are trying to connect to. Defaults to Wesnoth's default.
*
* Note: The handler parameter is a reference because it can't be a NULL pointer.
*/
client( handler& handler );
client( handler& handler,
const std::string& address = "server.wesnoth.org",
const std::string& port = "15000" );
// Idea: Add a set_handler method to change handlers during execution.
/** Standard destructor. */
~client();
/**
@ -167,14 +183,6 @@ namespace network
*/
void set_send_timeout( ana::timeout_policy type, size_t ms = 0 );
/**
* Set timeouts for connect operations for the client.
*
* Examples:
* - set_connect_timeout( ana::time::seconds( 10 ) );
*/
void set_connect_timeout( size_t ms = 0 );
// Possibilities: Either use set_timeout to set general timeouts for groups of
// operations or have a timeout parameter for each time you call a method.
@ -183,24 +191,25 @@ namespace network
* Attempt an asynchronous connection to a server, it will call the corresponding
* handle_connect with the results eventually.
*
* @param address : The address of the server you are trying to connect to. Defaults
* to Wesnoth's official server.
* @param port : The port you are trying to connect to. Defaults to Wesnoth's default.
* @param time : Time to connection attempt timeout, e.g. ana::time::seconds( 10 ).
* Default: no timeout.
*/
void async_connect( const std::string& address = "server.wesnoth.org",
const std::string& port = "15000" );
void async_connect( size_t timeout = 0 );
/**
* Attempt an asynchronous connection through proxy, will call handle_connect
* with results eventually.
*
* @param time : Time to connection attempt timeout, e.g. ana::time::seconds( 10 ).
* Default: no timeout.
* @param address : The address of the server you are trying to connect to. Defaults
* to Wesnoth's official server.
* @param port : The port you are trying to connect to. Defaults to Wesnoth's default.
* @param user_name : The proxy's user name used for credentials.
* @param password : The proxy's password used for credentials.
*/
void async_connect_through_proxy( const std::string& proxy_addr = "server.wesnoth.org",
void async_connect_through_proxy( size_t timeout = 0,
const std::string& proxy_addr = "server.wesnoth.org",
const std::string& proxy_port = "15000",
const std::string& user_name = "",
const std::string& password = "");
@ -209,26 +218,27 @@ namespace network
* Attempt to send a WML document to the server.
*
* @param config : The WML document to send.
* @param send_type : The type of send operation, use ana::DONT_COPY to avoid
* unnecessary memory duplication. However, you must ensure that
* the config object used will be alive until the call of
* the handle_send with the returned operation_id. The default
* value will be ana::COPY_BUFFER.
*
* @returns the ana::operation_id corresponding to this operation.
*
* @sa ana::operation_id
* @sa ana::send_type
*/
operation_id async_send( const config&, ana::send_type = ana::COPY_BUFFER);
operation_id async_send( const config& );
/**
* Signal the client that you are waiting for a message from the server
* The time parameter indicates how long you are willing to wait
* If a message is received before this time period the appropiate call to
* handle_receive will be made with ana::timeout_error.
* @param time: Indicates how long you are willing to wait. If a message
* is received before this time period the appropiate call to
* handle_receive will be made with ana::timeout_error. To create a time
* duration, use the functions declared in the ana::time namespace.
*
* Example :
* - client.waiting_for_message( ana::time::seconds( 5 ) );
*
* @sa ana::time
*/
void waiting_for_message( time );
void waiting_for_message( size_t time );
/**
* Get network stats for the client, the parameter indicates the time period
@ -247,7 +257,23 @@ namespace network
*/
std::string ip_address_of_server() const;
/**
* Returns the wesnoth_id of this client.
*
* @sa wesnoth_id
*/
wesnoth_id get_wesnoth_id() const;
private:
/* ------------------------------ Private methods -----------------------------*/
/**
* Sets the wesnoth_id of this client.
*
* @sa wesnoth_id
*/
void set_wesnoth_id( wesnoth_id );
/* ------------------------ Inhereted handler methods -------------------------*/
virtual void handle_connect( ana::error_code error, net_id server_id );
@ -262,11 +288,33 @@ namespace network
net_id client,
ana::operation_id op_id);
/* ------------------------------- Private Types ------------------------------*/
/** Connection status. */
enum client_status
{
/** Not connected to the server. */
DISCONNECTED,
/** Waiting for handshake completion. */
PENDING_HANDSHAKE,
/** Connected to the server. */
CONNECTED
};
/* -------------------------------- Attributes --------------------------------*/
client_status status_;
/** The ana::client object representing this client. @sa ana::client */
ana::client* client_;
/** The object handling the network events for this client. */
handler& handler_;
/** The wesnoth_id assigned by the server. */
wesnoth_id wesnoth_id_;
};
/**