This commit is contained in:
@@ -1,60 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cxxabi.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::string GetBasicClassName(const T& a_Value) {
|
|
||||||
int status;
|
|
||||||
char* demangled = abi::__cxa_demangle(typeid(a_Value).name(), 0, 0, &status);
|
|
||||||
if (status != 0)
|
|
||||||
return "";
|
|
||||||
return std::string(demangled);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::string GetBasicClassName() {
|
|
||||||
int status;
|
|
||||||
char* demangled = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
|
|
||||||
if (status != 0)
|
|
||||||
return "";
|
|
||||||
return std::string(demangled);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct Reflector {
|
|
||||||
static std::string GetClassName() {
|
|
||||||
return GetBasicClassName<T>();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct Reflector<std::string> {
|
|
||||||
static std::string GetClassName() {
|
|
||||||
return "std::string";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename K, typename V>
|
|
||||||
struct Reflector<std::map<K, V>> {
|
|
||||||
static std::string GetClassName();
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct Reflector<std::vector<T>> {
|
|
||||||
static std::string GetClassName();
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::string Reflector<std::vector<T>>::GetClassName() {
|
|
||||||
return "std::vector<" + Reflector<T>::GetClassName() + ">";
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename K, typename V>
|
|
||||||
std::string Reflector<std::map<K, V>>::GetClassName() {
|
|
||||||
return "std::map<" + Reflector<K>::GetClassName() + ", " + Reflector<V>::GetClassName() + ">";
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace sp
|
|
||||||
@@ -7,12 +7,14 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <ostream>
|
#include <functional>
|
||||||
|
|
||||||
namespace sp {
|
namespace sp {
|
||||||
|
|
||||||
class DataBuffer;
|
class DataBuffer;
|
||||||
|
|
||||||
|
using ReadFunc = std::function<void(std::uint8_t&)>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class VarInt
|
* \class VarInt
|
||||||
* \brief Variable-length format such that smaller numbers use fewer bytes.
|
* \brief Variable-length format such that smaller numbers use fewer bytes.
|
||||||
@@ -57,6 +59,7 @@ class VarInt {
|
|||||||
*/
|
*/
|
||||||
friend DataBuffer& operator>>(DataBuffer& in, VarInt& var);
|
friend DataBuffer& operator>>(DataBuffer& in, VarInt& var);
|
||||||
|
|
||||||
|
void Read(const ReadFunc& read);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sp
|
} // namespace sp
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <sp/io/IOInterface.h>
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
namespace io {
|
|
||||||
|
|
||||||
struct FileTag {
|
|
||||||
enum OpenMode {
|
|
||||||
In = 1,
|
|
||||||
Out = 1 << 1,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class IOInterface<FileTag> {
|
|
||||||
private:
|
|
||||||
std::unique_ptr<std::ifstream> m_FileInput;
|
|
||||||
std::unique_ptr<std::ofstream> m_FileOutput;
|
|
||||||
|
|
||||||
public:
|
|
||||||
IOInterface(const std::string& a_FilePath, unsigned int a_OpenMode);
|
|
||||||
IOInterface(IOInterface&& other);
|
|
||||||
|
|
||||||
DataBuffer Read(std::size_t a_Amount);
|
|
||||||
void Write(const sp::DataBuffer& a_Data);
|
|
||||||
};
|
|
||||||
|
|
||||||
using File = IOInterface<FileTag>;
|
|
||||||
|
|
||||||
} // namespace io
|
|
||||||
} // namespace sp
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <sp/common/DataBuffer.h>
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
namespace io {
|
|
||||||
|
|
||||||
template <typename IOTag>
|
|
||||||
class IOInterface {
|
|
||||||
public:
|
|
||||||
DataBuffer Read(std::size_t a_Amount);
|
|
||||||
void Write(const DataBuffer& a_Data);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename TOption>
|
|
||||||
class MessageEncapsulator {
|
|
||||||
public:
|
|
||||||
static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOption& a_Option);
|
|
||||||
static DataBuffer Decapsulate(DataBuffer& a_Data, const TOption& a_Option);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
|
||||||
class Stream {
|
|
||||||
protected:
|
|
||||||
MessageDispatcher m_Dispatcher;
|
|
||||||
IOInterface<IOTag> m_Interface;
|
|
||||||
std::tuple<TOptions...> m_Options;
|
|
||||||
|
|
||||||
using MessageBase = typename MessageDispatcher::MessageBaseType;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Stream() {}
|
|
||||||
Stream(IOInterface<IOTag>&& a_Interface, TOptions&&... a_Options);
|
|
||||||
Stream(Stream&& a_Stream);
|
|
||||||
|
|
||||||
void RecieveMessages();
|
|
||||||
void SendMessage(const MessageBase& a_Message);
|
|
||||||
|
|
||||||
|
|
||||||
template <typename TOption>
|
|
||||||
TOption& GetOption() {
|
|
||||||
return std::get<TOption>(m_Options);
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageDispatcher& GetDispatcher() {
|
|
||||||
return m_Dispatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOptions&... a_Options);
|
|
||||||
static DataBuffer Decapsulate(DataBuffer& a_Data, const TOptions&... a_Options);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace io
|
|
||||||
} // namespace sp
|
|
||||||
|
|
||||||
#include <sp/io/IOInterfaceImpl.inl>
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <sp/extensions/Compress.h>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
template <typename... TOptions>
|
|
||||||
struct MessageEncapsulatorPack {};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct MessageEncapsulatorPack<> {
|
|
||||||
static DataBuffer Encapsulate(const DataBuffer& a_Data) {
|
|
||||||
return a_Data;
|
|
||||||
}
|
|
||||||
static DataBuffer Decapsulate(DataBuffer& a_Data) {
|
|
||||||
return a_Data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename TOption, typename... TOptions>
|
|
||||||
struct MessageEncapsulatorPack<TOption, TOptions...> {
|
|
||||||
static DataBuffer Encapsulate(const DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) {
|
|
||||||
DataBuffer data = io::MessageEncapsulator<TOption>::Encapsulate(a_Data, a_Option);
|
|
||||||
return MessageEncapsulatorPack<TOptions...>::Encapsulate(data, a_Options...);
|
|
||||||
}
|
|
||||||
static DataBuffer Decapsulate(DataBuffer& a_Data, const TOption& a_Option, const TOptions&... a_Options) {
|
|
||||||
DataBuffer data = io::MessageEncapsulator<TOption>::Decapsulate(a_Data, a_Option);
|
|
||||||
return MessageEncapsulatorPack<TOptions...>::Decapsulate(data, a_Options...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
|
|
||||||
namespace io {
|
|
||||||
|
|
||||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
|
||||||
Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Stream(IOInterface<IOTag>&& a_Interface, TOptions&&... a_Options) :
|
|
||||||
m_Interface(std::move(a_Interface)), m_Options(std::make_tuple<TOptions...>(std::move(a_Options)...)) {}
|
|
||||||
|
|
||||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
|
||||||
Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Stream(
|
|
||||||
Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>&& a_Stream) :
|
|
||||||
m_Dispatcher(std::move(a_Stream.m_Dispatcher)), m_Interface(std::move(a_Stream.m_Interface)) {}
|
|
||||||
|
|
||||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
|
||||||
void Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::SendMessage(const MessageBase& a_Message) {
|
|
||||||
DataBuffer data = a_Message.Write();
|
|
||||||
TupleForEach([&data](const auto&... a_Options) {
|
|
||||||
data = Encapsulate(data, a_Options...);
|
|
||||||
}, m_Options);
|
|
||||||
DataBuffer finalData;
|
|
||||||
finalData.Reserve(data.GetSize() + sizeof(VarInt::MAX_VALUE));
|
|
||||||
finalData << VarInt{data.GetSize()} << data;
|
|
||||||
m_Interface.Write(finalData);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
|
||||||
void Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::RecieveMessages() {
|
|
||||||
while (true) {
|
|
||||||
// reading the first VarInt part byte by byte
|
|
||||||
std::uint64_t lenghtValue = 0;
|
|
||||||
unsigned int readPos = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
static constexpr int SEGMENT_BITS = (1 << 7) - 1;
|
|
||||||
static constexpr int CONTINUE_BIT = 1 << 7;
|
|
||||||
|
|
||||||
DataBuffer buffer = m_Interface.Read(sizeof(std::uint8_t));
|
|
||||||
|
|
||||||
// eof
|
|
||||||
if (buffer.GetSize() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::uint8_t part;
|
|
||||||
buffer >> part;
|
|
||||||
lenghtValue |= static_cast<std::uint64_t>(part & SEGMENT_BITS) << readPos;
|
|
||||||
|
|
||||||
if ((part & CONTINUE_BIT) == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
readPos += 7;
|
|
||||||
|
|
||||||
if (readPos >= 8 * sizeof(lenghtValue))
|
|
||||||
throw std::runtime_error("VarInt is too big");
|
|
||||||
}
|
|
||||||
|
|
||||||
// nothing to read
|
|
||||||
if (lenghtValue == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DataBuffer buffer;
|
|
||||||
buffer = m_Interface.Read(lenghtValue);
|
|
||||||
|
|
||||||
TupleForEach([&buffer, lenghtValue](const auto&... a_Options) {
|
|
||||||
buffer = Decapsulate(buffer, a_Options...);
|
|
||||||
}, m_Options);
|
|
||||||
|
|
||||||
VarInt packetType;
|
|
||||||
buffer >> packetType;
|
|
||||||
|
|
||||||
static const MessageFactory messageFactory;
|
|
||||||
|
|
||||||
std::unique_ptr<MessageBase> message = messageFactory.CreateMessage(packetType.GetValue());
|
|
||||||
|
|
||||||
assert(message != nullptr);
|
|
||||||
|
|
||||||
message->Read(buffer);
|
|
||||||
|
|
||||||
GetDispatcher().Dispatch(*message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
|
||||||
DataBuffer Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Encapsulate(
|
|
||||||
const DataBuffer& a_Data, const TOptions&... a_Options) {
|
|
||||||
return details::MessageEncapsulatorPack<TOptions...>::Encapsulate(a_Data, a_Options...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename IOTag, typename MessageDispatcher, typename MessageFactory, typename... TOptions>
|
|
||||||
DataBuffer Stream<IOTag, MessageDispatcher, MessageFactory, TOptions...>::Decapsulate(
|
|
||||||
DataBuffer& a_Data, const TOptions&... a_Options) {
|
|
||||||
return details::MessageEncapsulatorPack<TOptions...>::Decapsulate(a_Data, a_Options...);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace io
|
|
||||||
} // namespace sp
|
|
||||||
13
include/sp/io/IoInterface.h
Normal file
13
include/sp/io/IoInterface.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sp/common/DataBuffer.h>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
|
||||||
|
class IoInterface {
|
||||||
|
public:
|
||||||
|
virtual DataBuffer Read(std::size_t a_Amount) = 0;
|
||||||
|
virtual void Write(const DataBuffer& a_Data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sp
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <sp/io/IOInterface.h>
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
namespace io {
|
|
||||||
|
|
||||||
struct MemoryTag {};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class IOInterface<MemoryTag> {
|
|
||||||
private:
|
|
||||||
sp::DataBuffer m_VirtualIO;
|
|
||||||
|
|
||||||
public:
|
|
||||||
sp::DataBuffer Read(std::size_t a_Amount);
|
|
||||||
void Write(const sp::DataBuffer& a_Data);
|
|
||||||
};
|
|
||||||
|
|
||||||
using Memory = IOInterface<MemoryTag>;
|
|
||||||
|
|
||||||
} // namespace io
|
|
||||||
} // namespace sp
|
|
||||||
16
include/sp/io/MessageEncapsulator.h
Normal file
16
include/sp/io/MessageEncapsulator.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sp/common/DataBuffer.h>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
|
||||||
|
class MessageEncapsulator {
|
||||||
|
public:
|
||||||
|
MessageEncapsulator() {}
|
||||||
|
virtual ~MessageEncapsulator() {}
|
||||||
|
|
||||||
|
virtual DataBuffer Encapsulate(const DataBuffer& a_Data) = 0;
|
||||||
|
virtual DataBuffer Decapsulate(DataBuffer& a_Data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sp
|
||||||
36
include/sp/io/MessageIO.h
Normal file
36
include/sp/io/MessageIO.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/pfr.hpp>
|
||||||
|
#include <sp/common/ByteSwapping.h>
|
||||||
|
#include <sp/common/DataBuffer.h>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void WriteField(DataBuffer& a_Buffer, const T& a_Data) {
|
||||||
|
T swapped = a_Data;
|
||||||
|
ToNetwork(swapped);
|
||||||
|
a_Buffer << swapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void ReadField(DataBuffer& a_Buffer, T& a_Data) {
|
||||||
|
a_Buffer >> a_Data;
|
||||||
|
FromNetwork(a_Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TData>
|
||||||
|
DataBuffer WriteMessage(const TData& a_MessageData) {
|
||||||
|
DataBuffer buffer;
|
||||||
|
boost::pfr::for_each_field(a_MessageData, [&buffer](const auto& a_Field) { WriteField(buffer, a_Field); });
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TData>
|
||||||
|
void ReadMessage(DataBuffer& a_Buffer, TData& a_MessageData) {
|
||||||
|
boost::pfr::for_each_field(a_MessageData, [&a_Buffer](auto& a_Field) { ReadField(a_Buffer, a_Field); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace sp
|
||||||
34
include/sp/io/MessageStream.h
Normal file
34
include/sp/io/MessageStream.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sp/common/DataBuffer.h>
|
||||||
|
#include <sp/io/IoInterface.h>
|
||||||
|
#include <sp/io/MessageEncapsulator.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
|
||||||
|
template <typename TMessageFactory>
|
||||||
|
class MessageStream {
|
||||||
|
private:
|
||||||
|
std::vector<MessageEncapsulator> m_Encapsulators;
|
||||||
|
std::shared_ptr<IoInterface> m_Stream;
|
||||||
|
|
||||||
|
using MessageBaseType = typename TMessageFactory::MessageBaseType;
|
||||||
|
using MessageIdType = typename MessageBaseType::MessageIdType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MessageStream(std::shared_ptr<IoInterface>&& a_Stream) : m_Stream(std::move(a_Stream)) {}
|
||||||
|
|
||||||
|
std::unique_ptr<MessageBaseType> ReadMessage();
|
||||||
|
std::unique_ptr<MessageBaseType> ReadMessage(MessageIdType a_Id);
|
||||||
|
|
||||||
|
void WriteMessage(const MessageBaseType& a_Message, bool a_WriteId = true);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DataBuffer ReadAndDecapsulate();
|
||||||
|
std::unique_ptr<MessageBaseType> MakeMessage(DataBuffer& buffer, MessageIdType a_Id);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sp
|
||||||
|
|
||||||
|
#include <sp/io/MessageStream.inl>
|
||||||
63
include/sp/io/MessageStream.inl
Normal file
63
include/sp/io/MessageStream.inl
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sp/common/VarInt.h>
|
||||||
|
#include <sp/protocol/MessageFactory.h>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
|
||||||
|
template <typename TMessageFactory>
|
||||||
|
DataBuffer MessageStream<TMessageFactory>::ReadAndDecapsulate() {
|
||||||
|
VarInt messageLength;
|
||||||
|
messageLength.Read([this](std::uint8_t& data) { m_Stream->Read(1) >> data; });
|
||||||
|
std::size_t amount = messageLength.GetValue();
|
||||||
|
DataBuffer buffer = m_Stream->Read(amount);
|
||||||
|
|
||||||
|
for (MessageEncapsulator& enc : m_Encapsulators) {
|
||||||
|
buffer = enc.Decapsulate(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TMessageFactory>
|
||||||
|
std::unique_ptr<typename TMessageFactory::MessageBaseType> MessageStream<TMessageFactory>::MakeMessage(DataBuffer& buffer, MessageIdType a_Id) {
|
||||||
|
static const TMessageFactory FACTORY;
|
||||||
|
auto message = FACTORY.CreateMessage(a_Id);
|
||||||
|
message->Read(buffer);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TMessageFactory>
|
||||||
|
std::unique_ptr<typename TMessageFactory::MessageBaseType> MessageStream<TMessageFactory>::ReadMessage(MessageIdType a_Id) {
|
||||||
|
|
||||||
|
DataBuffer buffer = ReadAndDecapsulate();
|
||||||
|
|
||||||
|
return MakeMessage(buffer, a_Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TMessageFactory>
|
||||||
|
std::unique_ptr<typename TMessageFactory::MessageBaseType> MessageStream<TMessageFactory>::ReadMessage() {
|
||||||
|
DataBuffer buffer = ReadAndDecapsulate();
|
||||||
|
|
||||||
|
VarInt messageId;
|
||||||
|
buffer >> messageId;
|
||||||
|
|
||||||
|
return MakeMessage(buffer, MessageIdType(messageId.GetValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TMessageFactory>
|
||||||
|
void MessageStream<TMessageFactory>::WriteMessage(const MessageBaseType& a_Message, bool a_WriteId) {
|
||||||
|
DataBuffer buffer;
|
||||||
|
if (a_WriteId)
|
||||||
|
buffer << VarInt{static_cast<std::uint64_t>(a_Message.GetId())};
|
||||||
|
buffer << a_Message.Write();
|
||||||
|
for (MessageEncapsulator& enc : m_Encapsulators) {
|
||||||
|
buffer = enc.Encapsulate(buffer);
|
||||||
|
}
|
||||||
|
DataBuffer header;
|
||||||
|
header << VarInt{buffer.GetSize()};
|
||||||
|
m_Stream->Write(header);
|
||||||
|
m_Stream->Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sp
|
||||||
40
include/sp/io/StdIo.h
Normal file
40
include/sp/io/StdIo.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sp/io/IoInterface.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
|
||||||
|
class StdInput : public IoInterface {
|
||||||
|
private:
|
||||||
|
std::istream& m_Io;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StdInput(std::istream& a_Io) : m_Io(a_Io) {}
|
||||||
|
|
||||||
|
virtual DataBuffer Read(std::size_t a_Amount) override {
|
||||||
|
DataBuffer buffer(a_Amount);
|
||||||
|
m_Io.read(reinterpret_cast<char*>(buffer.data()), a_Amount);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Write(const DataBuffer& a_Data) override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StdOuput : public IoInterface {
|
||||||
|
private:
|
||||||
|
std::ostream& m_Io;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StdOuput(std::ostream& a_Io) : m_Io(a_Io) {}
|
||||||
|
|
||||||
|
virtual DataBuffer Read(std::size_t a_Amount) override {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Write(const DataBuffer& a_Data) override {
|
||||||
|
m_Io.write(reinterpret_cast<const char*>(a_Data.data()), a_Data.GetSize());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sp
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <sp/protocol/MessageBase.h>
|
#include <sp/protocol/MessageBase.h>
|
||||||
#include <sp/protocol/MessageIO.h>
|
#include <sp/io/MessageIO.h>
|
||||||
|
|
||||||
namespace sp {
|
namespace sp {
|
||||||
|
|
||||||
@@ -9,7 +9,6 @@ template <typename TData, typename TMessageID, TMessageID ID, typename THandler>
|
|||||||
class ConcreteMessage : public MessageBase<TMessageID, THandler> {
|
class ConcreteMessage : public MessageBase<TMessageID, THandler> {
|
||||||
public:
|
public:
|
||||||
using DataType = TData;
|
using DataType = TData;
|
||||||
using ThisType = ConcreteMessage<TData, TMessageID, ID, THandler>;
|
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
ConcreteMessage(const T&... args) : m_Data{args...} {}
|
ConcreteMessage(const T&... args) : m_Data{args...} {}
|
||||||
@@ -21,15 +20,15 @@ class ConcreteMessage : public MessageBase<TMessageID, THandler> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void Dispatch(THandler& handler) const override {
|
virtual void Dispatch(THandler& handler) const override {
|
||||||
handler.Handle(static_cast<const ThisType&>(*this));
|
handler.Handle(static_cast<const DataType&>(m_Data));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Read(std::istream& a_Is) override {
|
virtual void Read(DataBuffer& a_Buffer) override {
|
||||||
details::ReadMessage(a_Is, m_Data);
|
details::ReadMessage(a_Buffer, m_Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Write(std::ostream& a_Os) const override {
|
virtual DataBuffer Write() const override {
|
||||||
details::WriteMessage(a_Os, m_Data);
|
return details::WriteMessage(m_Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
#include <sp/common/DataBuffer.h>
|
||||||
|
|
||||||
namespace sp {
|
namespace sp {
|
||||||
|
|
||||||
@@ -17,8 +18,8 @@ class MessageBase {
|
|||||||
|
|
||||||
virtual void Dispatch(HandlerType& handler) const = 0;
|
virtual void Dispatch(HandlerType& handler) const = 0;
|
||||||
|
|
||||||
virtual void Read(std::istream& a_Is) = 0;
|
virtual void Read(DataBuffer& a_Buffer) = 0;
|
||||||
virtual void Write(std::ostream& a_Os) const = 0;
|
virtual DataBuffer Write() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sp
|
} // namespace sp
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ template <typename TBase, typename TTMessages>
|
|||||||
class MessageFactory {
|
class MessageFactory {
|
||||||
public:
|
public:
|
||||||
using IdType = typename TBase::MessageIdType;
|
using IdType = typename TBase::MessageIdType;
|
||||||
|
using MessageBaseType = TBase;
|
||||||
|
|
||||||
MessageFactory() {
|
MessageFactory() {
|
||||||
constexpr std::size_t messageCount = std::tuple_size_v<TTMessages>;
|
constexpr std::size_t messageCount = std::tuple_size_v<TTMessages>;
|
||||||
|
|||||||
133
include/sp/protocol/MessageHandler.h
Normal file
133
include/sp/protocol/MessageHandler.h
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace sp
|
||||||
|
{
|
||||||
|
// This class is inspired by https://arobenko.gitbooks.io/comms-protocols-cpp/content/
|
||||||
|
|
||||||
|
// TAll is all the message types, that need to be handled, bundled in std::tuple
|
||||||
|
template <typename TAll>
|
||||||
|
class MessageHandler;
|
||||||
|
|
||||||
|
// Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage
|
||||||
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||||
|
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||||
|
typename T11, typename T12, typename T13, typename T14, typename T15,
|
||||||
|
typename T16, typename T17, typename T18, typename T19, typename T20,
|
||||||
|
typename... TRest>
|
||||||
|
class MessageHandler<std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T11, T13, T14, T15, T16, T17, T18, T19, T20, TRest...> > : public MessageHandler<std::tuple<TRest...> >
|
||||||
|
{
|
||||||
|
using Base = MessageHandler<std::tuple<TRest...> >;
|
||||||
|
public:
|
||||||
|
using Base::Handle; // Don't hide all Handle() functions from base classes
|
||||||
|
virtual void Handle(const typename T1::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T2::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T3::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T4::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T5::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T6::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T7::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T8::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T9::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T10::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T11::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T12::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T13::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T14::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T15::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T16::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T17::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T18::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T19::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T20::DataType& msg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 10 by 10
|
||||||
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||||
|
typename T6, typename T7, typename T8, typename T9, typename T10,
|
||||||
|
typename... TRest>
|
||||||
|
class MessageHandler<std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRest...> > : public MessageHandler<std::tuple<TRest...> >
|
||||||
|
{
|
||||||
|
using Base = MessageHandler<std::tuple<TRest...> >;
|
||||||
|
public:
|
||||||
|
using Base::Handle; // Don't hide all Handle() functions from base classes
|
||||||
|
virtual void Handle(const typename T1::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T2::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T3::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T4::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T5::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T6::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T7::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T8::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T9::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T10::DataType& msg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 5 by 5
|
||||||
|
template <
|
||||||
|
typename T1, typename T2, typename T3, typename T4, typename T5,
|
||||||
|
typename... TRest>
|
||||||
|
class MessageHandler<std::tuple<T1, T2, T3, T4, T5, TRest...> > : public MessageHandler<std::tuple<TRest...> >
|
||||||
|
{
|
||||||
|
using Base = MessageHandler<std::tuple<TRest...> >;
|
||||||
|
public:
|
||||||
|
using Base::Handle; // Don't hide all Handle() functions from base classes
|
||||||
|
virtual void Handle(const typename T1::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T2::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T3::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T4::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T5::DataType& msg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deal with rest with 4 types
|
||||||
|
template <typename T1, typename T2, typename T3, typename T4>
|
||||||
|
class MessageHandler<std::tuple<T1, T2, T3, T4> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~MessageHandler() {}
|
||||||
|
virtual void Handle(const typename T1::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T2::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T3::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T4::DataType& msg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deal with rest with 3 types
|
||||||
|
template < typename T1, typename T2, typename T3>
|
||||||
|
class MessageHandler<std::tuple<T1, T2, T3> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~MessageHandler() {}
|
||||||
|
virtual void Handle(const typename T1::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T2::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T3::DataType& msg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deal with rest with 2 types
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
class MessageHandler<std::tuple<T1, T2> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~MessageHandler() {}
|
||||||
|
virtual void Handle(const typename T1::DataType& msg) {}
|
||||||
|
virtual void Handle(const typename T2::DataType& msg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deal with rest with 1 type
|
||||||
|
template <typename T1>
|
||||||
|
class MessageHandler<std::tuple<T1> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~MessageHandler() {}
|
||||||
|
virtual void Handle(const typename T1::DataType& msg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deal with rest with 0 type
|
||||||
|
template <>
|
||||||
|
class MessageHandler<std::tuple<> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~MessageHandler() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // sp
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <boost/pfr.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sp/common/ByteSwapping.h>
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void WriteField(std::ostream& a_Os, const T& a_Data) {
|
|
||||||
T swapped = a_Data;
|
|
||||||
ToNetwork(swapped);
|
|
||||||
a_Os.write(reinterpret_cast<const char*>(&swapped), sizeof(a_Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void ReadField(std::istream& a_Is, T& a_Data) {
|
|
||||||
a_Is.read(reinterpret_cast<char*>(&a_Data), sizeof(a_Data));
|
|
||||||
FromNetwork(a_Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TData>
|
|
||||||
void WriteMessage(std::ostream& a_Os, const TData& a_MessageData) {
|
|
||||||
boost::pfr::for_each_field(a_MessageData, [&a_Os](const auto& a_Field) {
|
|
||||||
WriteField(a_Os, a_Field);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TData>
|
|
||||||
void ReadMessage(std::istream& a_Is, TData& a_MessageData) {
|
|
||||||
boost::pfr::for_each_field(a_MessageData, [&a_Is](auto& a_Field) {
|
|
||||||
ReadField(a_Is, a_Field);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace sp
|
|
||||||
@@ -29,24 +29,29 @@ DataBuffer& operator<<(DataBuffer& out, const VarInt& var) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DataBuffer& operator>>(DataBuffer& in, VarInt& var) {
|
DataBuffer& operator>>(DataBuffer& in, VarInt& var) {
|
||||||
var.m_Value = 0;
|
var.Read([&in](std::uint8_t& data){
|
||||||
|
in.ReadSome(&data, 1);
|
||||||
|
});
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VarInt::Read(const ReadFunc& read) {
|
||||||
|
m_Value = 0;
|
||||||
unsigned int position = 0;
|
unsigned int position = 0;
|
||||||
std::uint8_t currentByte;
|
std::uint8_t currentByte;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
in.ReadSome(¤tByte, 1);
|
read(currentByte);
|
||||||
var.m_Value |= static_cast<std::uint64_t>(currentByte & SEGMENT_BITS) << position;
|
m_Value |= static_cast<std::uint64_t>(currentByte & SEGMENT_BITS) << position;
|
||||||
|
|
||||||
if ((currentByte & CONTINUE_BIT) == 0)
|
if ((currentByte & CONTINUE_BIT) == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
position += 7;
|
position += 7;
|
||||||
|
|
||||||
if (position >= 8 * sizeof(var.m_Value))
|
if (position >= 8 * sizeof(m_Value))
|
||||||
throw std::runtime_error("VarInt is too big");
|
throw std::runtime_error("VarInt is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
return in;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sp
|
} // namespace sp
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
#include <sp/extensions/Compress.h>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <sp/common/VarInt.h>
|
|
||||||
#include <zlib.h>
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
namespace zlib {
|
|
||||||
|
|
||||||
static DataBuffer Inflate(const std::uint8_t* source, std::size_t size, std::size_t uncompressedSize) {
|
|
||||||
DataBuffer result;
|
|
||||||
result.Resize(uncompressedSize);
|
|
||||||
|
|
||||||
uncompress(static_cast<Bytef*>(result.data()), reinterpret_cast<uLongf*>(&uncompressedSize), static_cast<const Bytef*>(source),
|
|
||||||
static_cast<uLong>(size));
|
|
||||||
|
|
||||||
assert(result.GetSize() == uncompressedSize);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DataBuffer Deflate(const std::uint8_t* source, std::size_t size) {
|
|
||||||
DataBuffer result;
|
|
||||||
uLongf compressedSize = size;
|
|
||||||
|
|
||||||
result.Resize(size); // Resize for the compressed data to fit into
|
|
||||||
compress(static_cast<Bytef*>(result.data()), &compressedSize, static_cast<const Bytef*>(source), static_cast<uLong>(size));
|
|
||||||
result.Resize(compressedSize); // Resize to cut useless data
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataBuffer Compress(const DataBuffer& buffer, std::size_t a_CompressionThreshold) {
|
|
||||||
DataBuffer packet;
|
|
||||||
|
|
||||||
if (buffer.GetSize() < a_CompressionThreshold) {
|
|
||||||
// Don't compress since it's a small packet
|
|
||||||
packet << VarInt{0};
|
|
||||||
packet << buffer;
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataBuffer compressedData = Deflate(buffer.data(), buffer.GetSize());
|
|
||||||
VarInt uncompressedDataLength = buffer.GetSize();
|
|
||||||
|
|
||||||
if (compressedData.GetSize() >= buffer.GetSize()) {
|
|
||||||
// the compression is overkill so we don't send the compressed buffer
|
|
||||||
packet << VarInt{0};
|
|
||||||
packet << buffer;
|
|
||||||
} else {
|
|
||||||
packet << uncompressedDataLength;
|
|
||||||
packet << compressedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength) {
|
|
||||||
VarInt uncompressedLength;
|
|
||||||
buffer >> uncompressedLength;
|
|
||||||
|
|
||||||
std::uint64_t compressedLength = packetLength - uncompressedLength.GetSerializedLength();
|
|
||||||
|
|
||||||
if (uncompressedLength.GetValue() == 0) {
|
|
||||||
// Data already uncompressed. Nothing to do
|
|
||||||
DataBuffer ret;
|
|
||||||
buffer.ReadSome(ret, compressedLength);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(buffer.GetReadOffset() + compressedLength <= buffer.GetSize());
|
|
||||||
|
|
||||||
return Inflate(buffer.data() + buffer.GetReadOffset(), compressedLength, uncompressedLength.GetValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace zlib
|
|
||||||
|
|
||||||
namespace io {
|
|
||||||
|
|
||||||
DataBuffer MessageEncapsulator<option::ZlibCompress>::Encapsulate(const DataBuffer& a_Data, const option::ZlibCompress& a_Option) {
|
|
||||||
static constexpr std::size_t MAX_COMPRESS_THRESHOLD = VarInt::MAX_VALUE;
|
|
||||||
return zlib::Compress(a_Data, a_Option.m_Enabled ? a_Option.m_CompressionThreshold : MAX_COMPRESS_THRESHOLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
DataBuffer MessageEncapsulator<option::ZlibCompress>::Decapsulate(DataBuffer& a_Data, const option::ZlibCompress& a_Option) {
|
|
||||||
return zlib::Decompress(a_Data, a_Data.GetSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace io
|
|
||||||
} // namespace sp
|
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
#include <sp/extensions/tcp/TcpListener.h>
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
// Windows
|
|
||||||
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
|
|
||||||
#define ioctl ioctlsocket
|
|
||||||
#define WOULDBLOCK WSAEWOULDBLOCK
|
|
||||||
#define MSG_DONTWAIT 0
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// Linux/Unix
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define closesocket close
|
|
||||||
#define WOULDBLOCK EWOULDBLOCK
|
|
||||||
#define SD_BOTH SHUT_RDWR
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef INVALID_SOCKET
|
|
||||||
#define INVALID_SOCKET -1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
namespace io {
|
|
||||||
|
|
||||||
TcpListener::TcpListener(std::uint16_t a_Port, int a_MaxConnexions) {
|
|
||||||
if ((m_Handle = static_cast<SocketHandle>(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) < 0) {
|
|
||||||
throw SocketError("Failed to create server socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in address;
|
|
||||||
address.sin_family = AF_INET;
|
|
||||||
address.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
address.sin_port = htons(a_Port);
|
|
||||||
|
|
||||||
if (bind(m_Handle, reinterpret_cast<sockaddr*>(&address), sizeof(address)) < 0)
|
|
||||||
throw SocketError("Failed to create server socket");
|
|
||||||
|
|
||||||
if (listen(m_Handle, a_MaxConnexions) < 0)
|
|
||||||
throw SocketError("Failed to create server socket");
|
|
||||||
|
|
||||||
socklen_t len = sizeof(address);
|
|
||||||
if (getsockname(m_Handle, reinterpret_cast<sockaddr*>(&address), &len) < 0)
|
|
||||||
throw SocketError("Failed to create server socket");
|
|
||||||
|
|
||||||
m_Port = ntohs(address.sin_port);
|
|
||||||
m_MaxConnections = a_MaxConnexions;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TcpListener::~TcpListener() {
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<TcpSocket> TcpListener::Accept() {
|
|
||||||
sockaddr remoteAddress;
|
|
||||||
int addrlen = sizeof(remoteAddress);
|
|
||||||
|
|
||||||
auto newSocket = std::make_unique<TcpSocket>();
|
|
||||||
|
|
||||||
newSocket->m_Handle = static_cast<SocketHandle>(
|
|
||||||
accept(m_Handle, reinterpret_cast<sockaddr*>(&remoteAddress), reinterpret_cast<socklen_t*>(&addrlen)));
|
|
||||||
|
|
||||||
if (newSocket->m_Handle < 0)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
newSocket->m_Status = TcpSocket::Status::Connected;
|
|
||||||
return newSocket;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TcpListener::Close() {
|
|
||||||
if (m_Handle > 0) {
|
|
||||||
closesocket(m_Handle);
|
|
||||||
shutdown(m_Handle, SD_BOTH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TcpListener::SetBlocking(bool a_Blocking) {
|
|
||||||
unsigned long mode = !a_Blocking;
|
|
||||||
|
|
||||||
if (ioctl(m_Handle, FIONBIO, &mode) < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint16_t TcpListener::GetListeningPort() const {
|
|
||||||
return m_Port;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TcpListener::GetMaximumConnections() const {
|
|
||||||
return m_MaxConnections;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace io
|
|
||||||
} // namespace sp
|
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
#include <sp/extensions/tcp/TcpSocket.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
// Windows
|
|
||||||
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
|
|
||||||
#define ioctl ioctlsocket
|
|
||||||
#define WOULDBLOCK WSAEWOULDBLOCK
|
|
||||||
#define MSG_DONTWAIT 0
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// Linux/Unix
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define closesocket close
|
|
||||||
#define WOULDBLOCK EWOULDBLOCK
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef INVALID_SOCKET
|
|
||||||
#define INVALID_SOCKET -1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
namespace io {
|
|
||||||
|
|
||||||
TcpSocket::IOInterface() : m_Handle(static_cast<SocketHandle>(INVALID_SOCKET)), m_Status(Status::Disconnected) {}
|
|
||||||
|
|
||||||
TcpSocket::IOInterface(const std::string& a_Host, std::uint16_t a_Port) : IOInterface() {
|
|
||||||
Connect(a_Host, a_Port);
|
|
||||||
}
|
|
||||||
|
|
||||||
TcpSocket::IOInterface(IOInterface&& a_Other) {
|
|
||||||
std::swap(m_Handle, a_Other.m_Handle);
|
|
||||||
std::swap(m_Status, a_Other.m_Status);
|
|
||||||
}
|
|
||||||
|
|
||||||
TcpSocket::~IOInterface() {}
|
|
||||||
|
|
||||||
void TcpSocket::Connect(const std::string& a_Host, std::uint16_t a_Port) {
|
|
||||||
struct addrinfo hints {};
|
|
||||||
|
|
||||||
struct addrinfo* result = nullptr;
|
|
||||||
|
|
||||||
hints.ai_family = AF_INET;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
hints.ai_protocol = IPPROTO_TCP;
|
|
||||||
|
|
||||||
m_Status = Status::Error;
|
|
||||||
|
|
||||||
if (getaddrinfo(a_Host.c_str(), std::to_string(static_cast<int>(a_Port)).c_str(), &hints, &result) != 0) {
|
|
||||||
throw SocketError("Failed to get address info");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Handle = static_cast<SocketHandle>(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
|
|
||||||
if (m_Handle < 0) {
|
|
||||||
throw SocketError("Failed to create socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct addrinfo* ptr = nullptr;
|
|
||||||
for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
|
|
||||||
struct sockaddr* sockaddr = ptr->ai_addr;
|
|
||||||
if (connect(m_Handle, sockaddr, sizeof(sockaddr_in)) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
freeaddrinfo(result);
|
|
||||||
|
|
||||||
if (!ptr) {
|
|
||||||
throw SocketError("Could not find a suitable interface for connecting");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Status = Status::Connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataBuffer TcpSocket::Read(std::size_t a_Amount) {
|
|
||||||
DataBuffer buffer(a_Amount);
|
|
||||||
|
|
||||||
std::size_t totalRecieved = 0;
|
|
||||||
|
|
||||||
while (totalRecieved < a_Amount) {
|
|
||||||
int recvAmount =
|
|
||||||
recv(m_Handle, reinterpret_cast<char*>(buffer.data() + totalRecieved), static_cast<int>(a_Amount - totalRecieved), 0);
|
|
||||||
if (recvAmount <= 0) {
|
|
||||||
#if defined(_WIN32) || defined(WIN32)
|
|
||||||
int err = WSAGetLastError();
|
|
||||||
#else
|
|
||||||
int err = errno;
|
|
||||||
#endif
|
|
||||||
if (err == WOULDBLOCK) {
|
|
||||||
// we are in non blocking mode and nothing is available
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Disconnect();
|
|
||||||
m_Status = Status::Error;
|
|
||||||
throw SocketError("Error while reading");
|
|
||||||
}
|
|
||||||
totalRecieved += recvAmount;
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TcpSocket::Write(const sp::DataBuffer& a_Data) {
|
|
||||||
if (GetStatus() != Status::Connected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::size_t sent = 0;
|
|
||||||
|
|
||||||
while (sent < a_Data.GetSize()) {
|
|
||||||
int cur = send(m_Handle, reinterpret_cast<const char*>(a_Data.data() + sent), static_cast<int>(a_Data.GetSize() - sent), 0);
|
|
||||||
|
|
||||||
if (cur <= 0) {
|
|
||||||
Disconnect();
|
|
||||||
m_Status = Status::Error;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sent += static_cast<std::size_t>(cur);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TcpSocket::SetBlocking(bool a_Block) {
|
|
||||||
unsigned long mode = !a_Block;
|
|
||||||
|
|
||||||
if (ioctl(m_Handle, FIONBIO, &mode) < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
TcpSocket::Status TcpSocket::GetStatus() const {
|
|
||||||
return m_Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TcpSocket::Disconnect() {
|
|
||||||
if (m_Handle > 0)
|
|
||||||
closesocket(m_Handle);
|
|
||||||
m_Status = Status::Disconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
TcpSocket& TcpSocket::operator=(IOInterface&& a_Other) {
|
|
||||||
std::swap(m_Handle, a_Other.m_Handle);
|
|
||||||
std::swap(m_Status, a_Other.m_Status);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace io
|
|
||||||
} // namespace sp
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
#include <sp/io/File.h>
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
namespace io {
|
|
||||||
|
|
||||||
File::IOInterface(const std::string& a_FilePath, unsigned int a_OpenMode) {
|
|
||||||
if (a_OpenMode & FileTag::OpenMode::In)
|
|
||||||
m_FileInput = std::make_unique<std::ifstream>(a_FilePath, std::ios::binary);
|
|
||||||
if (a_OpenMode & FileTag::OpenMode::Out)
|
|
||||||
m_FileOutput = std::make_unique<std::ofstream>(a_FilePath, std::ios::binary);
|
|
||||||
}
|
|
||||||
|
|
||||||
File::IOInterface(File&& other) :
|
|
||||||
m_FileInput(std::move(other.m_FileInput)), m_FileOutput(std::move(other.m_FileOutput)) {}
|
|
||||||
|
|
||||||
DataBuffer File::Read(std::size_t a_Amount) {
|
|
||||||
DataBuffer buffer;
|
|
||||||
buffer.Resize(a_Amount);
|
|
||||||
assert(m_FileInput != nullptr);
|
|
||||||
m_FileInput->read(reinterpret_cast<char*>(buffer.data()), a_Amount);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void File::Write(const sp::DataBuffer& a_Data) {
|
|
||||||
assert(m_FileOutput != nullptr);
|
|
||||||
m_FileOutput->write(reinterpret_cast<const char*>(a_Data.data()), a_Data.GetSize());
|
|
||||||
m_FileOutput->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace io
|
|
||||||
} // namespace sp
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
#include <sp/io/Memory.h>
|
|
||||||
|
|
||||||
namespace sp {
|
|
||||||
namespace io {
|
|
||||||
|
|
||||||
sp::DataBuffer Memory::Read(std::size_t a_Amount) {
|
|
||||||
DataBuffer data;
|
|
||||||
m_VirtualIO.ReadSome(data, a_Amount > m_VirtualIO.GetRemaining() ? m_VirtualIO.GetRemaining() : a_Amount);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
void Memory::Write(const sp::DataBuffer& a_Data) {
|
|
||||||
m_VirtualIO << a_Data;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace io
|
|
||||||
} // namespace sp
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#include <sp/protocol/ConcreteMessage.h>
|
#include <sp/protocol/ConcreteMessage.h>
|
||||||
#include <sp/protocol/GenericHandler.h>
|
|
||||||
#include <sp/protocol/MessageDispatcher.h>
|
#include <sp/protocol/MessageDispatcher.h>
|
||||||
#include <sp/protocol/MessageFactory.h>
|
#include <sp/protocol/MessageFactory.h>
|
||||||
|
#include <sp/protocol/MessageHandler.h>
|
||||||
|
#include <sp/io/MessageStream.h>
|
||||||
|
#include <sp/io/StdIo.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -24,12 +26,12 @@ using KeepAliveMessage = Message<KeepAlivePacket, PacketID::KeepAlive>;
|
|||||||
|
|
||||||
using AllMessages = std::tuple<KeepAliveMessage>;
|
using AllMessages = std::tuple<KeepAliveMessage>;
|
||||||
|
|
||||||
class PacketHandler : public sp::GenericHandler<AllMessages> {};
|
class PacketHandler : public sp::MessageHandler<AllMessages> {};
|
||||||
|
|
||||||
class MyHandler : public PacketHandler {
|
class MyHandler : public PacketHandler {
|
||||||
public:
|
public:
|
||||||
virtual void Handle(const KeepAliveMessage& msg) {
|
virtual void Handle(const KeepAlivePacket& msg) {
|
||||||
std::cout << "yo !" << "\n";
|
std::cout << "I recieved a keep alive : " << msg.m_KeepAlive << "\n";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,8 +39,10 @@ using PacketDispatcher = sp::MessageDispatcher<PacketBase>;
|
|||||||
|
|
||||||
using PacketFactory = sp::MessageFactory<PacketBase, AllMessages>;
|
using PacketFactory = sp::MessageFactory<PacketBase, AllMessages>;
|
||||||
|
|
||||||
|
using PacketStream = sp::MessageStream<PacketFactory>;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
KeepAliveMessage m{5UL};
|
KeepAliveMessage m{69UL};
|
||||||
|
|
||||||
// dispatch tests
|
// dispatch tests
|
||||||
|
|
||||||
@@ -56,9 +60,23 @@ int main() {
|
|||||||
|
|
||||||
std::ofstream file {"test.bin"};
|
std::ofstream file {"test.bin"};
|
||||||
|
|
||||||
message->Write(file);
|
PacketStream p(std::make_shared<sp::StdOuput>(file));
|
||||||
|
|
||||||
|
p.WriteMessage(m);
|
||||||
|
|
||||||
|
file.flush();
|
||||||
|
|
||||||
|
std::ifstream file2 {"test.bin"};
|
||||||
|
|
||||||
|
PacketStream p2(std::make_shared<sp::StdInput>(file2));
|
||||||
|
|
||||||
|
auto message2 = p2.ReadMessage();
|
||||||
|
|
||||||
|
d.Dispatch(*message2);
|
||||||
|
|
||||||
|
// message->Write(file);
|
||||||
// file << std::endl;
|
// file << std::endl;
|
||||||
m.Write(file);
|
// m.Write(file);
|
||||||
// file << std::endl;
|
// file << std::endl;
|
||||||
// message->Read(file);
|
// message->Read(file);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user