Implemented socket message templates. (#40)

Implemented socket message templates to support broadcast (shared_ptr) and to achieve buffer zero-copy.
This commit is contained in:
Ravin Perera
2019-10-23 13:04:57 +05:30
committed by GitHub
parent b4237f1285
commit 61b38bb0a0
16 changed files with 636 additions and 539 deletions

View File

@@ -1,93 +0,0 @@
#include <iostream>
#include "socket_client.hpp"
#include "../hplog.hpp"
using tcp = net::ip::tcp;
using error = boost::system::error_code;
namespace sock
{
socket_client::socket_client(net::io_context &ioc, socket_session_handler &session_handler)
: resolver_(net::make_strand(ioc)), ws_(net::make_strand(ioc)), sess_handler_(session_handler)
{
}
/**
* Entry point to socket client which will intiate a connection to server
*/
// boost async_resolve function requires a port as a string because of that port is passed as a string
void socket_client::run(std::string_view host, std::string_view port)
{
host_ = host;
port_ = port;
// Look up the domain name
resolver_.async_resolve(
host,
port,
[self = shared_from_this()](error ec, tcp::resolver::results_type results) {
self->on_resolve(ec, results);
});
}
/**
* Executes on completion of resolving the server
*/
void socket_client::on_resolve(error ec, tcp::resolver::results_type results)
{
if (ec)
socket_client_fail(ec, "socket_client_resolve");
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(ws_).async_connect(
results,
[self = shared_from_this()](error ec, tcp::resolver::results_type::endpoint_type type) {
self->on_connect(ec, type);
});
}
/**
* Executes on completion of connecting to the server
*/
void socket_client::on_connect(error ec, tcp::resolver::results_type::endpoint_type)
{
if (ec)
socket_client_fail(ec, "socket_client_connect");
// Turn off the timeout on the tcp_stream, because
// the websocket stream has its own timeout system.
beast::get_lowest_layer(ws_).expires_never();
// Set suggested timeout settings for the websocket
ws_.set_option(
websocket::stream_base::timeout::suggested(
beast::role_type::client));
// Perform the websocket handshake
ws_.async_handshake(host_, "/",
[self = shared_from_this()](error ec) {
self->on_handshake(ec);
});
}
/**
* Executes on completion of handshake
*/
void socket_client::on_handshake(error ec)
{
//Creates a new socket session object
std::make_shared<socket_session>(
ws_, sess_handler_)
->client_run(std::move(host_), std::move(port_), ec);
}
/**
* Executes on error
*/
void socket_client::socket_client_fail(beast::error_code ec, char const *what)
{
LOG_ERR << what << ": " << ec.message();
}
} // namespace sock

View File

@@ -5,6 +5,7 @@
#include <boost/beast.hpp>
#include "socket_session.hpp"
#include "socket_session_handler.hpp"
#include "../hplog.hpp"
namespace beast = boost::beast;
namespace net = boost::asio;
@@ -20,13 +21,14 @@ namespace sock
* Represents an active WebSocket client connection
* Based on the implementation from https://github.com/vinniefalco/CppCon2018
*/
class socket_client : public std::enable_shared_from_this<socket_client>
template <class T>
class socket_client : public std::enable_shared_from_this<socket_client<T>>
{
tcp::resolver resolver_; // resolver used to resolve host and the port
websocket::stream<beast::tcp_stream> ws_; // web socket stream used to send and receive messages
std::string host_; // address of the server in which the client connects
std::string port_; // port of the server in which client connects
socket_session_handler &sess_handler_; // handler passed to gain access to websocket events
tcp::resolver resolver; // resolver used to resolve host and the port
websocket::stream<beast::tcp_stream> ws; // web socket stream used to send and receive messages
std::string host; // address of the server in which the client connects
std::string port; // port of the server in which client connects
socket_session_handler<T> &sess_handler_; // handler passed to gain access to websocket events
void on_resolve(error ec, tcp::resolver::results_type results);
@@ -42,10 +44,99 @@ class socket_client : public std::enable_shared_from_this<socket_client>
public:
// Resolver and socket require an io_context
socket_client(net::io_context &ioc, socket_session_handler &session_handler);
socket_client(net::io_context &ioc, socket_session_handler<T> &session_handler);
//Entry point to the client which requires an active host and port
void run(std::string_view host, std::string_view port);
};
template <class T>
socket_client<T>::socket_client(net::io_context &ioc, socket_session_handler<T> &session_handler)
: resolver(net::make_strand(ioc)), ws(net::make_strand(ioc)), sess_handler_(session_handler)
{
}
/**
* Entry point to socket client which will intiate a connection to server
*/
// boost async_resolve function requires a port as a string because of that port is passed as a string
template <class T>
void socket_client<T>::run(std::string_view host, std::string_view port)
{
this->host = host;
this->port = port;
// Look up the domain name
resolver.async_resolve(
host,
port,
[self = this->shared_from_this()](error ec, tcp::resolver::results_type results) {
self->on_resolve(ec, results);
});
}
/**
* Executes on completion of resolving the server
*/
template <class T>
void socket_client<T>::on_resolve(error ec, tcp::resolver::results_type results)
{
if (ec)
socket_client_fail(ec, "socket_client_resolve");
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(ws).async_connect(
results,
[self = this->shared_from_this()](error ec, tcp::resolver::results_type::endpoint_type type) {
self->on_connect(ec, type);
});
}
/**
* Executes on completion of connecting to the server
*/
template <class T>
void socket_client<T>::on_connect(error ec, tcp::resolver::results_type::endpoint_type)
{
if (ec)
socket_client_fail(ec, "socket_client_connect");
// Turn off the timeout on the tcp_stream, because
// the websocket stream has its own timeout system.
beast::get_lowest_layer(ws).expires_never();
// Set suggested timeout settings for the websocket
ws.set_option(
websocket::stream_base::timeout::suggested(
beast::role_type::client));
// Perform the websocket handshake
ws.async_handshake(host, "/",
[self = this->shared_from_this()](error ec) {
self->on_handshake(ec);
});
}
/**
* Executes on completion of handshake
*/
template <class T>
void socket_client<T>::on_handshake(error ec)
{
//Creates a new socket session object
std::make_shared<socket_session<T>>(
ws, sess_handler_)
->client_run(std::move(host), std::move(port), ec);
}
/**
* Executes on error
*/
template <class T>
void socket_client<T>::socket_client_fail(beast::error_code ec, char const *what)
{
LOG_ERR << what << ": " << ec.message();
}
} // namespace sock
#endif

View File

@@ -1,112 +0,0 @@
#include <iostream>
#include <string>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/strand.hpp>
#include "socket_server.hpp"
#include "../hplog.hpp"
namespace net = boost::asio; // namespace asio
using tcp = net::ip::tcp;
using error_code = boost::system::error_code;
namespace sock
{
socket_server::socket_server(net::io_context &ioc, tcp::endpoint endpoint, socket_session_handler &session_handler)
: acceptor_(ioc), socket_(ioc),sess_handler_(session_handler)
{
error_code ec;
// Open the acceptor
acceptor_.open(endpoint.protocol(), ec);
if (ec)
{
fail(ec, "open");
return;
}
// Allow address reuse
acceptor_.set_option(net::socket_base::reuse_address(true));
if (ec)
{
fail(ec, "set_option");
return;
}
// Bind to the server address
acceptor_.bind(endpoint, ec);
if (ec)
{
fail(ec, "bind");
return;
}
// Start listening for connections
acceptor_.listen(
net::socket_base::max_listen_connections, ec);
if (ec)
{
fail(ec, "listen");
return;
}
}
/**
* Entry point to socket server which accepts new connections
*/
void socket_server::run()
{
// Start accepting a connection
acceptor_.async_accept(
socket_,
[self = shared_from_this()](error_code ec) {
self->on_accept(ec);
});
}
/**
* Executes on error
*/
void socket_server::fail(error_code ec, char const *what)
{
// Don't report on canceled operations
if (ec == net::error::operation_aborted)
return;
LOG_ERR << what << ": " << ec.message();
}
/**
* Executes on acceptance of new connection
*/
void socket_server::on_accept(error_code ec)
{
if (ec)
{
return fail(ec, "accept");
}
else
{
std::string port = std::to_string(socket_.remote_endpoint().port());
std::string address = socket_.remote_endpoint().address().to_string();
//Creating websocket stream required to pass to initiate a new session
websocket::stream<beast::tcp_stream> ws(std::move(socket_));
// Launch a new session for this connection
std::make_shared<socket_session>(
ws, sess_handler_)
->server_run(std::move(address), std::move(port));
}
// Accept another connection
acceptor_.async_accept(
socket_,
[self = shared_from_this()](error_code ec) {
self->on_accept(ec);
});
}
} // namespace sock

View File

@@ -2,12 +2,16 @@
#define _SOCK_SERVER_LISTENER_H_
#include <boost/asio.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include "socket_session_handler.hpp"
#include "../hplog.hpp"
namespace net = boost::asio; // namespace asio
using tcp = net::ip::tcp;
using error = boost::system::error_code; // from <boost/system/error_code.hpp>
using error_code = boost::system::error_code;
namespace sock
{
@@ -16,22 +20,124 @@ namespace sock
* Represents an active WebSocket server connection
* Based on the implementation from https://github.com/vinniefalco/CppCon2018
*/
class socket_server : public std::enable_shared_from_this<socket_server>
template <class T>
class socket_server : public std::enable_shared_from_this<socket_server<T>>
{
tcp::acceptor acceptor_; // acceptor which accepts new connections
tcp::socket socket_; // socket in which the client connects
socket_session_handler &sess_handler_; // handler passed to gain access to websocket events
tcp::acceptor acceptor; // acceptor which accepts new connections
tcp::socket socket; // socket in which the client connects
socket_session_handler<T> &sess_handler; // handler passed to gain access to websocket events
void fail(error ec, char const *what);
void fail(error_code ec, char const *what);
void on_accept(error ec);
void on_accept(error_code ec);
public:
socket_server(net::io_context &ioc, tcp::endpoint endpoint, socket_session_handler &session_handler);
socket_server(net::io_context &ioc, tcp::endpoint endpoint, socket_session_handler<T> &session_handler);
// Start accepting incoming connections
void run();
};
template <class T>
socket_server<T>::socket_server(net::io_context &ioc, tcp::endpoint endpoint, socket_session_handler<T> &session_handler)
: acceptor(ioc), socket(ioc), sess_handler(session_handler)
{
error_code ec;
// Open the acceptor
acceptor.open(endpoint.protocol(), ec);
if (ec)
{
fail(ec, "open");
return;
}
// Allow address reuse
acceptor.set_option(net::socket_base::reuse_address(true));
if (ec)
{
fail(ec, "set_option");
return;
}
// Bind to the server address
acceptor.bind(endpoint, ec);
if (ec)
{
fail(ec, "bind");
return;
}
// Start listening for connections
acceptor.listen(
net::socket_base::max_listen_connections, ec);
if (ec)
{
fail(ec, "listen");
return;
}
}
/**
* Entry point to socket server which accepts new connections
*/
template <class T>
void socket_server<T>::run()
{
// Start accepting a connection
acceptor.async_accept(
socket,
[self = this->shared_from_this()](error_code ec) {
self->on_accept(ec);
});
}
/**
* Executes on error
*/
template <class T>
void socket_server<T>::fail(error_code ec, char const *what)
{
// Don't report on canceled operations
if (ec == net::error::operation_aborted)
return;
LOG_ERR << what << ": " << ec.message();
}
/**
* Executes on acceptance of new connection
*/
template <class T>
void socket_server<T>::on_accept(error_code ec)
{
if (ec)
{
return fail(ec, "accept");
}
else
{
std::string port = std::to_string(socket.remote_endpoint().port());
std::string address = socket.remote_endpoint().address().to_string();
//Creating websocket stream required to pass to initiate a new session
websocket::stream<beast::tcp_stream> ws(std::move(socket));
// Launch a new session for this connection
std::make_shared<socket_session<T>>(
ws, sess_handler)
->server_run(std::move(address), std::move(port));
}
// Accept another connection
acceptor.async_accept(
socket,
[self = this->shared_from_this()](error_code ec) {
self->on_accept(ec);
});
}
} // namespace sock
#endif

View File

@@ -1,209 +0,0 @@
#include <iostream>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include "socket_session.hpp"
#include "../util.hpp"
namespace net = boost::asio;
using tcp = net::ip::tcp;
using error_code = boost::system::error_code;
namespace sock
{
socket_session::socket_session(websocket::stream<beast::tcp_stream> &websocket, socket_session_handler &sess_handler)
: ws_(std::move(websocket)), sess_handler_(sess_handler)
{
ws_.binary(true);
}
socket_session::~socket_session()
{
sess_handler_.on_close(this);
}
//port and address will be used to identify from which client the message recieved in the handler
void socket_session::server_run(const std::string &&address, const std::string &&port)
{
port_ = port;
address_ = address;
//Set this flag to identify whether this socket session created when node acts as a server
flags_.set(util::SESSION_FLAG::INBOUND);
// Accept the websocket handshake
ws_.async_accept(
[sp = shared_from_this()](
error ec) {
sp->on_accept(ec);
});
}
//port and address will be used to identify from which server the message recieved in the handler
void socket_session::client_run(const std::string &&address, const std::string &&port, error ec)
{
port_ = port;
address_ = address;
if (ec)
return fail(ec, "handshake");
sess_handler_.on_connect(this);
ws_.async_read(
buffer_,
[sp = shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_read(ec, bytes);
});
}
/**
* Executes on error
*/
void socket_session::fail(error_code ec, char const *what)
{
// LOG_ERR << what << ": " << ec.message();
// Don't report these
if (ec == net::error::operation_aborted ||
ec == websocket::error::closed)
return;
}
/**
* Executes on acceptance of new connection
*/
void socket_session::on_accept(error_code ec)
{
// Handle the error, if any
if (ec)
return fail(ec, "accept");
sess_handler_.on_connect(this);
// Read a message
ws_.async_read(
buffer_,
[sp = shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_read(ec, bytes);
});
}
/*
* Executes on completion of recieiving a new message
*/
void socket_session::on_read(error_code ec, std::size_t)
{
//if something goes wrong when trying to read, socket connection will be closed and calling this to inform it to the handler
// read may get called when operation_aborted as well.
// We don't need to process read operation in that case.
if (ec == net::error::operation_aborted)
return;
// Handle the error, if any
if (ec)
{
// if something goes wrong when trying to read, socket connection will be closed and calling this to inform it to the handler
on_close(ec, 1);
return fail(ec, "read");
}
std::string message = beast::buffers_to_string(buffer_.data());
sess_handler_.on_message(this, std::move(message));
// Clear the buffer
buffer_.consume(buffer_.size());
// Read another message
ws_.async_read(
buffer_,
[sp = shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_read(ec, bytes);
});
}
/*
* Send message through an active websocket connection
*/
void socket_session::send(std::string &&ss)
{
// Always add to queue
queue_.push_back(ss);
// Are we already writing?
if (queue_.size() > 1)
return;
// We are not currently writing, so send this immediately
ws_.async_write(
net::buffer(queue_.front()),
[sp = shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_write(ec, bytes);
});
}
/*
* Executes on completion of write operation to a socket
*/
void socket_session::on_write(error_code ec, std::size_t)
{
// Handle the error, if any
if (ec)
return fail(ec, "write");
// Remove the string from the queue
queue_.erase(queue_.begin());
// Send the next message if any
if (!queue_.empty())
ws_.async_write(
net::buffer(queue_.front()),
[sp = shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_write(ec, bytes);
});
}
/*
* Close an active websocket connection gracefully
*/
void socket_session::close()
{
// Close the WebSocket connection
ws_.async_close(websocket::close_code::normal,
[sp = shared_from_this()](
error_code ec) {
sp->on_close(ec, 0);
});
}
/*
* Executes on completion of closing a socket connection
*/
//type will be used identify whether the error is due to failure in closing the web socket or transfer of another exception to this method
void socket_session::on_close(error_code ec, std::int8_t type)
{
// sess_handler_.on_close(this);
// if (type == 1)
// return;
// if (ec)
// return fail(ec, "close");
}
// When called, initializes the unique id string for this session.
void socket_session::init_uniqueid()
{
// Create a unique id for the session combining ip and port.
// We prepare this appended string here because we need to use it for finding elemends from the maps
// for validation purposes whenever a message is received.
uniqueid_.append(address_).append(":").append(port_);
}
} // namespace sock

View File

@@ -1,11 +1,14 @@
#ifndef _SOCK_SERVER_SESSION_H_
#define _SOCK_SERVER_SESSION_H_
#include <string>
#include <memory>
#include <vector>
#include <bitset>
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include "../util.hpp"
#include "socket_session_handler.hpp"
namespace beast = boost::beast;
@@ -14,51 +17,67 @@ namespace websocket = boost::beast::websocket;
namespace http = boost::beast::http;
using tcp = net::ip::tcp;
using error = boost::system::error_code;
using error_code = boost::system::error_code;
namespace sock
{
/**
* Represents an outbound message that is sent with a websocket.
* We use this class to wrap different object types holding actual message contents.
* We use this mechanism to achieve end-to-end zero-copy between original message
* content generator and websocket flush.
*/
class outbound_message
{
public:
// Returns a pointer to the internal buffer owned by the message object.
// Contents of this buffer is the message that is sent/received with the socket.
virtual std::string_view buffer() = 0;
};
//Forward Declaration
template <class T>
class socket_session_handler;
/**
* Represents an active WebSocket connection
*/
class socket_session : public std::enable_shared_from_this<socket_session>
template <class T>
class socket_session : public std::enable_shared_from_this<socket_session<T>>
{
beast::flat_buffer buffer_; // used to store incoming messages
websocket::stream<beast::tcp_stream> ws_; // websocket stream used send an recieve messages
std::vector<std::string> queue_; // uses to store messages temporarily until it is sent to the relevant party
socket_session_handler &sess_handler_; // handler passed to gain access to websocket events
beast::flat_buffer buffer; // used to store incoming messages
websocket::stream<beast::tcp_stream> ws; // websocket stream used send an recieve messages
std::vector<T> queue; // used to store messages temporarily until it is sent to the relevant party
socket_session_handler<T> &sess_handler; // handler passed to gain access to websocket events
void fail(error ec, char const *what);
void fail(error_code ec, char const *what);
void on_accept(error ec);
void on_accept(error_code ec);
void on_read(error ec, std::size_t bytes_transferred);
void on_read(error_code ec, std::size_t bytes_transferred);
void on_write(error ec, std::size_t bytes_transferred);
void on_write(error_code ec, std::size_t bytes_transferred);
void on_close(error ec, std::int8_t type);
void on_close(error_code ec, std::int8_t type);
public:
socket_session(websocket::stream<beast::tcp_stream> &websocket, socket_session_handler &sess_handler);
socket_session(websocket::stream<beast::tcp_stream> &websocket, socket_session_handler<T> &sess_handler);
~socket_session();
// Port and the address of the remote party is being saved to used in the session handler
// to identify from which remote party the message recieved. Since the port is passed as a string
// to identify from which remote party the message recieved. Since the port is passed as a string
// from the parent we store as it is, since we are not going to pass it anywhere or used in a method
// The port of the remote party.
std::string port_;
std::string port;
// The IP address of the remote party.
std::string address_;
std::string address;
// The unique identifier of the remote party (format <ip>:<port>).
std::string uniqueid_;
std::string uniqueid;
// The set of util::SESSION_FLAG enum flags that will be set by user-code of this calss.
// We mainly use this to store contexual information about this session based on the use case.
@@ -66,14 +85,232 @@ public:
std::bitset<8> flags_;
void server_run(const std::string &&address, const std::string &&port);
void client_run(const std::string &&address, const std::string &&port, error ec);
void client_run(const std::string &&address, const std::string &&port, error_code ec);
void send(std::string &&ss);
void send(T msg);
// When called, initializes the unique id string for this session.
void init_uniqueid();
void close();
};
template <class T>
socket_session<T>::socket_session(websocket::stream<beast::tcp_stream> &websocket, socket_session_handler<T> &sess_handler)
: ws(std::move(websocket)), sess_handler(sess_handler)
{
// We use binary data instead of ASCII/UTF8 character data.
ws.binary(true);
}
template <class T>
socket_session<T>::~socket_session()
{
sess_handler.on_close(this);
}
//port and address will be used to identify from which client the message recieved in the handler
template <class T>
void socket_session<T>::server_run(const std::string &&address, const std::string &&port)
{
this->port = port;
this->address = address;
//Set this flag to identify whether this socket session created when node acts as a server
flags_.set(util::SESSION_FLAG::INBOUND);
// Accept the websocket handshake
ws.async_accept(
[sp = this->shared_from_this()](
error_code ec) {
sp->on_accept(ec);
});
}
//port and address will be used to identify from which server the message recieved in the handler
template <class T>
void socket_session<T>::client_run(const std::string &&address, const std::string &&port, error_code ec)
{
this->port = port;
this->address = address;
if (ec)
return fail(ec, "handshake");
sess_handler.on_connect(this);
ws.async_read(
buffer,
[sp = this->shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_read(ec, bytes);
});
}
/**
* Executes on error
*/
template <class T>
void socket_session<T>::fail(error_code ec, char const *what)
{
// LOG_ERR << what << ": " << ec.message();
// Don't report these
if (ec == net::error::operation_aborted ||
ec == websocket::error::closed)
return;
}
/**
* Executes on acceptance of new connection
*/
template <class T>
void socket_session<T>::on_accept(error_code ec)
{
// Handle the error, if any
if (ec)
return fail(ec, "accept");
sess_handler.on_connect(this);
// Read a message
ws.async_read(
buffer,
[sp = this->shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_read(ec, bytes);
});
}
/*
* Executes on completion of recieiving a new message
*/
template <class T>
void socket_session<T>::on_read(error_code ec, std::size_t)
{
//if something goes wrong when trying to read, socket connection will be closed and calling this to inform it to the handler
// read may get called when operation_aborted as well.
// We don't need to process read operation in that case.
if (ec == net::error::operation_aborted)
return;
// Handle the error, if any
if (ec)
{
// if something goes wrong when trying to read, socket connection will be closed and calling this to inform it to the handler
on_close(ec, 1);
return fail(ec, "read");
}
// Wrap the buffer data in a string_view and call session handler.
// We DO NOT transfer ownership of buffer data to the session handler. It should
// read and process the message and we will clear the buffer after its done with it.
const char *buffer_data = net::buffer_cast<const char *>(buffer.data());
std::string_view message(buffer_data, buffer.size());
sess_handler.on_message(this, message);
// Clear the buffer
buffer.consume(buffer.size());
// Read another message
ws.async_read(
buffer,
[sp = this->shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_read(ec, bytes);
});
}
/*
* Send message through an active websocket connection
*/
template <class T>
void socket_session<T>::send(T msg)
{
// Always add to queue
queue.push_back(std::move(msg));
// Are we already writing?
if (queue.size() > 1)
return;
std::string_view sv = queue.front().buffer();
// We are not currently writing, so send this immediately
ws.async_write(
// Project the outbound_message buffer from the queue front into the asio buffer.
net::buffer(sv.data(), sv.length()),
[sp = this->shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_write(ec, bytes);
});
}
/*
* Executes on completion of write operation to a socket
*/
template <class T>
void socket_session<T>::on_write(error_code ec, std::size_t)
{
// Handle the error, if any
if (ec)
return fail(ec, "write");
// Remove the string from the queue
queue.erase(queue.begin());
// Send the next message if any
if (!queue.empty())
{
std::string_view sv = queue.front().buffer();
ws.async_write(
net::buffer(sv.data(), sv.length()),
[sp = this->shared_from_this()](
error_code ec, std::size_t bytes) {
sp->on_write(ec, bytes);
});
}
}
/*
* Close an active websocket connection gracefully
*/
template <class T>
void socket_session<T>::close()
{
// Close the WebSocket connection
ws.async_close(websocket::close_code::normal,
[sp = this->shared_from_this()](
error_code ec) {
sp->on_close(ec, 0);
});
}
/*
* Executes on completion of closing a socket connection
*/
//type will be used identify whether the error is due to failure in closing the web socket or transfer of another exception to this method
template <class T>
void socket_session<T>::on_close(error_code ec, std::int8_t type)
{
// sess_handler.on_close(this);
// if (type == 1)
// return;
// if (ec)
// return fail(ec, "close");
}
// When called, initializes the unique id string for this session.
template <class T>
void socket_session<T>::init_uniqueid()
{
// Create a unique id for the session combining ip and port.
// We prepare this appended string here because we need to use it for finding elemends from the maps
// for validation purposes whenever a message is received.
uniqueid.append(address).append(":").append(port);
}
} // namespace sock
#endif

View File

@@ -7,28 +7,30 @@ namespace sock
{
// Forward declaration
template <class T>
class socket_session;
/**
* Represents a WebSocket sessions handler. Can inherit from this class and access websocket events
*/
template <class T>
class socket_session_handler
{
public:
/**
* Executes on initiation of a new connection
*/
virtual void on_connect(socket_session *session) = 0;
virtual void on_connect(socket_session<T> *session) = 0;
/**
* Executes on recieval of new message
*/
virtual void on_message(socket_session *session, std::string &&message) = 0;
virtual void on_message(socket_session<T> *session, std::string_view message) = 0;
/**
* Executes on websocket connection close
*/
virtual void on_close(socket_session *session) = 0;
virtual void on_close(socket_session<T> *session) = 0;
};
} // namespace sock