Commit 6a2b8d31 authored by louiz’'s avatar louiz’

Far better architecturing in network module

- Renamed interface_* files and classes
- io_service is correctly owned by the correct classes and initialized first
- No more pointer used for sockets to work around the previous bad design
parent d8cc3d68
/**
* A very simple base class that must be inherited by all network object
* that needs their own ioservice (for example anything that has a socket,
* or a timer, etc). But not by class that need to share a io_service that
* was created earlier. Basically, this means only Server, Client and
* GameClient should inherite this. All remote_client etc need to share the
* Server's io_service.
*/
#ifndef BASE_IOSERVICE
# define BASE_IOSERVICE
#include <boost/asio.hpp>
class BaseIoservice
{
public:
explicit BaseIoservice() {}
~BaseIoservice() {}
/**
* Returns a reference to the io_service object, so other affiliated
* objects can use it to create some io-related objects.
*/
boost::asio::io_service& get_io_service()
{
return this->io_service;
}
protected:
boost::asio::io_service io_service;
private:
BaseIoservice(const BaseIoservice&) = delete;
BaseIoservice(BaseIoservice&&) = delete;
BaseIoservice& operator=(const BaseIoservice&) = delete;
BaseIoservice& operator=(BaseIoservice&&) = delete;
};
#endif // BASE_IOSERVICE
/**
* A very simple base class that must be inherited by all network object
* that needs a socket.
* If an io_service is provided, we use it. Otherwise we use our own
* io_service.
*/
#ifndef BASE_SOCKET
# define BASE_SOCKET
#include <boost/asio.hpp>
class BaseSocket
{
public:
explicit BaseSocket(boost::asio::io_service& io_service):
socket(io_service) {}
const boost::asio::ip::tcp::socket& get_socket() const
{
return this->socket;
}
boost::asio::ip::tcp::socket& get_socket()
{
return this->socket;
}
~BaseSocket() {}
protected:
boost::asio::ip::tcp::socket socket;
private:
BaseSocket(const BaseSocket&) = delete;
BaseSocket(BaseSocket&&) = delete;
BaseSocket& operator=(const BaseSocket&) = delete;
BaseSocket& operator=(BaseSocket&&) = delete;
};
#endif // BASE_SOCKET
......@@ -9,12 +9,12 @@
#ifndef __CLIENT_HPP__
# define __CLIENT_HPP__
#include <network/interface_client.hpp>
#include <network/client_base.hpp>
#include <network/transfer_receiver.hpp>
using boost::asio::ip::tcp;
class Client: public InterfaceClient
class Client: public ClientBase
{
public:
Client();
......
#include <logging/logging.hpp>
#include <network/interface_client.hpp>
#include <network/client_base.hpp>
#include <boost/algorithm/string.hpp>
#if defined(_WIN32) || defined(_WIN64)
# include <WinBase.h>
......@@ -13,59 +13,59 @@ static void poll_timeout_handler(const boost::system::error_code&)
{
}
InterfaceClient::InterfaceClient():
ClientBase::ClientBase():
BaseIoservice(),
CommandHandler(io_service),
timeout(io_service)
{
this->socket = new tcp::socket(io_service);
}
InterfaceClient::~InterfaceClient()
ClientBase::~ClientBase()
{
if (this->socket->is_open())
if (this->socket.is_open())
{
log_debug("Closing connection");
this->socket->close();
this->socket.close();
}
delete this->socket;
}
// Connect, asyncly, and call one of the given callbacks
void InterfaceClient::connect(const std::string& host,
void ClientBase::connect(const std::string& host,
const short& port,
std::function< void(void) > on_success,
std::function< void(void) > on_failure)
{
// TODO use resolve and DNS
tcp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
this->socket->async_connect(endpoint,
std::bind(&InterfaceClient::connect_handler, this,
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
this->socket.async_connect(endpoint,
std::bind(&ClientBase::connect_handler, this,
on_success, on_failure,
std::placeholders::_1));
log_info("Connecting to " << host << ":" << port);
}
void InterfaceClient::connect_handler(std::function< void(void) > on_success,
std::function< void(void) > on_failure,
const boost::system::error_code& error)
void ClientBase::connect_handler(std::function< void(void) > on_success,
std::function< void(void) > on_failure,
const boost::system::error_code& error)
{
if (error)
{
log_info("Connection failed: " << error);
if (on_failure)
on_failure();
on_failure();
}
else
{
log_info("Connected.");
this->install_callback("PING", std::bind(&InterfaceClient::ping_callback, this, std::placeholders::_1));
this->install_callback("PING", std::bind(&ClientBase::ping_callback, this, std::placeholders::_1));
this->install_callbacks();
this->install_read_handler();
if (on_success)
on_success();
on_success();
}
}
void InterfaceClient::ping_callback(Command*)
void ClientBase::ping_callback(Command*)
{
log_debug("Received PING");
......@@ -74,12 +74,7 @@ void InterfaceClient::ping_callback(Command*)
this->send(command);
}
boost::asio::io_service& InterfaceClient::get_io_service()
{
return this->io_service;
}
void InterfaceClient::poll(long timeout)
void ClientBase::poll(long timeout)
{
if (timeout == 0)
{
......
......@@ -7,30 +7,29 @@
* Connects to the remote server, sends requests to it and
* calls the appropriate callbacks when the answer is received (or
* when a standalone command is received from the server)
* @class InterfaceClient
* @class ClientBase
*/
#include <boost/asio.hpp>
#include <functional>
#include <string>
#include <cstdlib>
#ifndef __INTERFACE_CLIENT_HPP__
# define __INTERFACE_CLIENT_HPP__
#ifndef __CLIENT_BASE_HPP__
# define __CLIENT_BASE_HPP__
#include <network/base_ioservice.hpp>
#include <network/command_handler.hpp>
#include <network/ping_handler.hpp>
#include <network/timed_event_handler.hpp>
#include <network/command.hpp>
#include <network/timed_event.hpp>
using boost::asio::ip::tcp;
class InterfaceClient: public CommandHandler, public TimedEventHandler, public PingHandler
class ClientBase: public BaseIoservice, public CommandHandler,
public TimedEventHandler, public PingHandler
{
public:
InterfaceClient();
~InterfaceClient();
ClientBase();
~ClientBase();
/**
* Tries to connect to the remote server. Calls one of the given callbacks, if it
* succeeds or fails.
......@@ -52,7 +51,6 @@ public:
void poll(long timeout = 0);
virtual void on_connection_closed() = 0;
virtual boost::asio::io_service& get_io_service();
private:
void connect_handler(std::function< void(void) >,
......@@ -63,7 +61,6 @@ private:
*/
void ping_callback(Command*);
boost::asio::io_service io_service;
boost::asio::deadline_timer timeout;
};
......
......@@ -2,7 +2,8 @@
#include <network/command_handler.hpp>
#include <network/command.hpp>
CommandHandler::CommandHandler():
CommandHandler::CommandHandler(boost::asio::io_service& io_service):
BaseSocket(io_service),
writing(false)
{
}
......@@ -103,7 +104,7 @@ void CommandHandler::read_handler(const boost::system::error_code& error, const
// We check what we need to read on the socket to have the rest of the binary datas
const std::size_t length_to_read = this->data.size() >= size ? 0 : size - this->data.size();
boost::asio::async_read(*this->socket,
boost::asio::async_read(this->socket,
this->data,
boost::asio::transfer_at_least(length_to_read),
std::bind(&CommandHandler::binary_read_handler, this,
......@@ -137,7 +138,7 @@ void CommandHandler::binary_read_handler(const boost::system::error_code& error,
void CommandHandler::install_read_handler(void)
{
boost::asio::async_read_until(*this->socket,
boost::asio::async_read_until(this->socket,
this->data,
':',
std::bind(&CommandHandler::read_handler, this,
......@@ -179,7 +180,7 @@ void CommandHandler::actually_send(Command* command)
std::vector<boost::asio::const_buffer> buffs;
buffs.push_back(boost::asio::buffer(command->header.data(), command->header.length()));
buffs.push_back(boost::asio::buffer(command->body, command->body_size));
async_write(*this->socket,
async_write(this->socket,
buffs,
std::bind(&CommandHandler::send_handler, this,
std::placeholders::_1,
......
......@@ -9,27 +9,27 @@
* @class CommandHandler
*/
#ifndef __COMMAND_HANDLER_HPP__
# define __COMMAND_HANDLER_HPP__
#include <deque>
#include <map>
#include <functional>
#include <boost/asio.hpp>
#ifndef __COMMAND_HANDLER_HPP__
# define __COMMAND_HANDLER_HPP__
#include <boost/asio.hpp>
#include <network/transfer_sender.hpp>
#include <network/base_socket.hpp>
#include <network/command.hpp>
using boost::asio::ip::tcp;
typedef std::function<void(Command*)> t_read_callback;
typedef std::deque<Command*> command_queue;
class CommandHandler
class CommandHandler: public BaseSocket
{
friend void TransferSender::send_next_chunk();
public:
CommandHandler();
explicit CommandHandler(boost::asio::io_service&);
virtual ~CommandHandler();
void install_read_handler();
......@@ -104,8 +104,6 @@ protected:
*/
virtual void on_connection_closed() = 0;
tcp::socket* socket;
private:
CommandHandler(const CommandHandler&);
CommandHandler& operator=(const CommandHandler&);
......
......@@ -11,11 +11,11 @@
#ifndef __GAME_CLIENT_HPP__
# define __GAME_CLIENT_HPP__
#include <network/interface_client.hpp>
#include <network/client_base.hpp>
using boost::asio::ip::tcp;
class GameClient: public InterfaceClient
class GameClient: public ClientBase
{
public:
GameClient();
......@@ -32,7 +32,6 @@ private:
void connect_handler(std::function< void(void) >,
std::function< void(void) >,
const boost::system::error_code&);
boost::asio::io_service io_service;
};
#endif /*__CLIENT_HPP__ */
......
......@@ -3,8 +3,8 @@
#include <database/db_object.hpp>
RemoteClient::RemoteClient(boost::asio::io_service& io_service,
Server<RemoteClient>* server):
InterfaceRemoteClient(io_service),
Server<RemoteClient>* server):
RemoteClientBase(io_service),
server(server),
user(nullptr)
{
......@@ -114,11 +114,6 @@ void RemoteClient::on_connection_closed()
this->server->remove_client(this);
}
boost::asio::io_service& RemoteClient::get_io_service()
{
return this->server->io_service;
}
void RemoteClient::on_transfer_ended(const TransferSender* transfer)
{
log_debug("on_transfer_ended");
......
......@@ -14,21 +14,18 @@
#include <boost/asio.hpp>
#include <functional>
#include <network/interface_remote_client.hpp>
#include <network/remote_client_base.hpp>
#include <network/transfer_sender.hpp>
#include <network/server.hpp>
#include <database/user.hpp>
using boost::asio::ip::tcp;
class RemoteClient: public InterfaceRemoteClient
class RemoteClient: public RemoteClientBase
{
public:
RemoteClient(boost::asio::io_service&, Server<RemoteClient>*);
explicit RemoteClient(boost::asio::io_service&, Server<RemoteClient>*);
~RemoteClient();
virtual void on_connection_closed();
virtual boost::asio::io_service& get_io_service();
/**
* Sends a file to the remote client.
* @param filename The file to send.
......
#include <logging/logging.hpp>
#include <network/interface_remote_client.hpp>
#include <network/remote_client_base.hpp>
#include <database/database.hpp>
#include <database/db_object.hpp>
unsigned long int InterfaceRemoteClient::clients_number = 0;
unsigned long int RemoteClientBase::clients_number = 0;
InterfaceRemoteClient::InterfaceRemoteClient(boost::asio::io_service& io_service):
number(InterfaceRemoteClient::clients_number++)
RemoteClientBase::RemoteClientBase(boost::asio::io_service& io_service):
CommandHandler(io_service),
number(RemoteClientBase::clients_number++),
io_service(io_service)
{
this->socket = new tcp::socket(io_service);
}
InterfaceRemoteClient::~InterfaceRemoteClient()
RemoteClientBase::~RemoteClientBase()
{
log_info("Deleting remote client " << this->number);
delete this->socket;
}
tcp::socket& InterfaceRemoteClient::get_socket(void)
void RemoteClientBase::start()
{
return *this->socket;
}
void InterfaceRemoteClient::start()
{
log_debug("Starting InterfaceRemoteClient " << this->number);
log_debug("Starting RemoteClientBase " << this->number);
this->install_callbacks();
this->install_timed_event(std::bind(&InterfaceRemoteClient::send_ping, this), 2);
this->install_timed_event(this->io_service, std::bind(&RemoteClientBase::send_ping, this), 2);
CommandHandler::install_read_handler();
}
void InterfaceRemoteClient::send_ping()
void RemoteClientBase::send_ping()
{
Command* command = new Command;
command->set_name("PING");
this->request_answer(command, std::bind(&InterfaceRemoteClient::on_pong, this, std::placeholders::_1), "PONG");
this->request_answer(command, std::bind(&RemoteClientBase::on_pong, this, std::placeholders::_1), "PONG");
this->ping_sent();
}
void InterfaceRemoteClient::on_pong(Command*)
void RemoteClientBase::on_pong(Command*)
{
this->pong_received();
log_debug("Current ping: " << this->get_latency() << "micro seconds.");
this->install_timed_event(std::bind(&InterfaceRemoteClient::send_ping, this), 2);
this->install_timed_event(this->io_service, std::bind(&RemoteClientBase::send_ping, this), 2);
}
......@@ -9,14 +9,14 @@
* we need to communicate something to it.
* The derived classes must install their own callback, install a transfer handler
* in it or not, etc.
* @class InterfaceRemoteClient
* @class RemoteClientBase
*/
#include <boost/asio.hpp>
#include <functional>
#ifndef __INTERFACE_REMOTE_CLIENT_HPP__
# define __INTERFACE_REMOTE_CLIENT_HPP__
#ifndef REMOTE_CLIENT_BASE
# define REMOTE_CLIENT_BASE
#include <network/command_handler.hpp>
#include <network/command.hpp>
......@@ -24,21 +24,15 @@
#include <network/timed_event.hpp>
#include <network/ping_handler.hpp>
using boost::asio::ip::tcp;
class InterfaceRemoteClient: public CommandHandler, public TimedEventHandler, public PingHandler
class RemoteClientBase: public CommandHandler, public TimedEventHandler, public PingHandler
{
public:
InterfaceRemoteClient(boost::asio::io_service&);
~InterfaceRemoteClient();
explicit RemoteClientBase(boost::asio::io_service&);
~RemoteClientBase();
/**
* starts the client (install the read handler, etc)
*/
void start();
/**
* @return tcp::socket&
*/
tcp::socket& get_socket(void);
/**
* Send a ping request to the remote client.
*/
......@@ -63,8 +57,6 @@ public:
virtual void on_connection_closed() = 0;
virtual boost::asio::io_service& get_io_service() = 0;
protected:
/**
* The client number (aka id).
......@@ -77,7 +69,14 @@ protected:
*/
virtual void install_callbacks() = 0;
void install_read_handler(void);
private:
/**
* We keep a reference on the io_service that was passed to us by the
* Server, for convenience.
*/
boost::asio::io_service& io_service;
};
#endif
#endif // REMOTE_CLIENT_BASE
/**@}*/
......@@ -4,8 +4,8 @@
#include <world/server_world.hpp>
RemoteGameClient::RemoteGameClient(boost::asio::io_service& io_service,
Server<RemoteGameClient>* server):
InterfaceRemoteClient(io_service),
Server<RemoteGameClient>* server):
RemoteClientBase(io_service),
server(server)
{
}
......@@ -32,11 +32,6 @@ void RemoteGameClient::install_callbacks()
this->install_callback("SPAWN", std::bind(&ServerWorld::spawn_callback, world, std::placeholders::_1));
}
boost::asio::io_service& RemoteGameClient::get_io_service()
{
return this->server->io_service;
}
void RemoteGameClient::ok_callback(Command* command)
{
// todo replace that Event struct by just an int.
......
......@@ -14,19 +14,18 @@
#include <boost/asio.hpp>
#include <network/interface_remote_client.hpp>
#include <network/remote_client_base.hpp>
#include <network/server.hpp>
#include <database/user.hpp>
using boost::asio::ip::tcp;
class RemoteGameClient: public InterfaceRemoteClient
class RemoteGameClient: public RemoteClientBase
{
public:
RemoteGameClient(boost::asio::io_service&, Server<RemoteGameClient>*);
~RemoteGameClient();
virtual void on_connection_closed();
virtual boost::asio::io_service& get_io_service();
private:
/**
......
......@@ -16,7 +16,6 @@
#ifndef __SERVER_HPP__
# define __SERVER_HPP__
#include <istream>
#include <vector>
#include <functional>
......@@ -24,11 +23,11 @@
#include <boost/asio.hpp>
#include <logging/logging.hpp>
#include <network/base_ioservice.hpp>
#include <network/base_socket.hpp>
#include <network/command.hpp>
#include <world/time.hpp>
using boost::asio::ip::tcp;
/**
* Does nothing, it is just used to exit the io_service.run_one() after
* a timeout.
......@@ -38,7 +37,7 @@ static void poll_timeout_handler(const boost::system::error_code&)
}
template <class T>
class Server
class Server: public BaseIoservice, public BaseSocket
{
public:
/**
......@@ -46,14 +45,16 @@ public:
* @param port The port on which the servers accepts new connections.
*/
Server(short port):
BaseIoservice(),
BaseSocket(io_service),
port(port),
timeout(io_service)
timeout(io_service),
acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{
}
~Server()
{
log_debug("closing socket");
delete this->acceptor;
}
/**
* Starts the server
......@@ -187,17 +188,15 @@ public:
assert(false);
}
boost::asio::io_service io_service;
private:
void install_accept_handler(void)
{
T* new_client = new T(this->io_service, this);
this->acceptor->async_accept(new_client->get_socket(),
std::bind(&Server<T>::handle_accept,
this, new_client,
std::placeholders::_1));
this->acceptor.async_accept(new_client->get_socket(),
std::bind(&Server<T>::handle_accept,
this, new_client,
std::placeholders::_1));
}
void handle_accept(T* client, const boost::system::error_code& error)
......@@ -216,8 +215,7 @@ private:
void accept(void)