#include <iostream>
#include "tcp_connexion.h"
#include "tcp_server.h"
using namespace std;
int main()
{
try
{
boost::asio::io_context ioc;
//TO DO : Improve variable type
std::shared_ptr<std::string const> const& doc_root = std::make_shared<std::string>("/");
tcp::endpoint endpoint(tcp::v4(), 4000);
tcp_server server(ioc, endpoint, doc_root);
ioc.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
#ifndef WEBSOCKET_SESSION_H
#define WEBSOCKET_SESSION_H
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/asio.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/http.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "message.h"
namespace websocket = boost::beast::websocket;
using tcp = boost::asio::ip::tcp;
namespace http = boost::beast::http;
class websocket_session : public std::enable_shared_from_this<websocket_session>
{
public:
void fail(boost::system::error_code ec, char const* what);
websocket::stream<tcp::socket> m_ws;
websocket_session(tcp::socket socket);
void parse(boost::beast::multi_buffer mess )
{
std::stringstream ss;
ss << boost::beast::buffers_to_string(mess.data());
pt::read_json(ss, root);
int event = root.get<int>("event");
std::cout << "event : " << event << std::endl;
}
void do_read()
{
m_ws.async_read(buffer,
boost::asio::bind_executor(
strand,
std::bind(
&websocket_session::on_read,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2
)));
}
void on_read(boost::system::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
// This indicates that the session was closed
if(ec == websocket::error::closed)
return;
if(ec)
fail(ec, "read");
std::cout<< "message : " << boost::beast::buffers_to_string(buffer.data()) << std::endl;
parse(buffer);
do_read();
}
void on_write(boost::system::error_code ec, std::size_t bytes_transferred);
private:
boost::beast::multi_buffer buffer;
boost::asio::strand<
boost::asio::io_context::executor_type> strand;
pt::ptree root;
};
typedef boost::shared_ptr<websocket_session> websocket_session_ptr;
#endif
#include "websocket_session.h"
#include <iostream>
websocket_session::websocket_session(tcp::socket socket)
: m_ws(std::move(socket))
, strand(socket.get_executor())
{
}
void websocket_session::fail(boost::system::error_code ec, char const* what)
{
std::cerr << what << ": " << ec.message() << "\n";
}
void websocket_session::on_write(boost::system::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
// Clear the buffer
buffer.consume(buffer.size());
}
#include "tcp_server.h"
#include "sql.h"
tcp_server::tcp_server(boost::asio::io_context& ioc, tcp::endpoint endpoint, std::shared_ptr<std::string const> const& doc_root)
: acceptor(ioc, endpoint),
socket(ioc),
strand(ioc.get_executor()),
room(new chatroom(*this))
{
wait_for_connection();
}
void tcp_server::wait_for_connection()
{
websocket_session_ptr new_websocket(new websocket_session(std::move(socket)));
acceptor.async_accept(
socket,
boost::bind(
&tcp_server::handle_accept,
this,
boost::asio::placeholders::error,
new_websocket
));
}
void tcp_server::handle_accept(const boost::system::error_code ec, websocket_session_ptr new_websocket)
{
if (!ec)
{
// Happens when the timer closes the socket
if(ec == boost::asio::error::operation_aborted)
return;
new_websocket->m_ws.async_accept(
boost::asio::bind_executor(
strand,
std::bind(
&tcp_server::on_accept,
this,
std::placeholders::_1)));
}
}
void tcp_server::on_accept(boost::system::error_code ec)
{
if(ec)
std::cout<<"accept : " << ec.value() << ec.message()<<std::endl;
wait_for_websocket_connection();
}
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include "chat_websocket_session.h"
#include "chatroom.h"
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
namespace http = boost::beast::http;
class tcp_server
{
tcp::acceptor acceptor;
tcp::socket socket;
boost::asio::strand<boost::asio::io_context::executor_type> strand;
chatroompointer room;
boost::beast::flat_buffer buffer;
public:
tcp_server(boost::asio::io_context& ioc, tcp::endpoint endpoint, std::shared_ptr<std::string const> const& doc_root);
void wait_for_connection();
void handle_accept(const boost::system::error_code ec, websocket_session_ptr new_websocket);
http::request<http::string_body> req;
private:
void checkGETVerb(boost::system::error_code ec);
message message_read;
};
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include "chatroom.h"
#include "websocket_session.h"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
class tcp_server;
using boost::asio::ip::tcp;
class chat_websocket_session: public boost::enable_shared_from_this<chat_websocket_session>
{
public:
~chat_websocket_session();
void start();
static chatwebsocketsessionpointer create(chatroompointer room, websocket_session_ptr websocket )
{
chatwebsocketsessionpointer session (new chat_websocket_session(room, websocket));
session->wait_for_data();
return session;
}
void deliver(const message &msg);
private:
chat_websocket_session(chatroompointer room, websocket_session_ptr websocket);
void wait_for_data ();
void handle_write (const boost::system::error_code& error);
void handle_read (const boost::system::error_code& error);
websocket_session_ptr m_websocket;
chatroomwptr m_room;
message m_message;
bool is_leaving;
};
typedef boost::shared_ptr<chat_websocket_session> chatwebsocketsessionpointer;
#include "chat_websocket_session.h"
chat_websocket_session::chat_websocket_session(chatroompointer room, websocket_session_ptr websocket)
: m_websocket(websocket),
m_room(room)
{
is_leaving = false;
std::cout << "New chat_websocket_session ! " <<
std::endl;
}
chat_websocket_session::~chat_websocket_session()
{
std::cout << "Session détruite" << std::endl;
}
void chat_websocket_session::wait_for_data()
{
// m_websocket->run();
// m_websocket->do_read(m_message, boost::bind(&chat_websocket_session::handle_read, shared_from_this(), boost::asio::placeholders::error));
// std::cout<<"messagea : " << boost::beast::buffers_to_string(m_websocket->buffer.data())<<std::endl;
}
void chat_websocket_session::handle_read(const boost::system::error_code &error)
{
chatroompointer room = m_room.lock();
if (room)
{
if (!error)
{
//std::cout<<"message : "<<m_message.m_message<<std::endl;
room->deliver(m_message);
wait_for_data();
}
else
{
if (!is_leaving)
{
is_leaving = true;
room->leave(shared_from_this() );
}
}
}
}
void chat_websocket_session::deliver(const message& msg)
{
/*
m_tcp_connection->async_write(msg,
boost::bind(&chat_websocket_session::handle_write, shared_from_this(),
boost::asio::placeholders::error)
);
//std::cout<<msg.m_message<<" : message "<<std::endl;
*/
}
void chat_websocket_session::handle_write(const boost::system::error_code &error)
{
chatroompointer room = m_room.lock();
if (room && error && (!is_leaving) )
{
is_leaving = true;
room->leave(shared_from_this() );
}
}
#ifndef CHATROOM_H
#define CHATROOM_H
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <set>
#include "message.h"
class chat_websocket_session;
class tcp_server;
typedef boost::shared_ptr<chat_websocket_session> chatwebsocketsessionpointer;
class chatroom
{
public:
chatroom(tcp_server& server);
void join(chatwebsocketsessionpointer participant);
void leave(chatwebsocketsessionpointer participant);
void deliver(const message& msg);
private:
std::set<chatwebsocketsessionpointer> m_participants;
// tcp_server& m_server;
};
typedef boost::weak_ptr<chatroom> chatroomwptr;
typedef boost::shared_ptr<chatroom> chatroompointer;
#endif // CHATROOM_H
#include "chatroom.h"
#include "chat_websocket_session.h"
chatroom::chatroom(tcp_server& server)
//:m_server(server)
{
std::cout << "New room" << std::endl;
}
void chatroom::join(chatwebsocketsessionpointer participant)
{
// m_participants.insert(participant);
message e;
e.m_type = message::PERSON_CONNECTED;
e.m_message = "Welcome ! \n";
// deliver(e);
}
void chatroom::leave(chatwebsocketsessionpointer participant)
{
message e;
e.m_type = message::PERSON_LEFT;
deliver(e);
// m_participants.erase(participant);
}
void chatroom::deliver(const message& msg)
{
// std::for_each(m_participants.begin(), m_participants.end(),
// boost::bind(&chat_websocket_session::deliver, _1, boost::ref(msg)));
}
#ifndef TCP_CONNEXION_H
#define TCP_CONNEXION_H
#include <boost/asio.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <iostream>
using boost::asio::ip::tcp;
class tcp_connection
{
public:
tcp_connection(boost::asio::io_service& io_service)
: m_socket(io_service)
{
}
boost::asio::ip::tcp::socket& socket()
{
return m_socket;
}
template <typename T, typename Handler>
void async_write(const T& t, Handler handler)
{
std::ostringstream archive_stream;
boost::archive::text_oarchive archive(archive_stream);
archive << t;
m_outbound_data = archive_stream.str();
std::ostringstream header_stream;
header_stream << std::setw(header_length)
<< std::hex << m_outbound_data.size();
if (!header_stream || header_stream.str().size() != header_length)
{
boost::system::error_code error(boost::asio::error::invalid_argument);
m_socket.get_io_service().post(boost::bind(handler, error));
return;
}
m_outbound_header = header_stream.str();
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(m_outbound_header));
buffers.push_back(boost::asio::buffer(m_outbound_data));
boost::asio::async_write(m_socket, buffers, handler);
}
template <typename T, typename Handler>
void async_read(T& t, Handler handler)
{
void (tcp_connection::*f)( const boost::system::error_code&, T&, boost::tuple<Handler>)
= &tcp_connection::handle_read_header<T, Handler>;
boost::asio::async_read(m_socket, boost::asio::buffer(m_inbound_header),
boost::bind(f,
this, boost::asio::placeholders::error, boost::ref(t),
boost::make_tuple(handler)));
}
template <typename T, typename Handler>
void handle_read_header(const boost::system::error_code& e,
T& t, boost::tuple<Handler> handler)
{
if (e)
{
boost::get<0>(handler)(e);
}
else
{
std::istringstream is(std::string(m_inbound_header, header_length));
std::size_t m_inbound_datasize = 0;
if (!(is >> std::hex >> m_inbound_datasize))
{
boost::system::error_code error(boost::asio::error::invalid_argument);
boost::get<0>(handler)(error);
return;
}
m_inbound_data.resize(m_inbound_datasize);
void (tcp_connection::*f)(const boost::system::error_code&, T&, boost::tuple<Handler>)
= &tcp_connection::handle_read_data<T, Handler>;
boost::asio::async_read(m_socket, boost::asio::buffer(m_inbound_data),
boost::bind(f, this,
boost::asio::placeholders::error, boost::ref(t), handler));
}
}
template <typename T, typename Handler>
void handle_read_data(const boost::system::error_code& e,
T& t, boost::tuple<Handler> handler)
{
if (e)
{
boost::get<0>(handler)(e);
}
else
{
try
{
std::string archive_data(&m_inbound_data[0], m_inbound_data.size());
std::istringstream archive_stream(archive_data);
boost::archive::text_iarchive archive(archive_stream);
archive >> t;
}
catch (std::exception& e)
{
boost::system::error_code error(boost::asio::error::invalid_argument);
boost::get<0>(handler)(error);
return;
}
boost::get<0>(handler)(e);
}
}
private:
boost::asio::ip::tcp::socket m_socket;
enum { header_length = 8 };
std::string m_outbound_header;
std::string m_outbound_data;
char m_inbound_header[header_length];
std::vector<char> m_inbound_data;
};
typedef boost::shared_ptr<tcp_connection> connection_ptr;
#endif
#ifndef MESSAGE_H
#define MESSAGE_H
#include <string>
#include <list>
#include <boost/serialization/map.hpp>
#include <boost/serialization/list.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
namespace pt = boost::property_tree;
class message
{
public:
void reset()
{
m_list_string.clear();
m_message.clear();
m_login.clear();
}
/* void parse(message)
{
pt::read_json(message, root);
int event = root.get<int>("event");
std::cout << "event" << event << std::endl;
}
*/
void TransformtoJSON(int m_type, std::string message)
{
// JSON.stringify("type" : m_type, message : m_message);
}
int m_type;
std::list<std::string> m_list_string;
bool tcp ;
std::string m_message;
std::string m_login;
template<class Archive>
void serialize(Archive& ar, const unsigned int version){
ar & m_type & m_list_string & m_message & m_login;
}
enum {
NEW_MSG = 0,
PERSON_LEFT = 1,
PERSON_CONNECTED = 2,
};
private:
pt::ptree root;
};
#endif