Commit 0416dc98 authored by louiz’'s avatar louiz’

Finish network callbacks etc.

Now we can easily implement a request/answer protocol between the clients
and the server.
parent 9ef00255
......@@ -61,12 +61,12 @@ std::string Config::get(const std::string& option, const std::string& def)
}
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
Config::read_conf("./batajelo.conf");
std::string res = Config::get("port", "12");
std::cout << res << std::endl;
return 0;
}
// int main(int argc, char *argv[])
// {
// (void)argc;
// (void)argv;
// Config::read_conf("./batajelo.conf");
// std::string res = Config::get("port", "12");
// std::cout << res << std::endl;
// return 0;
// }
......@@ -50,6 +50,3 @@ private:
};
#endif
#include "game.hpp"
#include <unistd.h>
Game::Game()
{
std::cout << "Launching game" << std::endl;
}
Game::~Game()
{
std::cout << "End." << std::endl;
}
void Game::on_login_form_validated(const std::string& login,
const std::string& password,
const std::string& host,
const short& port)
{
this->client.connect(host, port,
boost::bind(&Game::on_connection_success, this, login, password),
boost::bind(&Game::on_connection_failed, this, host, port));
}
void Game::on_connection_failed(const std::string& host, const short& port)
{
std::cout << "Connection to remote " << host << ":" << port << " failed" << std::endl;
}
void Game::on_connection_success(const std::string& login, const std::string& password)
{
std::cout << "Login in..." << std::endl;
this->authenticate(login, password);
}
void Game::authenticate(const std::string& login, const std::string& password)
{
this->client.request_answer("AUTH", login.data(), boost::bind(&Game::on_authenticate, this, _1));
}
void Game::on_authenticate(const std::string& result)
{
std::cout << "on_authenticate " << result << std::endl;
}
void Game::run()
{
while (true)
{
this->client.poll();
usleep(10000); // replace with game logic
}
}
// Test main
// Simulate a login flow
int main(int argc, char** argv)
{
std::cout << "coucou" << std::endl;
Game game;
game.on_login_form_validated("test", "coucou", "127.0.0.1", 7878);
game.run();
return 0;
}
// The main class for the game itself
// Network
#include "client.hpp"
#ifndef __GAME_HPP__
# define __GAME_HPP__
class Game
{
public:
Game();
~Game();
// Called whenever the login form has been filled, clicked and checked for validity.
void on_login_form_validated(const std::string&, const std::string&, const std::string&, const short&);
void on_connection_failed(const std::string&, const short&);
void on_connection_success(const std::string&, const std::string&);
void authenticate(const std::string&, const std::string&);
void on_authenticate(const std::string&);
void run();
private:
Client client;
Game(const Game&);
Game& operator=(const Game&);
};
#endif // __GAME_HPP__
all: server client
server:
g++ server.cpp remote_client.cpp -lboost_system -L/usr/include/boost -lpthread -o server
g++ command_handler.cpp server.cpp remote_client.cpp -lboost_system -L/usr/include/boost -lpthread -o server
client:
g++ client.cpp -lboost_system -L/usr/include/boost -lpthread -o client
......
#include "client.hpp"
Client::Client(short port): port(port)
Client::Client()
{
this->socket = new tcp::socket(this->io_service);
this->socket = new tcp::socket(io_service);
}
Client::~Client()
......@@ -16,16 +16,18 @@ Client::~Client()
}
// Connect, asyncly, and call one of the given callbacks
void Client::connect(boost::function< void(void) > on_success,
void Client::connect(const std::string& host,
const short& port,
boost::function< void(void) > on_success,
boost::function< void(void) > on_failure)
{
// TODO use resolve and DNS
tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), this->port);
tcp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
std::cout << "Connecting" << std::endl;
this->socket->async_connect(endpoint,
boost::bind(&Client::connect_handler, this,
on_success, on_failure,
boost::asio::placeholders::error));
boost::bind(&Client::connect_handler, this,
on_success, on_failure,
boost::asio::placeholders::error));
}
void Client::connect_handler(boost::function< void(void) > on_success,
......@@ -41,6 +43,7 @@ void Client::connect_handler(boost::function< void(void) > on_success,
else
{
std::cout << "Connected." << std::endl;
this->install_read_handler();
if (on_success)
on_success();
}
......@@ -48,33 +51,6 @@ void Client::connect_handler(boost::function< void(void) > on_success,
void Client::poll(void)
{
this->io_service.poll();
this->io_service.poll();
}
void Client::send_message(void)
{
std::cout << "sending coucou" << std::endl;
this->send("coucou\n");
}
void Client::send(const char* msg)
{
async_write(*this->socket,
boost::asio::buffer(msg, strlen(msg)),
boost::bind(&Client::send_handler, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void Client::send_handler(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
}
// int main (int ac, char **av)
// {
// Client client(7878);
// client.connect(boost::bind(&Client::send_message, &client));
// client.poll();
// }
#include "command_handler.hpp"
#include <boost/asio.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
......@@ -9,24 +11,22 @@
using boost::asio::ip::tcp;
class Client
class Client: public CommandHandler
{
public:
Client(short);
Client();
~Client();
void connect(boost::function< void(void) > on_success = 0, boost::function< void(void) > on_failure = 0);
void connect(const std::string&,
const short&,
boost::function< void(void) > on_success = 0,
boost::function< void(void) > on_failure = 0);
void connect_handler(boost::function< void(void) >,
boost::function< void(void) >,
const boost::system::error_code&);
void send_handler(const boost::system::error_code&, std::size_t);
void poll(void);
void send(const char*);
void send_message(void);
private:
const short port;
boost::asio::io_service io_service;
tcp::socket* socket;
};
#endif /*__CLIENT_HPP__ */
#include "command_handler.hpp"
CommandHandler::CommandHandler()
{
}
void CommandHandler::install_callback(const std::string& command,
boost::function< void(std::string) > callback)
{
std::cout << "installing callback for command " << command << std::endl;
this->callbacks[command] = callback;
}
void CommandHandler::install_callback_once(const std::string& command,
boost::function< void(std::string) > callback)
{
std::cout << "installing ONCE callback for command " << command << std::endl;
this->callbacks_once[command] = callback;
}
boost::function< void(std::string) > CommandHandler::get_callback(const std::string& command)
{
std::cout << "get_callback" << std::endl;
std::map<const std::string, boost::function< void(std::string) > >::iterator it;
it = this->callbacks.find(command);
if (it != this->callbacks.end())
return it->second;
it = this->callbacks_once.find(command);
if (it != this->callbacks_once.end())
{
std::cout << "Removing callback for command " << command << std::endl;
boost::function< void(std::string) > callback = it->second;
this->callbacks_once.erase(it);
return callback;
}
return 0;
}
void CommandHandler::read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
std::cout << "read_handler, size: " << bytes_transferred << " bytes. error: " << error << std::endl;
if (error)
{
// TODO check more precisely for errors
return;
}
// Extract the needed data from the buffer
char c[bytes_transferred+1];
this->data.sgetn(c, bytes_transferred);
c[bytes_transferred] = 0;
std::cout << "Received: [" << c << "] " << this->data.size() << std::endl;
// find the . separator
size_t pos = 0;
while (c[pos] && c[pos] != '.')
pos++;
std::cout << pos << std::endl;
std::string command;
std::size_t size;
if (pos == bytes_transferred)
{ // no . was found
command = std::string(c, pos-1);
size = 0;
}
else
{
command = std::string(c, pos);
size = atoi(std::string(c+pos+1, bytes_transferred-pos-2).data()); // remove the ending :
}
std::cout << command << " . " << size << std::endl;
// Find out if a callback was registered for that command.
boost::function< void(std::string) > callback = this->get_callback(command);
// We check what we need to read on the socket to have the rest of the binary datas
std::size_t length_to_read = this->data.size() >= size ? 0 : size - this->data.size();
boost::asio::async_read(*this->socket,
this->data,
boost::asio::transfer_at_least(length_to_read),
boost::bind(&CommandHandler::binary_read_handler, this,
boost::asio::placeholders::error,
size, callback));
}
void CommandHandler::binary_read_handler(const boost::system::error_code& error,
std::size_t bytes_transferred,
// std::size_t size,
boost::function<void(std::string)> callback)
{
std::cout << "binary_read_handler" << bytes_transferred << std::endl;
char c[bytes_transferred+1];
this->data.sgetn(c, bytes_transferred);
c[bytes_transferred] = 0;
std::cout << "[" << c << "]" << std::endl;
if (callback)
callback(c);
else
std::cout << "no callback" << std::endl;
this->install_read_handler();
}
// Set a callback to be called whenever there’s something to read on the socket.
void CommandHandler::install_read_handler(void)
{
boost::asio::async_read_until(*this->socket,
this->data,
':',
boost::bind(&CommandHandler::read_handler, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
// Send a command and add a callback to be called when the answer to
// this command will be received
void CommandHandler::request_answer(const char* command, const char* data,
boost::function< void(const std::string&) > on_answer)
{
std::string msg(command);
std::ostringstream ssize;
ssize << strlen(data);
msg += std::string(".") + std::string(ssize.str()) + std::string(":") + std::string(data);
this->install_callback_once(command, on_answer);
this->send(msg.data());
}
void CommandHandler::send(const char* msg)
{
async_write(*this->socket,
boost::asio::buffer(msg, strlen(msg)),
boost::bind(&CommandHandler::send_handler, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
std::cout << "Sending [" << msg << "]" << std::endl;
}
void CommandHandler::send_handler(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
// TODO check for error
std::cout << bytes_transferred << " bytes sent" << std::endl;
}
#include <map>
#include <boost/function.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#ifndef __COMMAND_HANDLER_HPP__
# define __COMMAND_HANDLER_HPP__
using boost::asio::ip::tcp;
// Handles the reading part of the socket
// Keeps a list of callbacks to call, associated with a command name
class CommandHandler
{
public:
CommandHandler();
~CommandHandler() {}
void read_handler(const boost::system::error_code& error, std::size_t bytes_transferred);
void install_read_handler();
void binary_read_handler(const boost::system::error_code&, std::size_t, boost::function<void(std::string)>);
void request_answer(const char*, const char*, boost::function< void(const std::string&) >);
protected:
tcp::socket* socket;
void install_callback(const std::string&, boost::function< void(std::string) >);
void install_callback_once(const std::string&, boost::function< void(std::string) >);
boost::function< void(std::string) > get_callback(const std::string&);
boost::asio::streambuf data;
void send(const char*);
void send_handler(const boost::system::error_code&, std::size_t);
private:
CommandHandler(const CommandHandler&);
CommandHandler& operator=(const CommandHandler&);
std::map<const std::string, boost::function< void(std::string) > > callbacks;
std::map<const std::string, boost::function< void(std::string) > > callbacks_once;
};
#endif // __COMMAND_HANDLER_HPP__
......@@ -3,46 +3,39 @@
int RemoteClient::clients_number = 0;
RemoteClient::RemoteClient(boost::asio::io_service& io_service):
socket(io_service),
number(RemoteClient::clients_number++)
{
this->socket = new tcp::socket(io_service);
}
tcp::socket& RemoteClient::get_socket(void)
RemoteClient::~RemoteClient()
{
return socket;
std::cout << "Deleting remote client" << std::endl;
delete this->socket;
}
void RemoteClient::read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
tcp::socket& RemoteClient::get_socket(void)
{
std::cout << "read_handler on client " << this->number << ": " << bytes_transferred << " bytes. error: " << error << std::endl;
return *this->socket;
}
if (error)
{
// TODO check more precisely for errors
std::cout << "Stopping remote client " << this->number << " [" << error << "]. Exiting." << std::endl;
return;
}
std::string s((std::istreambuf_iterator<char>(&this->data)),
std::istreambuf_iterator<char>());
std::cout << s << std::endl;
// TODO, pass the string to a callback
this->install_read_handler();
void RemoteClient::install_callbacks()
{
this->install_callback("AUTH", boost::bind(&RemoteClient::auth_callback, this, _1));
}
// Set a callback to be called whenever there’s something to read on the socket.
void RemoteClient::install_read_handler(void)
void RemoteClient::auth_callback(const std::string& arg)
{
boost::asio::async_read_until(this->socket,
this->data,
'\n',
boost::bind(&RemoteClient::read_handler, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
std::cout << "auth_callback: " << arg << std::endl;
// TODO check login for user, check if it is already logged,
// set it as logged in the database, and return 0 if it failed and 1
// if it succeded
this->send("AUTH.1:1");
}
void RemoteClient::start(void)
{
std::cout << "Starting RemoteClient " << this->number << std::endl;
this->install_read_handler();
CommandHandler::install_read_handler();
this->install_callbacks();
}
#include "command_handler.hpp"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#ifndef __REMOTE_CLIENT_HPP__
# define __REMOTE_CLIENT_HPP__
using boost::asio::ip::tcp;
class RemoteClient
class RemoteClient: public CommandHandler
{
public:
RemoteClient(boost::asio::io_service&);
~RemoteClient();
void start(void);
tcp::socket& get_socket(void);
static int clients_number;
private:
void read_handler(const boost::system::error_code&, std::size_t);
void install_callbacks();
void install_read_handler(void);
void auth_callback(const std::string&);
boost::asio::streambuf data;
tcp::socket socket;
const int number;
};
......
......@@ -10,7 +10,6 @@ Server::Server(short port):
Server::~Server()
{
std::cout << "closing socket" << std::endl;
// this->socket->close();
delete this->acceptor;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment