diff --git a/include/ki/protocol/net/Participant.h b/include/ki/protocol/net/Participant.h deleted file mode 100644 index 67a11aa..0000000 --- a/include/ki/protocol/net/Participant.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -#include -#include - -#define KI_DEFAULT_MAXIMUM_RECEIVE_SIZE 0x2000 -#define KI_START_SIGNAL 0xF00D - -namespace ki -{ -namespace protocol -{ -namespace net -{ - enum class ReceiveState - { - // Waiting for the 0xF00D start signal. - WAITING_FOR_START_SIGNAL, - - // Waiting for the 2-byte length. - WAITING_FOR_LENGTH, - - // Waiting for the packet data. - WAITING_FOR_PACKET - }; - - enum class ParticipantType - { - SERVER, - CLIENT - }; - - /** - * This class implements the packet framing logic when - * sending and receiving data to/from an external source. - */ - class Participant - { - public: - Participant(ParticipantType type); - virtual ~Participant() = default; - - ParticipantType get_type() const; - void set_type(ParticipantType type); - - uint16_t get_maximum_packet_size() const; - void set_maximum_packet_size(uint16_t maximum_packet_size); - protected: - std::stringstream m_data_stream; - - /** - * Frames raw data into a Packet, and transmits it. - */ - void send_data(const char *data, size_t size); - - /** - * Process incoming raw data into Packets. - * Once a packet is read into the internal data - * stream, handle_packet_available is called. - */ - void process_data(const char *data, size_t size); - - virtual void send_packet_data(const char *data, const size_t size) = 0; - virtual void on_packet_available() {}; - virtual void close() = 0; - private: - ParticipantType m_type; - uint16_t m_maximum_packet_size; - - ReceiveState m_receive_state; - uint16_t m_start_signal; - uint16_t m_incoming_packet_size; - uint8_t m_shift; - }; -} -} -} diff --git a/include/ki/protocol/net/Session.h b/include/ki/protocol/net/Session.h new file mode 100644 index 0000000..f3b7682 --- /dev/null +++ b/include/ki/protocol/net/Session.h @@ -0,0 +1,122 @@ +#pragma once +#include "PacketHeader.h" +#include "../control/Opcode.h" +#include "../../util/Serializable.h" +#include +#include +#include +#include + +#define KI_DEFAULT_MAXIMUM_RECEIVE_SIZE 0x2000 +#define KI_START_SIGNAL 0xF00D +#define KI_CONNECTION_TIMEOUT 3 + +namespace ki +{ +namespace protocol +{ +namespace net +{ + enum class ReceiveState + { + // Waiting for the 0xF00D start signal. + WAITING_FOR_START_SIGNAL, + + // Waiting for the 2-byte length. + WAITING_FOR_LENGTH, + + // Waiting for the packet data. + WAITING_FOR_PACKET + }; + + /** + * This class implements session and packet framing logic + * when sending and receiving data to/from an external + * source. + */ + class Session + { + public: + explicit Session(uint16_t id = 0); + virtual ~Session() = default; + + uint16_t get_maximum_packet_size() const; + void set_maximum_packet_size(uint16_t maximum_packet_size); + + uint16_t get_id() const; + bool is_established() const; + + uint8_t get_access_level() const; + void set_access_level(uint8_t access_level); + + uint16_t get_latency() const; + + virtual bool is_alive() const = 0; + + void send_packet(bool is_control, uint8_t opcode, + const util::Serializable &data); + protected: + /* Higher-level session members */ + uint16_t m_id; + bool m_established; + uint8_t m_access_level; + + /* Timing members */ + std::chrono::steady_clock::time_point m_creation_time; + std::chrono::steady_clock::time_point m_connection_time; + std::chrono::steady_clock::time_point m_establish_time; + std::chrono::steady_clock::time_point m_last_received_heartbeat_time; + std::chrono::steady_clock::time_point m_last_sent_heartbeat_time; + bool m_waiting_for_keep_alive_response; + uint16_t m_latency; + + // The packet data stream + std::stringstream m_data_stream; + + /** + * Reads a serializable structure from the data stream. + */ + template + DataT read_data() + { + static_assert(std::is_base_of::value, + "DataT must inherit Serializable."); + + DataT data = DataT(); + data.read_from(m_data_stream); + return data; + } + + /** + * Frames raw data into a Packet, and transmits it. + */ + void send_data(const char *data, size_t size); + + /** + * Process incoming raw data into Packets. + * Once a packet is read into the internal data + * stream, handle_packet_available is called. + */ + void process_data(const char *data, size_t size); + + /* Event handlers */ + virtual void on_invalid_packet() {} + virtual void on_control_message(const PacketHeader &header) {} + virtual void on_application_message(const PacketHeader &header) {} + + /* Low-level socket methods */ + virtual void send_packet_data(const char *data, const size_t size) = 0; + virtual void close() = 0; + private: + /* Low-level networking members */ + uint16_t m_maximum_packet_size; + ReceiveState m_receive_state; + uint16_t m_start_signal; + uint16_t m_incoming_packet_size; + uint8_t m_shift; + + void on_packet_available(); + }; +} +} +} diff --git a/src/protocol/net/Participant.cpp b/src/protocol/net/Session.cpp similarity index 62% rename from src/protocol/net/Participant.cpp rename to src/protocol/net/Session.cpp index 8e1fb73..cbfbbd3 100644 --- a/src/protocol/net/Participant.cpp +++ b/src/protocol/net/Session.cpp @@ -1,6 +1,5 @@ -#include "ki/protocol/net/Participant.h" +#include "ki/protocol/net/Session.h" #include "ki/protocol/exception.h" -#include namespace ki { @@ -8,43 +7,74 @@ namespace protocol { namespace net { - Participant::Participant(const ParticipantType type) - { - m_type = type; - m_maximum_packet_size = KI_DEFAULT_MAXIMUM_RECEIVE_SIZE; + Session::Session(const uint16_t id) + { + m_id = id; + m_established = false; + m_access_level = 0; + m_latency = 0; + m_creation_time = std::chrono::steady_clock::now(); + m_waiting_for_keep_alive_response = false; + m_maximum_packet_size = KI_DEFAULT_MAXIMUM_RECEIVE_SIZE; m_receive_state = ReceiveState::WAITING_FOR_START_SIGNAL; m_start_signal = 0; m_incoming_packet_size = 0; m_shift = 0; } - ParticipantType Participant::get_type() const - { - return m_type; - } - - void Participant::set_type(const ParticipantType type) - { - m_type = type; - } - - - uint16_t Participant::get_maximum_packet_size() const + uint16_t Session::get_maximum_packet_size() const { return m_maximum_packet_size; } - void Participant::set_maximum_packet_size(const uint16_t maximum_packet_size) + void Session::set_maximum_packet_size(const uint16_t maximum_packet_size) { m_maximum_packet_size = maximum_packet_size; } - void Participant::send_data(const char* data, const size_t size) + uint16_t Session::get_id() const + { + return m_id; + } + + bool Session::is_established() const + { + return m_established; + } + + uint8_t Session::get_access_level() const + { + return m_access_level; + } + + void Session::set_access_level(const uint8_t access_level) + { + m_access_level = access_level; + } + + uint16_t Session::get_latency() const + { + return m_latency; + } + + void Session::send_packet(const bool is_control, const uint8_t opcode, + const util::Serializable& data) + { + std::ostringstream ss; + PacketHeader header(is_control, opcode); + header.write_to(ss); + data.write_to(ss); + + const auto buffer = ss.str(); + send_data(buffer.c_str(), buffer.length()); + } + + void Session::send_data(const char* data, const size_t size) { // Allocate the entire buffer char *packet_data = new char[size + 4]; - + // Add the frame header ((uint16_t *)packet_data)[0] = KI_START_SIGNAL; ((uint16_t *)packet_data)[1] = size; @@ -55,7 +85,7 @@ namespace net delete[] packet_data; } - void Participant::process_data(const char *data, const size_t size) + void Session::process_data(const char *data, const size_t size) { size_t position = 0; while (position < size) @@ -133,6 +163,29 @@ namespace net } } + void Session::on_packet_available() + { + // Read the packet header + PacketHeader header; + try + { + header.read_from(m_data_stream); + } + catch (parse_error &e) + { + on_invalid_packet(); + return; + } + + // Hand off to the right handler based on + // whether this is a control packet or not + if (header.is_control()) + on_control_message(header); + else if (m_established) + on_application_message(header); + else + close(); + } } } }