From 0a99058c4d171c73c8285be62f6bd175099a1a20 Mon Sep 17 00:00:00 2001 From: Gawaboumga Date: Mon, 30 May 2016 14:22:31 +0200 Subject: [PATCH] Documentation for module: Network Former-commit-id: d36042138d0883eb118cc9a70f94f3522214dd46 --- include/Nazara/Network/AbstractSocket.inl | 31 ++++ include/Nazara/Network/Config.hpp | 15 +- include/Nazara/Network/ConfigCheck.hpp | 4 +- include/Nazara/Network/DebugOff.hpp | 2 +- include/Nazara/Network/IpAddress.inl | 164 ++++++++++++++++++++- include/Nazara/Network/NetPacket.inl | 93 ++++++++++++ include/Nazara/Network/RUdpConnection.inl | 87 ++++++++++- include/Nazara/Network/TcpClient.hpp | 2 +- include/Nazara/Network/TcpClient.inl | 35 +++++ include/Nazara/Network/TcpServer.inl | 31 ++++ include/Nazara/Network/UdpSocket.hpp | 1 - include/Nazara/Network/UdpSocket.inl | 53 ++++++- src/Nazara/Network/AbstractSocket.cpp | 68 +++++++++ src/Nazara/Network/AlgorithmNetwork.cpp | 42 +++++- src/Nazara/Network/IpAddress.cpp | 50 +++++++ src/Nazara/Network/NetPacket.cpp | 70 +++++++++ src/Nazara/Network/Network.cpp | 27 +++- src/Nazara/Network/RUdpConnection.cpp | 157 +++++++++++++++++++- src/Nazara/Network/TcpClient.cpp | 168 ++++++++++++++++++++++ src/Nazara/Network/TcpServer.cpp | 34 +++++ src/Nazara/Network/UdpSocket.cpp | 84 +++++++++++ tests/Engine/Network/IpAddress.cpp | 47 ++++++ tests/Engine/Network/RUdpConnection.cpp | 40 ++++++ tests/Engine/Network/TCP.cpp | 49 +++++++ tests/Engine/Network/UdpSocket.cpp | 39 +++++ 25 files changed, 1368 insertions(+), 25 deletions(-) create mode 100644 tests/Engine/Network/IpAddress.cpp create mode 100644 tests/Engine/Network/RUdpConnection.cpp create mode 100644 tests/Engine/Network/TCP.cpp create mode 100644 tests/Engine/Network/UdpSocket.cpp diff --git a/include/Nazara/Network/AbstractSocket.inl b/include/Nazara/Network/AbstractSocket.inl index 8bebabfdb..5b5174b98 100644 --- a/include/Nazara/Network/AbstractSocket.inl +++ b/include/Nazara/Network/AbstractSocket.inl @@ -6,31 +6,62 @@ namespace Nz { + /*! + * \brief Gets the last error + * \return Socket error + */ + inline SocketError AbstractSocket::GetLastError() const { return m_lastError; } + /*! + * \brief Gets the internal socket handle + * \return Socket handle + */ + inline SocketHandle AbstractSocket::GetNativeHandle() const { return m_handle; } + /*! + * \brief Gets the internal state + * \return Socket state + */ + inline SocketState AbstractSocket::GetState() const { return m_state; } + /*! + * \brief Gets the internal type + * \return Socket type + */ + inline SocketType AbstractSocket::GetType() const { return m_type; } + /*! + * \brief Checks whether the blocking is enabled + * \return true If successful + */ + inline bool AbstractSocket::IsBlockingEnabled() const { return m_isBlockingEnabled; } + /*! + * \brief Updates the state of the socket + * + * \param newState Next state for the socket + */ + inline void AbstractSocket::UpdateState(SocketState newState) { if (m_state != newState) diff --git a/include/Nazara/Network/Config.hpp b/include/Nazara/Network/Config.hpp index 727108061..2d73c26bb 100644 --- a/include/Nazara/Network/Config.hpp +++ b/include/Nazara/Network/Config.hpp @@ -27,17 +27,22 @@ #ifndef NAZARA_CONFIG_NETWORK_HPP #define NAZARA_CONFIG_NETWORK_HPP -/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci +/*! +* \defgroup network (NazaraNetwork) Network module +* Network/System module including classes to handle networking elements... +*/ -// Utilise le MemoryManager pour gérer les allocations dynamiques (détecte les leaks au prix d'allocations/libérations dynamiques plus lentes) +/// Each modification of a paramater of the module needs a recompilation of the unit + +// Use the MemoryManager to manage dynamic allocations (can detect memory leak but allocations/frees are slower) #define NAZARA_NETWORK_MANAGE_MEMORY 0 -// Active les tests de sécurité basés sur le code (Conseillé pour le développement) +// Activate the security tests based on the code (Advised for development) #define NAZARA_NETWORK_SAFE 1 -/// Chaque modification d'un paramètre ci-dessous implique une modification (souvent mineure) du code +/// Each modification of a parameter following implies a modification (often minor) of the code -/// Vérification des valeurs et types de certaines constantes +/// Checking the values and types of certain constants #include #if defined(NAZARA_STATIC) diff --git a/include/Nazara/Network/ConfigCheck.hpp b/include/Nazara/Network/ConfigCheck.hpp index 73513ebc9..2f1ba1dc8 100644 --- a/include/Nazara/Network/ConfigCheck.hpp +++ b/include/Nazara/Network/ConfigCheck.hpp @@ -7,11 +7,11 @@ #ifndef NAZARA_CONFIG_CHECK_NETWORK_HPP #define NAZARA_CONFIG_CHECK_NETWORK_HPP -/// Ce fichier sert à vérifier la valeur des constantes du fichier Config.hpp +/// This file is used to check the constant values defined in Config.hpp #include -// On force la valeur de MANAGE_MEMORY en mode debug +// We fore the value of MANAGE_MEMORY in debug #if defined(NAZARA_DEBUG) && !NAZARA_NETWORK_MANAGE_MEMORY #undef NAZARA_NETWORK_MANAGE_MEMORY #define NAZARA_NETWORK_MANAGE_MEMORY 0 diff --git a/include/Nazara/Network/DebugOff.hpp b/include/Nazara/Network/DebugOff.hpp index dd45ac45c..e9df99f1f 100644 --- a/include/Nazara/Network/DebugOff.hpp +++ b/include/Nazara/Network/DebugOff.hpp @@ -2,7 +2,7 @@ // This file is part of the "Nazara Engine - Network module" // For conditions of distribution and use, see copyright notice in Config.hpp -// On suppose que Debug.hpp a déjà été inclus, tout comme Config.hpp +// We suppose that Debug.hpp is already included, same goes for Config.hpp #if NAZARA_NETWORK_MANAGE_MEMORY #undef delete #undef new diff --git a/include/Nazara/Network/IpAddress.inl b/include/Nazara/Network/IpAddress.inl index 3184bd34d..2fc9ac2bd 100644 --- a/include/Nazara/Network/IpAddress.inl +++ b/include/Nazara/Network/IpAddress.inl @@ -9,11 +9,22 @@ namespace Nz { + /*! + * \brief Constructs a IpAddress object by default + */ + inline IpAddress::IpAddress() : m_isValid(false) { } + /*! + * \brief Constructs a IpAddress object with an IP and a port + * + * \param ip IPv4 address + * \param port Port of the IP + */ + inline IpAddress::IpAddress(const IPv4& ip, UInt16 port) : m_ipv4(ip), m_protocol(NetProtocol_IPv4), @@ -22,6 +33,13 @@ namespace Nz { } + /*! + * \brief Constructs a IpAddress object with an IP and a port + * + * \param ip IPv6 address + * \param port Port of the IP + */ + inline IpAddress::IpAddress(const IPv6& ip, UInt16 port) : m_ipv6(ip), m_protocol(NetProtocol_IPv6), @@ -30,46 +48,100 @@ namespace Nz { } + /*! + * \brief Constructs a IpAddress object with an IP and a port + * + * \param ip IPv4 address (a.b.c.d) + * \param port Port of the IP + */ + inline IpAddress::IpAddress(const UInt8& a, const UInt8& b, const UInt8& c, const UInt8& d, UInt16 port) : IpAddress(IPv4{a, b, c, d}, port) { } + /*! + * \brief Constructs a IpAddress object with an IP and a port + * + * \param ip IPv6 address (a.b.c.d.e.f.g.h) + * \param port Port of the IP + */ + inline IpAddress::IpAddress(const UInt16& a, const UInt16& b, const UInt16& c, const UInt16& d, const UInt16& e, const UInt16& f, const UInt16& g, const UInt16& h, UInt16 port) : IpAddress(IPv6{a, b, c, d, e, f, g, h}, port) { } + /*! + * Constructs a IpAddress object with a C-string + * + * \param address Hostname or textual IP address + */ + inline IpAddress::IpAddress(const char* address) { BuildFromAddress(address); } + /*! + * Constructs a IpAddress object with a string + * + * \param address Hostname or textual IP address + */ + inline IpAddress::IpAddress(const String& address) { BuildFromAddress(address.GetConstBuffer()); } + /*! + * \brief Gets the port + * \return Port attached to the IP address + */ + inline UInt16 IpAddress::GetPort() const { return m_port; } + /*! + * \brief Gets the net protocol + * \return Protocol attached to the IP address + */ + inline NetProtocol IpAddress::GetProtocol() const { return m_protocol; } + /*! + * \brief Checks whether the IP address is valid + * \return true If successful + */ + inline bool IpAddress::IsValid() const { return m_isValid; } + /*! + * \brief Sets the port + * + * \param port Port attached to the IP address + */ + inline void IpAddress::SetPort(UInt16 port) { m_port = port; } + /*! + * \brief Converts IpAddress to IPv4 + * \return Corresponding IPv4 + * + * \remark Produces a NazaraAssert if net protocol is not IPv4 + */ + inline IpAddress::IPv4 IpAddress::ToIPv4() const { NazaraAssert(m_isValid && m_protocol == NetProtocol_IPv4, "Address is not a valid IPv4"); @@ -77,6 +149,13 @@ namespace Nz return m_ipv4; } + /*! + * \brief Converts IpAddress to IPv6 + * \return Corresponding IPv6 + * + * \remark Produces a NazaraAssert if net protocol is not IPv6 + */ + inline IpAddress::IPv6 IpAddress::ToIPv6() const { NazaraAssert(m_isValid && m_protocol == NetProtocol_IPv6, "IP is not a valid IPv6"); @@ -84,6 +163,13 @@ namespace Nz return m_ipv6; } + /*! + * \brief Converts IpAddress to UInt32 + * \return Corresponding UInt32 + * + * \remark Produces a NazaraAssert if net protocol is not IPv4 + */ + inline UInt32 IpAddress::ToUInt32() const { NazaraAssert(m_isValid && m_protocol == NetProtocol_IPv4, "Address is not a valid IPv4"); @@ -94,17 +180,40 @@ namespace Nz UInt32(m_ipv4[3]) << 0; } + /*! + * \brief Converts IpAddress to boolean + * \return true If IpAddress is valid + * + * \see IsValid + */ + inline IpAddress::operator bool() const { return IsValid(); } + /*! + * \brief Output operator + * \return The stream + * + * \param out The stream + * \param address The address to output + */ + inline std::ostream& operator<<(std::ostream& out, const IpAddress& address) { out << "IpAddress(" << address.ToString() << ')'; return out; } + /*! + * \brief Compares the IpAddress to other one + * \return true if the ip addresses are the same + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator==(const IpAddress& first, const IpAddress& second) { // We need to check the validity of each address before comparing them @@ -146,11 +255,27 @@ namespace Nz return true; } + /*! + * \brief Compares the IpAddress to other one + * \return false if the ip addresses are the same + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator!=(const IpAddress& first, const IpAddress& second) { return !operator==(first, second); } + /*! + * \brief Compares the IpAddress to other one + * \return true if this ip address is inferior to the other one + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator<(const IpAddress& first, const IpAddress& second) { // If the second address is invalid, there's no way we're lower than it @@ -196,16 +321,40 @@ namespace Nz return false; //< Same address } + /*! + * \brief Compares the IpAddress to other one + * \return true if this ip address is inferior or equal to the other one + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator<=(const IpAddress& first, const IpAddress& second) { return !operator<(second, first); } + /*! + * \brief Compares the IpAddress to other one + * \return true if this ip address is greather to the other one + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator>(const IpAddress& first, const IpAddress& second) { return second < first; } + /*! + * \brief Compares the IpAddress to other one + * \return true if this ip address is greather or equal to the other one + * + * \param first First ip address to compare + * \param second Second ip address to compare with + */ + inline bool operator>=(const IpAddress& first, const IpAddress& second) { return !operator<(first, second); @@ -217,6 +366,13 @@ namespace std template<> struct hash { + /*! + * \brief Converts IpAddress to hash + * \return Hash of the IpAddress + * + * \param ip IpAddress to hash + */ + size_t operator()(const Nz::IpAddress& ip) const { if (!ip) @@ -224,7 +380,7 @@ namespace std // This is SDBM adapted for IP addresses, tested to generate the least collisions possible // (It doesn't mean it cannot be improved though) - std::size_t h = 0; + std::size_t hash = 0; switch (ip.GetProtocol()) { case Nz::NetProtocol_Any: @@ -233,20 +389,20 @@ namespace std case Nz::NetProtocol_IPv4: { - h = ip.ToUInt32() + (h << 6) + (h << 16) - h; + hash = ip.ToUInt32() + (hash << 6) + (hash << 16) - hash; break; } case Nz::NetProtocol_IPv6: { Nz::IpAddress::IPv6 v6 = ip.ToIPv6(); for (std::size_t i = 0; i < v6.size(); i++) - h = v6[i] + (h << 6) + (h << 16) - h; + hash = v6[i] + (hash << 6) + (hash << 16) - hash; break; } } - return ip.GetPort() + (h << 6) + (h << 16) - h; + return ip.GetPort() + (hash << 6) + (hash << 16) - hash; } }; } diff --git a/include/Nazara/Network/NetPacket.inl b/include/Nazara/Network/NetPacket.inl index c9bbed681..e9765c9dc 100644 --- a/include/Nazara/Network/NetPacket.inl +++ b/include/Nazara/Network/NetPacket.inl @@ -9,21 +9,46 @@ namespace Nz { + /*! + * \brief Constructs a NetPacket object by default + */ + inline NetPacket::NetPacket() : m_netCode(NetCode_Invalid) { } + /*! + * \brief Constructs a NetPacket object with a packet number and a minimal capacity + * + * \param netCode Packet number + * \param minCapacity Minimal capacity of the packet + */ + inline NetPacket::NetPacket(UInt16 netCode, std::size_t minCapacity) { Reset(netCode, minCapacity); } + /*! + * \brief Constructs a NetPacket object with a packet number and raw memory + * + * \param netCode Packet number + * \param ptr Raw memory + * \param size Size of the memory + */ + inline NetPacket::NetPacket(UInt16 netCode, const void* ptr, std::size_t size) { Reset(netCode, ptr, size); } + /*! + * \brief Constructs a NetPacket object with another one by move semantic + * + * \param packet NetPacket to move into this + */ + inline NetPacket::NetPacket(NetPacket&& packet) : ByteStream(std::move(packet)), m_buffer(std::move(packet.m_buffer)), @@ -35,12 +60,23 @@ namespace Nz SetStream(&m_memoryStream); } + /*! + * \brief Destructs the object + */ + inline NetPacket::~NetPacket() { FlushBits(); //< Needs to be done here as the stream will be freed before ByteStream calls it FreeStream(); } + /*! + * \brief Gets the raw buffer + * \return Constant raw buffer + * + * \remark Produces a NazaraAssert if internal buffer is invalid + */ + inline const UInt8* NetPacket::GetConstData() const { NazaraAssert(m_buffer, "Invalid buffer"); @@ -48,6 +84,13 @@ namespace Nz return m_buffer->GetConstBuffer(); } + /*! + * \brief Gets the raw buffer + * \return Raw buffer + * + * \remark Produces a NazaraAssert if internal buffer is invalid + */ + inline UInt8* NetPacket::GetData() const { NazaraAssert(m_buffer, "Invalid buffer"); @@ -55,6 +98,11 @@ namespace Nz return m_buffer->GetBuffer(); } + /*! + * \brief Gets the size of the data + * \return Size of the data + */ + inline size_t NetPacket::GetDataSize() const { if (m_buffer) @@ -63,22 +111,46 @@ namespace Nz return 0; } + /*! + * \brief Gets the packet number + * \return Packet number + */ + inline UInt16 NetPacket::GetNetCode() const { return m_netCode; } + /*! + * \brief Resets the packet + */ + inline void NetPacket::Reset() { FreeStream(); } + /*! + * \brief Resets the packet with a packet number and a minimal capacity + * + * \param netCode Packet number + * \param minCapacity Minimal capacity of the packet + */ + inline void NetPacket::Reset(UInt16 netCode, std::size_t minCapacity) { InitStream(HeaderSize + minCapacity, HeaderSize, OpenMode_ReadWrite); m_netCode = netCode; } + /*! + * \brief Resets the packet with a packet number and raw memory + * + * \param netCode Packet number + * \param ptr Raw memory + * \param size Size of the memory + */ + inline void NetPacket::Reset(UInt16 netCode, const void* ptr, std::size_t size) { InitStream(HeaderSize + size, HeaderSize, OpenMode_ReadOnly); @@ -88,6 +160,14 @@ namespace Nz m_netCode = netCode; } + /*! + * \brief Resizes the packet + * + * \param newSize Size for the resizing operation + * + * \remark Produces a NazaraAssert if internal buffer is invalid + */ + inline void NetPacket::Resize(std::size_t newSize) { NazaraAssert(m_buffer, "Invalid buffer"); @@ -95,11 +175,24 @@ namespace Nz m_buffer->Resize(newSize); } + /*! + * \brief Sets the packet number + * + * \param netCode Packet number + */ + inline void NetPacket::SetNetCode(UInt16 netCode) { m_netCode = netCode; } + /*! + * \brief Moves the NetPacket into this + * \return A reference to this + * + * \param packet NetPacket to move in this + */ + inline NetPacket& Nz::NetPacket::operator=(NetPacket&& packet) { FreeStream(); diff --git a/include/Nazara/Network/RUdpConnection.inl b/include/Nazara/Network/RUdpConnection.inl index b2372285d..53e797b59 100644 --- a/include/Nazara/Network/RUdpConnection.inl +++ b/include/Nazara/Network/RUdpConnection.inl @@ -8,31 +8,66 @@ namespace Nz { + /*! + * \brief Closes the connection + */ + inline void RUdpConnection::Close() { m_socket.Close(); } + /*! + * \brief Disconnects the connection + * + * \see Close + */ + inline void RUdpConnection::Disconnect() { Close(); } + /*! + * \brief Gets the bound address + * \return IpAddress we are linked to + */ + inline IpAddress RUdpConnection::GetBoundAddress() const { return m_socket.GetBoundAddress(); } + /*! + * \brief Gets the port of the bound address + * \return Port we are linked to + */ + inline UInt16 RUdpConnection::GetBoundPort() const { return m_socket.GetBoundPort(); } + /*! + * \brief Gets the last error + * \return Socket error + */ + inline SocketError RUdpConnection::GetLastError() const { return m_lastError; } + /*! + * \brief Listens to a socket + * \return true If successfully bound + * + * \param protocol Net protocol to listen to + * \param port Port to listen to + * + * \remark Produces a NazaraAssert if protocol is unknown or any + */ + inline bool RUdpConnection::Listen(NetProtocol protocol, UInt16 port) { NazaraAssert(protocol != NetProtocol_Any, "Any protocol not supported for Listen"); //< TODO @@ -59,16 +94,36 @@ namespace Nz return Listen(any); } + /*! + * \brief Sets the protocol id + * + * \param protocolId Protocol ID like NNet + */ + inline void RUdpConnection::SetProtocolId(UInt32 protocolId) { m_protocol = protocolId; } + /*! + * \brief Sets the time before ack + * + * \param Time before acking to send many together (in ms) + */ + inline void RUdpConnection::SetTimeBeforeAck(UInt32 ms) { m_forceAckSendTime = ms * 1000; //< Store in microseconds for easier handling } - + + /*! + * \brief Computes the difference of sequence + * \return Delta between the two sequences + * + * \param sequence First sequence + * \param sequence2 Second sequence + */ + inline unsigned int RUdpConnection::ComputeSequenceDifference(SequenceIndex sequence, SequenceIndex sequence2) { unsigned int difference; @@ -80,6 +135,13 @@ namespace Nz return 0; } + /*! + * \brief Checks whether the peer has pending packets + * \return true If it is the case + * + * \param peer Data relative to the peer + */ + inline bool RUdpConnection::HasPendingPackets(PeerData& peer) { for (unsigned int priority = PacketPriority_Highest; priority <= PacketPriority_Lowest; ++priority) @@ -94,6 +156,14 @@ namespace Nz return false; } + /*! + * \brief Checks whether the ack is more recent + * \return true If it is the case + * + * \param ack First sequence + * \param ack2 Second sequence + */ + inline bool RUdpConnection::IsAckMoreRecent(SequenceIndex ack, SequenceIndex ack2) { constexpr SequenceIndex maxDifference = std::numeric_limits::max() / 2; @@ -106,6 +176,13 @@ namespace Nz return false; ///< Same ack } + /*! + * \brief Checks whether the connection is reliable + * \return true If it is the case + * + * \remark Produces a NazaraError if enumeration is invalid + */ + inline bool RUdpConnection::IsReliable(PacketReliability reliability) { switch (reliability) @@ -122,6 +199,14 @@ namespace Nz return false; } + /*! + * \brief Simulates the loss of packets on network + * + * \param packetLoss Ratio of packet loss according to bernoulli distribution + * + * \remark Produces a NazaraAssert if packetLoss is not in between 0.0 and 1.0 + */ + inline void RUdpConnection::SimulateNetwork(double packetLoss) { NazaraAssert(packetLoss >= 0.0 && packetLoss <= 1.0, "Packet loss must be in range [0..1]"); diff --git a/include/Nazara/Network/TcpClient.hpp b/include/Nazara/Network/TcpClient.hpp index 0fcedcf39..fda4671f9 100644 --- a/include/Nazara/Network/TcpClient.hpp +++ b/include/Nazara/Network/TcpClient.hpp @@ -79,8 +79,8 @@ namespace Nz PendingPacket m_pendingPacket; UInt64 m_keepAliveInterval; UInt64 m_keepAliveTime; - bool m_isKeepAliveEnabled; bool m_isLowDelayEnabled; + bool m_isKeepAliveEnabled; }; } diff --git a/include/Nazara/Network/TcpClient.inl b/include/Nazara/Network/TcpClient.inl index da292b982..23f5412da 100644 --- a/include/Nazara/Network/TcpClient.inl +++ b/include/Nazara/Network/TcpClient.inl @@ -7,6 +7,10 @@ namespace Nz { + /*! + * \brief Constructs a TcpClient object by default + */ + inline TcpClient::TcpClient() : AbstractSocket(SocketType_TCP), Stream(StreamOption_Sequential), @@ -17,31 +21,62 @@ namespace Nz { } + /*! + * \brief Disconnects the connection + * + * \see Close + */ + inline void TcpClient::Disconnect() { Close(); } + /*! + * \brief Gets the interval between two keep alive pings + * \return Interval in milliseconds between two pings + */ + inline UInt64 TcpClient::GetKeepAliveInterval() const { return m_keepAliveInterval; } + /*! + * \brief Gets the time before expiration of connection + * \return Time in milliseconds before expiration + */ + inline UInt64 TcpClient::GetKeepAliveTime() const { return m_keepAliveTime; } + /*! + * \brief Gets the remote address + * \return Address of peer + */ + inline IpAddress TcpClient::GetRemoteAddress() const { return m_peerAddress; } + /*! + * \brief Checks whether low delay is enabled + * \return true If it is the case + */ + inline bool TcpClient::IsLowDelayEnabled() const { return m_isLowDelayEnabled; } + /*! + * \brief Checks whether the keep alive flag is enabled + * \return true If it is the case + */ + inline bool TcpClient::IsKeepAliveEnabled() const { return m_isKeepAliveEnabled; diff --git a/include/Nazara/Network/TcpServer.inl b/include/Nazara/Network/TcpServer.inl index 5350a58ee..1fcb4eb32 100644 --- a/include/Nazara/Network/TcpServer.inl +++ b/include/Nazara/Network/TcpServer.inl @@ -7,27 +7,58 @@ namespace Nz { + /*! + * \brief Constructs a TcpServer object by default + */ + inline TcpServer::TcpServer() : AbstractSocket(SocketType_TCP) { } + /*! + * \brief Constructs a TcpServer object with another one by move semantic + * + * \param tcpServer TcpServer to move into this + */ + inline TcpServer::TcpServer(TcpServer&& tcpServer) : AbstractSocket(std::move(tcpServer)), m_boundAddress(std::move(tcpServer.m_boundAddress)) { } + /*! + * \brief Gets the bound address + * \return IpAddress we are linked to + */ + inline IpAddress TcpServer::GetBoundAddress() const { return m_boundAddress; } + /*! + * \brief Gets the port of the bound address + * \return Port we are linked to + */ + inline UInt16 TcpServer::GetBoundPort() const { return m_boundAddress.GetPort(); } + /*! + * \brief Listens to a socket + * \return State of the socket + * + * \param protocol Net protocol to listen to + * \param port Port to listen to + * \param queueSize Size of the queue + * + * \remark Produces a NazaraAssert if protocol is unknown or any + */ + inline SocketState TcpServer::Listen(NetProtocol protocol, UInt16 port, unsigned int queueSize) { NazaraAssert(protocol != NetProtocol_Any, "Any protocol not supported for Listen"); //< TODO diff --git a/include/Nazara/Network/UdpSocket.hpp b/include/Nazara/Network/UdpSocket.hpp index 96c391436..2aac124af 100644 --- a/include/Nazara/Network/UdpSocket.hpp +++ b/include/Nazara/Network/UdpSocket.hpp @@ -49,7 +49,6 @@ namespace Nz void OnOpened() override; IpAddress m_boundAddress; - SocketState m_state; bool m_isBroadCastingEnabled; }; } diff --git a/include/Nazara/Network/UdpSocket.inl b/include/Nazara/Network/UdpSocket.inl index 9ad4d067b..4096d8c04 100644 --- a/include/Nazara/Network/UdpSocket.inl +++ b/include/Nazara/Network/UdpSocket.inl @@ -6,24 +6,46 @@ namespace Nz { + /*! + * \brief Constructs a UdpSocket object by default + */ + inline UdpSocket::UdpSocket() : AbstractSocket(SocketType_UDP) { } + /*! + * \brief Constructs a UdpSocket object with a net protocol + * + * \param protocol Net protocol to use + */ + inline UdpSocket::UdpSocket(NetProtocol protocol) : UdpSocket() { Create(protocol); } + /*! + * \brief Constructs a UdpSocket object with another one by move semantic + * + * \param udpSocket UdpSocket to move into this + */ + inline UdpSocket::UdpSocket(UdpSocket&& udpSocket) : AbstractSocket(std::move(udpSocket)), - m_boundAddress(std::move(udpSocket.m_boundAddress)), - m_state(udpSocket.m_state) + m_boundAddress(std::move(udpSocket.m_boundAddress)) { } + /*! + * \brief Binds a specific port + * \return State of the socket + * + * \param port Port to bind + */ + inline SocketState UdpSocket::Bind(UInt16 port) { IpAddress any; @@ -47,6 +69,13 @@ namespace Nz return Bind(any); } + /*! + * \brief Creates a UDP socket + * \return true If successful + * + * \param protocol Net protocol to use + */ + bool UdpSocket::Create(NetProtocol protocol) { NazaraAssert(protocol != NetProtocol_Unknown, "Invalid protocol"); @@ -54,21 +83,41 @@ namespace Nz return Open(protocol); } + /*! + * \brief Gets the bound address + * \return IpAddress we are linked to + */ + inline IpAddress UdpSocket::GetBoundAddress() const { return m_boundAddress; } + /*! + * \brief Gets the port of the bound address + * \return Port we are linked to + */ + inline UInt16 UdpSocket::GetBoundPort() const { return m_boundAddress.GetPort(); } + /*! + * \brief Gets the state of the socket + * \return State of the socket + */ + inline SocketState UdpSocket::GetState() const { return m_state; } + /*! + * \brief Checks whether the broadcasting is enabled + * \return true If it is the case + */ + inline bool UdpSocket::IsBroadcastingEnabled() const { return m_isBroadCastingEnabled; diff --git a/src/Nazara/Network/AbstractSocket.cpp b/src/Nazara/Network/AbstractSocket.cpp index e46a5906a..516364373 100644 --- a/src/Nazara/Network/AbstractSocket.cpp +++ b/src/Nazara/Network/AbstractSocket.cpp @@ -17,6 +17,18 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::AbstractSocket + * \brief Network class that represents the base of socket + * + * \remark This class is abstract + */ + + /*! + * \brief Constructs a AbstractSocket object with a type + */ + AbstractSocket::AbstractSocket(SocketType type) : m_lastError(SocketError_NoError), m_handle(SocketImpl::InvalidHandle), @@ -26,6 +38,12 @@ namespace Nz { } + /*! + * \brief Constructs a AbstractSocket object with another one by move semantic + * + * \param abstractSocket AbstractSocket to move into this + */ + AbstractSocket::AbstractSocket(AbstractSocket&& abstractSocket) : m_protocol(abstractSocket.m_protocol), m_lastError(abstractSocket.m_lastError), @@ -37,11 +55,21 @@ namespace Nz abstractSocket.m_handle = SocketImpl::InvalidHandle; } + /*! + * \brief Destructs the object and calls Close + * + * \see Close + */ + AbstractSocket::~AbstractSocket() { Close(); } + /*! + * \brief Closes the socket + */ + void AbstractSocket::Close() { if (m_handle != SocketImpl::InvalidHandle) @@ -53,6 +81,12 @@ namespace Nz } } + /*! + * \brief Enables blocking + * + * \param blocking Should the read block + */ + void AbstractSocket::EnableBlocking(bool blocking) { if (m_isBlockingEnabled != blocking) @@ -64,6 +98,11 @@ namespace Nz } } + /*! + * \brief Queries the available bytes + * \return Number of bytes which can be read + */ + std::size_t AbstractSocket::QueryAvailableBytes() const { if (m_handle == SocketImpl::InvalidHandle) @@ -72,11 +111,21 @@ namespace Nz return SocketImpl::QueryAvailableBytes(m_handle); } + /*! + * \brief Operation to do when closing socket + */ + void AbstractSocket::OnClose() { UpdateState(SocketState_NotConnected); } + /*! + * \brief Operation to do when opening socket + * + * \remark Produces a NazaraWarning if blocking failed + */ + void AbstractSocket::OnOpened() { SocketError errorCode; @@ -84,6 +133,11 @@ namespace Nz NazaraWarning("Failed to set socket blocking mode (0x" + String::Number(errorCode, 16) + ')'); } + /*! + * \brief Opens the socket according to a net protocol + * \return true If successful + */ + bool AbstractSocket::Open(NetProtocol protocol) { if (m_handle == SocketImpl::InvalidHandle || m_protocol != protocol) @@ -99,6 +153,13 @@ namespace Nz return true; } + /*! + * \brief Opens the socket according to a socket handle + * \return true If successful + * + * \remark Produces a NazaraAssert if handle is invalid + */ + void AbstractSocket::Open(SocketHandle handle) { NazaraAssert(handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -109,6 +170,13 @@ namespace Nz OnOpened(); } + /*! + * \brief Moves the AbstractSocket into this + * \return A reference to this + * + * \param abstractSocket AbstractSocket to move in this + */ + AbstractSocket& AbstractSocket::operator=(AbstractSocket&& abstractSocket) { Close(); diff --git a/src/Nazara/Network/AlgorithmNetwork.cpp b/src/Nazara/Network/AlgorithmNetwork.cpp index 273169005..307ac679a 100644 --- a/src/Nazara/Network/AlgorithmNetwork.cpp +++ b/src/Nazara/Network/AlgorithmNetwork.cpp @@ -11,6 +11,15 @@ namespace Nz { namespace Detail { + /*! + * \brief Parses a decimal number + * \return true If successful + * + * \param str C-string symbolizing the string to parse + * \param number Optional argument to return the number parsed + * \param endOfRead Optional argument to determine where parsing stopped + */ + bool ParseDecimal(const char* str, unsigned int* number, const char** endOfRead) { const char* ptr = str; @@ -35,6 +44,15 @@ namespace Nz return true; } + /*! + * \brief Parses a hexadecimal number + * \return true If successful + * + * \param str C-string symbolizing the string to parse + * \param number Optional argument to return the number parsed + * \param endOfRead Optional argument to determine where parsing stopped + */ + bool ParseHexadecimal(const char* str, unsigned int* number, const char** endOfRead) { const char* ptr = str; @@ -60,10 +78,26 @@ namespace Nz } } - // From http://rosettacode.org/wiki/Parse_an_IP_Address - // Parse a textual IPv4 or IPv6 address, optionally with port, into a binary - // array (for the address, in host order), and an optionally provided port. - // Also, indicate which of those forms (4 or 6) was parsed. + /*! + * \ingroup network + * \brief Parse a textual IPv4 or IPv6 address + * \return true If successful + * + * From http://rosettacode.org/wiki/Parse_an_IP_Address + * Parse a textual IPv4 or IPv6 address, optionally with port, into a binary + * array (for the address, in host order), and an optionally provided port. + * Also, indicate which of those forms (4 or 6) was parsed. + * + * \param addressPtr C-string which symbolizes the ip adress + * \param result Byte array to return the result in + * \param port Optional argument to resolve according to a specific port + * \param isIPv6 Optional argument to determine if the address is IPv6 + * \param endOfRead Optional argument to determine where parsing stopped + * + * \remark Produces a NazaraAssert if addressPtr is invalid + * \remark Produces a NazaraAssert if result is invalid + */ + bool ParseIPAddress(const char* addressPtr, UInt8 result[16], UInt16* port, bool* isIPv6, const char** endOfRead) { NazaraAssert(addressPtr, "Invalid address string"); diff --git a/src/Nazara/Network/IpAddress.cpp b/src/Nazara/Network/IpAddress.cpp index d40e2a581..29be756ea 100644 --- a/src/Nazara/Network/IpAddress.cpp +++ b/src/Nazara/Network/IpAddress.cpp @@ -22,6 +22,19 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::IpAddress + * \brief Network class that represents an IP address + */ + + /*! + * \brief Builds the IP from a hostname + * \return true If successful + * + * \remark address C-string symbolizing the IP address or hostname + */ + bool IpAddress::BuildFromAddress(const char* address) { m_isValid = false; @@ -50,6 +63,13 @@ namespace Nz return true; } + /*! + * \brief Checks whether the IP address is loopback + * \return true If it is the case + * + * \remark Produces a NazaraAssert if internal protocol is invalid (should never happen) + */ + bool IpAddress::IsLoopback() const { if (!m_isValid) @@ -73,6 +93,13 @@ namespace Nz return false; } + /*! + * \brief Gives a string representation + * \return A string representation of the object + * + * \remark Produces a NazaraAssert if internal protocol is invalid (should never happen) + */ + String IpAddress::ToString() const { StringStream stream; @@ -153,6 +180,17 @@ namespace Nz return stream; } + /*! + * \brief Resolves the address based on the IP + * \return Hostname of the address + * + * \param address IP address to resolve + * \param service Optional argument to specify the protocol used + * \param error Optional argument to get the error + * + * \remark Produces a NazaraAssert if address is invalid + */ + String IpAddress::ResolveAddress(const IpAddress& address, String* service, ResolveError* error) { NazaraAssert(address.IsValid(), "Invalid address"); @@ -163,6 +201,18 @@ namespace Nz return hostname; } + /*! + * \brief Resolves the address based on the hostname + * \return Informations about the host: IP(s) of the address, names, ... + * + * \param protocol Net protocol to use + * \param hostname Hostname to resolve + * \param service Specify the service used (http, ...) + * \param error Optional argument to get the error + * + * \remark Produces a NazaraAssert if net protocol is set to unknown + */ + std::vector IpAddress::ResolveHostname(NetProtocol protocol, const String& hostname, const String& service, ResolveError* error) { NazaraAssert(protocol != NetProtocol_Unknown, "Invalid protocol"); diff --git a/src/Nazara/Network/NetPacket.cpp b/src/Nazara/Network/NetPacket.cpp index 7a1c0d7f5..0823f5258 100644 --- a/src/Nazara/Network/NetPacket.cpp +++ b/src/Nazara/Network/NetPacket.cpp @@ -9,11 +9,36 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::NetPacket + * \brief Network class that represents a packet + */ + + /*! + * \brief Operation to do when receiving data + * + * \param netCode Packet number + * \param data Raw memory + * \param size Size of the memory + */ + void NetPacket::OnReceive(UInt16 netCode, const void* data, std::size_t size) { Reset(netCode, data, size); } + /*! + * \brief Operation to do when sending data + * \return Beggining of the raw memory + * + * \param newSize Size of the memory to send + * + * \remark Produces a NazaraAssert if newSize is invalid + * \remark Produces a NazaraAssert if net code is invalid + * \remark Produces a NazaraError if header could not be encoded + */ + const void* NetPacket::OnSend(std::size_t* newSize) const { NazaraAssert(newSize, "Invalid size pointer"); @@ -30,6 +55,15 @@ namespace Nz return m_buffer->GetBuffer(); } + /*! + * \brief Decodes the header of the packet + * \return true If successful + * + * \param data Raw memory + * \param packetSize Size of the packet + * \param netCode Packet number + */ + bool NetPacket::DecodeHeader(const void* data, UInt16* packetSize, UInt16* netCode) { MemoryView stream(data, HeaderSize); @@ -40,6 +74,15 @@ namespace Nz return Unserialize(context, packetSize) && Unserialize(context, netCode); } + /*! + * \brief Encodes the header of the packet + * \return true If successful + * + * \param data Raw memory + * \param packetSize Size of the packet + * \param netCode Packet number + */ + bool NetPacket::EncodeHeader(void* data, UInt16 packetSize, UInt16 netCode) { MemoryView stream(data, HeaderSize); @@ -50,11 +93,19 @@ namespace Nz return Serialize(context, packetSize) && Serialize(context, netCode); } + /*! + * \brief Operation to do when stream is empty + */ + void NetPacket::OnEmptyStream() { Reset(0); } + /*! + * \brief Frees the stream + */ + void NetPacket::FreeStream() { if (!m_buffer) @@ -66,6 +117,16 @@ namespace Nz s_availableBuffers.emplace_back(std::make_pair(size, std::move(m_buffer))); } + /*! + * \brief Inits the internal stream + * + * \param minCapacity Minimal capacity of the stream + * \param cursorPos Position of the cursor in the stream + * \param openMode Flag of the stream + * + * \remark Produces a NazaraAssert if cursor position is greather than the capacity + */ + void NetPacket::InitStream(std::size_t minCapacity, UInt64 cursorPos, UInt32 openMode) { NazaraAssert(minCapacity >= cursorPos, "Cannot init stream with a smaller capacity than wanted cursor pos"); @@ -92,12 +153,21 @@ namespace Nz SetStream(&m_memoryStream); } + /*! + * \brief Initializes the NetPacket class + * \return true If initialization is successful + */ + bool NetPacket::Initialize() { s_availableBuffersMutex = std::make_unique(); return true; } + /*! + * \brief Uninitializes the NetPacket class + */ + void NetPacket::Uninitialize() { s_availableBuffers.clear(); diff --git a/src/Nazara/Network/Network.cpp b/src/Nazara/Network/Network.cpp index 41868b79b..fa3813dd4 100644 --- a/src/Nazara/Network/Network.cpp +++ b/src/Nazara/Network/Network.cpp @@ -24,9 +24,23 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::Network + * \brief Network class that represents the module initializer of Network + */ + + /*! + * \brief Initializes the Network module + * \return true if initialization is successful + * + * \remark Produces a NazaraNotice + * \remark Produces a NazaraError if one submodule failed + */ + bool Network::Initialize() { - if (s_moduleReferenceCounter > 0) + if (IsInitialized()) { s_moduleReferenceCounter++; return true; // Already initialized @@ -68,11 +82,22 @@ namespace Nz return true; } + /*! + * \brief Checks whether the module is initialized + * \return true if module is initialized + */ + bool Network::IsInitialized() { return s_moduleReferenceCounter != 0; } + /*! + * \brief Uninitializes the Core module + * + * \remark Produces a NazaraNotice + */ + void Network::Uninitialize() { if (s_moduleReferenceCounter != 1) diff --git a/src/Nazara/Network/RUdpConnection.cpp b/src/Nazara/Network/RUdpConnection.cpp index 451d08f1e..1b374d604 100644 --- a/src/Nazara/Network/RUdpConnection.cpp +++ b/src/Nazara/Network/RUdpConnection.cpp @@ -10,6 +10,16 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::RUdpConnection + * \brief Network class that represents a reliable UDP connection + */ + + /*! + * \brief Constructs a RUdpConnection object by default + */ + RUdpConnection::RUdpConnection() : m_peerIterator(0), m_forceAckSendTime(10'000), //< 10ms @@ -23,9 +33,20 @@ namespace Nz { } + /*! + * \brief Connects to the IpAddress + * \return true + * + * \param remoteAddress Address to connect to + * + * \remark Produces a NazaraAssert if socket is not bound + * \remark Produces a NazaraAssert if remote is invalid + * \remark Produces a NazaraAssert if port is not specified + */ + bool RUdpConnection::Connect(const IpAddress& remoteAddress) { - NazaraAssert(m_socket.GetState() != SocketState_Bound, "Socket must be bound first"); + NazaraAssert(m_socket.GetState() == SocketState_Bound, "Socket must be bound first"); NazaraAssert(remoteAddress.IsValid(), "Invalid remote address"); NazaraAssert(remoteAddress.GetPort() != 0, "Remote address has no port"); @@ -39,6 +60,16 @@ namespace Nz return true; } + /*! + * \brief Connects to the hostname + * \return true If successful + * + * \param hostName Hostname of the remote + * \param protocol Net protocol to use + * \param service Specify the protocol used + * \param error Optional argument to get the error + */ + bool RUdpConnection::Connect(const String& hostName, NetProtocol protocol, const String& service, ResolveError* error) { std::vector results = IpAddress::ResolveHostname(protocol, hostName, service, error); @@ -64,6 +95,13 @@ namespace Nz return Connect(hostnameAddress); } + /*! + * \brief Listens to a socket + * \return true If successfully bound + * + * \param remoteAddress Address to listen to + */ + bool RUdpConnection::Listen(const IpAddress& address) { if (!InitSocket(address.GetProtocol())) @@ -72,8 +110,19 @@ namespace Nz return m_socket.Bind(address) == SocketState_Bound; } + /*! + * \brief Polls the message + * \return true If there is a message + * + * \param message Message to poll + * + * \remark Produces a NazaraAssert if message is invalid + */ + bool RUdpConnection::PollMessage(RUdpMessage* message) { + NazaraAssert(message, "Invalid message"); + if (m_receivedMessages.empty()) return false; @@ -82,6 +131,16 @@ namespace Nz return true; } + /*! + * \brief Sends the packet to a peer + * \return true If peer exists (false may result from disconnected client) + * + * \param peerIp IpAddress of the peer + * \param priority Priority of the packet + * \param reliability Policy of reliability of the packet + * \param packet Packet to send + */ + bool RUdpConnection::Send(const IpAddress& peerIp, PacketPriority priority, PacketReliability reliability, const NetPacket& packet) { auto it = m_peerByIP.find(peerIp); @@ -92,6 +151,10 @@ namespace Nz return true; } + /*! + * \brief Updates the reliable connection + */ + void RUdpConnection::Update() { m_currentTime = m_clock.GetMicroseconds(); @@ -156,6 +219,14 @@ namespace Nz //m_activeClients.Reset(); } + /*! + * \brief Disconnects a peer + * + * \param peerIndex Index of the peer + * + * \remark Produces a NazaraNotice + */ + void RUdpConnection::DisconnectPeer(std::size_t peerIndex) { PeerData& peer = m_peers[peerIndex]; @@ -193,6 +264,15 @@ namespace Nz m_peers.pop_back(); } + /*! + * \brief Enqueues a packet in the sending list + * + * \param peer Data relative to the peer + * \param priority Priority of the packet + * \param reliability Policy of reliability of the packet + * \param packet Packet to send + */ + void RUdpConnection::EnqueuePacket(PeerData& peer, PacketPriority priority, PacketReliability reliability, const NetPacket& packet) { UInt16 protocolBegin = static_cast(m_protocol & 0xFFFF); @@ -208,6 +288,15 @@ namespace Nz EnqueuePacketInternal(peer, priority, reliability, std::move(data)); } + /*! + * \brief Enqueues internally a packet in the sending list + * + * \param peer Data relative to the peer + * \param priority Priority of the packet + * \param reliability Policy of reliability of the packet + * \param packet Packet to send + */ + void RUdpConnection::EnqueuePacketInternal(PeerData& peer, PacketPriority priority, PacketReliability reliability, NetPacket&& data) { PendingPacket pendingPacket; @@ -219,6 +308,13 @@ namespace Nz m_activeClients.UnboundedSet(peer.index); } + /*! + * \brief Inits the internal socket + * \return true If successful + * + * \param protocol Net protocol to use + */ + bool RUdpConnection::InitSocket(NetProtocol protocol) { CallOnExit updateLastError([this] @@ -233,6 +329,14 @@ namespace Nz return true; } + /*! + * \brief Processes the acks + * + * \param peer Data relative to the peer + * \param lastAck Last index of the ack + * \param ackBits Bits for acking + */ + void RUdpConnection::ProcessAcks(PeerData& peer, SequenceIndex lastAck, UInt32 ackBits) { auto it = peer.pendingAckQueue.begin(); @@ -257,6 +361,14 @@ namespace Nz } } + /*! + * \brief Registers a peer + * \return Data relative to the peer + * + * \param address Address of the peer + * \param state Status of the peer + */ + RUdpConnection::PeerData& RUdpConnection::RegisterPeer(const IpAddress& address, PeerState state) { PeerData data; @@ -266,7 +378,7 @@ namespace Nz data.index = m_peers.size(); data.lastPacketTime = m_currentTime; data.lastPingTime = m_currentTime; - data.roundTripTime = 1000000; ///< Okay that's quite a lot + data.roundTripTime = 1'000'000; ///< Okay that's quite a lot data.state = state; m_activeClients.UnboundedSet(data.index); @@ -276,6 +388,14 @@ namespace Nz return m_peers.back(); } + /*! + * \brief Operation to do when client requests a connection + * + * \param address Address of the peer + * \param sequenceId Sequence index for the ack + * \param token Token for connection + */ + void RUdpConnection::OnClientRequestingConnection(const IpAddress& address, SequenceIndex sequenceId, UInt64 token) { // Call hook to check if client should be accepted or not @@ -292,6 +412,13 @@ namespace Nz EnqueuePacket(client, PacketPriority_Immediate, PacketReliability_Reliable, connectionAcceptedPacket); } + /*! + * \brief Operation to do when a packet is lost + * + * \param peer Data relative to the peer + * \param packet Pending packet + */ + void RUdpConnection::OnPacketLost(PeerData& peer, PendingAckPacket&& packet) { //NazaraNotice(m_socket.GetBoundAddress().ToString() + ": Lost packet " + String::Number(packet.sequenceId)); @@ -300,6 +427,14 @@ namespace Nz EnqueuePacketInternal(peer, packet.priority, packet.reliability, std::move(packet.data)); } + /*! + * \brief Operation to do when receiving a packet + * + * \param peerIndex Index of the peer + * + * \remark Produces a NazaraNotice + */ + void RUdpConnection::OnPacketReceived(const IpAddress& peerIp, NetPacket&& packet) { UInt16 protocolBegin; @@ -436,6 +571,13 @@ namespace Nz } } + /*! + * \brief Sends a packet to a peer + * + * \param peer Data relative to the peer + * \param packet Pending packet + */ + void RUdpConnection::SendPacket(PeerData& peer, PendingPacket&& packet) { if (peer.state == PeerState_WillAck) @@ -448,7 +590,7 @@ namespace Nz { if (ack == remoteSequence) continue; - + unsigned int difference = ComputeSequenceDifference(remoteSequence, ack); if (difference <= 32U) previousAcks |= (1U << (difference - 1)); @@ -473,6 +615,11 @@ namespace Nz peer.pendingAckQueue.emplace_back(std::move(pendingAckPacket)); } + /*! + * \brief Initializes the RUdpConnection class + * \return true + */ + bool RUdpConnection::Initialize() { std::random_device device; @@ -481,6 +628,10 @@ namespace Nz return true; } + /*! + * \brief Uninitializes the RUdpConnection class + */ + void RUdpConnection::Uninitialize() { } diff --git a/src/Nazara/Network/TcpClient.cpp b/src/Nazara/Network/TcpClient.cpp index a134cf870..cf123ff89 100644 --- a/src/Nazara/Network/TcpClient.cpp +++ b/src/Nazara/Network/TcpClient.cpp @@ -20,6 +20,22 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::TcpClient + * \brief Network class that represents a client in a TCP connection + */ + + /*! + * \brief Connects to the IpAddress + * \return State of the socket + * + * \param remoteAddress Address to connect to + * + * \remark Produces a NazaraAssert if remote is invalid + * \remark Produces a NazaraAssert if remote's port is not specified + */ + SocketState TcpClient::Connect(const IpAddress& remoteAddress) { NazaraAssert(remoteAddress.IsValid(), "Invalid remote address"); @@ -45,6 +61,17 @@ namespace Nz return state; } + + /*! + * \brief Connects to the hostname + * \return State of the socket + * + * \param hostName Hostname of the remote + * \param protocol Net protocol to use + * \param service Specify the protocol used + * \param error Optional argument to get the error + */ + SocketState TcpClient::Connect(const String& hostName, NetProtocol protocol, const String& service, ResolveError* error) { UpdateState(SocketState_Resolving); @@ -73,6 +100,14 @@ namespace Nz return Connect(hostnameAddress); } + /*! + * \brief Enables low delay in emitting + * + * \param lowDelay Should low delay be used + * + * \remark This may produce lag + */ + void TcpClient::EnableLowDelay(bool lowDelay) { if (m_isLowDelayEnabled != lowDelay) @@ -84,6 +119,14 @@ namespace Nz } } + /*! + * \brief Enables the keep alive flag + * + * \param keepAlive Should the connection be kept alive + * \param msTime Time in milliseconds before expiration + * \param msInterval Interval in milliseconds between two pings + */ + void TcpClient::EnableKeepAlive(bool keepAlive, UInt64 msTime, UInt64 msInterval) { if (m_isKeepAliveEnabled != keepAlive || m_keepAliveTime != msTime || m_keepAliveInterval != msInterval) @@ -97,22 +140,51 @@ namespace Nz } } + /*! + * \brief Checks whether the stream reached the end of the stream + * \return true if there is no more available bytes + */ + bool TcpClient::EndOfStream() const { return QueryAvailableBytes() == 0; } + /*! + * \brief Gets the position of the cursor + * \return 0 + * + * \remark Produces a NazaraError because it is a special stream + */ + UInt64 TcpClient::GetCursorPos() const { NazaraError("GetCursorPos() cannot be used on sequential streams"); return 0; } + /*! + * \brief Gets the size of the raw memory available + * \return Size of the memory available + */ + UInt64 TcpClient::GetSize() const { return QueryAvailableBytes(); } + /*! + * \brief Receives the data available + * \return true If data received + * + * \param buffer Raw memory to write + * \param size Size of the buffer + * \param received Optional argument to get the number of bytes received + * + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if buffer and its size is invalid + */ + bool TcpClient::Receive(void* buffer, std::size_t size, std::size_t* received) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -142,6 +214,17 @@ namespace Nz return true; } + /*! + * \brief Receives the packet available + * \return true If packet received + * + * \param packet Packet to receive + * + * \remark Produces a NazaraAssert if packet is invalid + * \remark Produces a NazaraAssert if packet size is inferior to the header size + * \remark Produces a NazaraWarning if packet's header is invalid + */ + bool TcpClient::ReceivePacket(NetPacket* packet) { //TODO: Every packet requires at least two Receive call, using an internal buffer of a fixed size would prevent this @@ -157,6 +240,7 @@ namespace Nz m_pendingPacket.received += received; + //TODO: Should never happen in production ! NazaraAssert(m_pendingPacket.received <= NetPacket::HeaderSize, "Received more data than header size"); if (m_pendingPacket.received >= NetPacket::HeaderSize) { @@ -185,6 +269,7 @@ namespace Nz m_pendingPacket.received += received; + //TODO: Should never happen in production ! NazaraAssert(m_pendingPacket.received <= packetSize, "Received more data than packet size"); if (m_pendingPacket.received >= packetSize) { @@ -202,6 +287,19 @@ namespace Nz return false; } + /*! + * \brief Sends the data available + * \return true If data sended + * + * \param buffer Raw memory to read + * \param size Size of the buffer + * \param sent Optional argument to get the number of bytes sent + * + * \remark Large sending are handled, you do not need to call this multiple time + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if buffer and its size is invalid + */ + bool TcpClient::Send(const void* buffer, std::size_t size, std::size_t* sent) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -244,6 +342,15 @@ namespace Nz return true; } + /*! + * \brief Sends the packet available + * \return true If packet sent + * + * \param packet Packet to send + * + * \remark Produces a NazaraError if packet could not be prepared for sending + */ + bool TcpClient::SendPacket(const NetPacket& packet) { std::size_t size = 0; @@ -258,12 +365,30 @@ namespace Nz return Send(ptr, size, nullptr); } + /*! + * \brief Sets the position of the cursor + * \return false + * + * \param offset Offset according to the beginning of the stream + * + * \remark Produces a NazaraError because it is a special stream + */ + bool TcpClient::SetCursorPos(UInt64 offset) { NazaraError("SetCursorPos() cannot be used on sequential streams"); return false; } + /*! + * \brief Waits for being connected before time out + * \return true If connection is successful + * + * \param msTimeout Time in milliseconds before time out + * + * \remark Produces a NazaraAssert if socket is invalid + */ + bool TcpClient::WaitForConnected(UInt64 msTimeout) { switch (m_state) @@ -308,10 +433,18 @@ namespace Nz return false; } + /*! + * \brief Flushes the stream + */ + void TcpClient::FlushStream() { } + /*! + * \brief Operation to do when closing socket + */ + void TcpClient::OnClose() { AbstractSocket::OnClose(); @@ -320,6 +453,12 @@ namespace Nz m_peerAddress = IpAddress::Invalid; } + /*! + * \brief Operation to do when opening socket + * + * \remark Produces a NazaraWarning if delay mode or keep alive failed + */ + void TcpClient::OnOpened() { AbstractSocket::OnOpened(); @@ -336,6 +475,16 @@ namespace Nz m_openMode = OpenMode_ReadWrite; } + /*! + * \brief Reads blocks + * \return Number of blocks read + * + * \param buffer Preallocated buffer to contain information read + * \param size Size of the read and thus of the buffer + * + * \remark Produces a NazaraAssert if socket is invalid + */ + std::size_t TcpClient::ReadBlock(void* buffer, std::size_t size) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -357,6 +506,13 @@ namespace Nz return received; } + /*! + * \brief Resets the connection with a new socket and a peer address + * + * \param handle Socket to connect + * \param peerAddress Address to connect to + */ + void TcpClient::Reset(SocketHandle handle, const IpAddress& peerAddress) { Open(handle); @@ -365,8 +521,20 @@ namespace Nz UpdateState(SocketState_Connected); } + /*! + * \brief Writes blocks + * \return Number of blocks written + * + * \param buffer Preallocated buffer containing information to write + * \param size Size of the writting and thus of the buffer + * + * \remark Produces a NazaraAssert if buffer is nullptr + * \remark Produces a NazaraAssert if socket is invalid + */ + std::size_t TcpClient::WriteBlock(const void* buffer, std::size_t size) { + NazaraAssert(buffer, "Invalid buffer"); NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); CallOnExit restoreBlocking; diff --git a/src/Nazara/Network/TcpServer.cpp b/src/Nazara/Network/TcpServer.cpp index b5995b6f5..7a33c066b 100644 --- a/src/Nazara/Network/TcpServer.cpp +++ b/src/Nazara/Network/TcpServer.cpp @@ -19,6 +19,22 @@ namespace Nz { + /*! + * \ingroup network + * \class Nz::TcpServer + * \brief Network class that represents a server in a TCP connection + */ + + /*! + * \brief Accepts a client + * \return true If client'socket is valid + * + * \param newClient Client connection + * + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if newClient is invalid + */ + bool TcpServer::AcceptClient(TcpClient* newClient) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Server isn't listening"); @@ -35,6 +51,16 @@ namespace Nz return false; } + /*! + * \brief Listens to a socket + * \return State of the socket + * + * \param address Address to listen to + * \param queueSize Size of the queue + * + * \remark Produces a NazaraAssert if address is invalid + */ + SocketState TcpServer::Listen(const IpAddress& address, unsigned int queueSize) { NazaraAssert(address.IsValid(), "Invalid address"); @@ -49,6 +75,10 @@ namespace Nz return state; } + /*! + * \brief Operation to do when closing socket + */ + void TcpServer::OnClose() { AbstractSocket::OnClose(); @@ -56,6 +86,10 @@ namespace Nz m_boundAddress = IpAddress::Invalid; } + /*! + * \brief Operation to do when opening socket + */ + void TcpServer::OnOpened() { AbstractSocket::OnOpened(); diff --git a/src/Nazara/Network/UdpSocket.cpp b/src/Nazara/Network/UdpSocket.cpp index a233881ea..b49aedcf3 100644 --- a/src/Nazara/Network/UdpSocket.cpp +++ b/src/Nazara/Network/UdpSocket.cpp @@ -17,6 +17,16 @@ namespace Nz { + /*! + * \brief Binds a specific IpAddress + * \return State of the socket + * + * \param address Address to bind + * + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if address is invalid + */ + SocketState UdpSocket::Bind(const IpAddress& address) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created"); @@ -30,6 +40,14 @@ namespace Nz return state; } + /*! + * \brief Enables broadcasting + * + * \param broadcasting Should the UDP broadcast + * + * \remark Produces a NazaraAssert if socket is invalid + */ + void UdpSocket::EnableBroadcasting(bool broadcasting) { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); @@ -41,6 +59,13 @@ namespace Nz } } + /*! + * \brief Gets the maximum datagram size allowed + * \return Number of bytes + * + * \remark Produces a NazaraAssert if socket is invalid + */ + std::size_t UdpSocket::QueryMaxDatagramSize() { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created"); @@ -48,8 +73,22 @@ namespace Nz return SocketImpl::QueryMaxDatagramSize(m_handle, &m_lastError); } + /*! + * \brief Receives the data available + * \return true If data received + * + * \param buffer Raw memory to write + * \param size Size of the buffer + * \param from IpAddress of the peer + * \param received Optional argument to get the number of bytes received + * + * \remark Produces a NazaraAssert if socket is invalid + * \remark Produces a NazaraAssert if buffer and its size is invalid + */ + bool UdpSocket::Receive(void* buffer, std::size_t size, IpAddress* from, std::size_t* received) { + NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created"); NazaraAssert(buffer && size > 0, "Invalid buffer"); int read; @@ -62,8 +101,21 @@ namespace Nz return true; } + /*! + * \brief Receives the packet available + * \return true If packet received + * + * \param packet Packet to receive + * \param from IpAddress of the peer + * + * \remark Produces a NazaraAssert if packet is invalid + * \remark Produces a NazaraWarning if packet's header is invalid + */ + bool UdpSocket::ReceivePacket(NetPacket* packet, IpAddress* from) { + NazaraAssert(packet, "Invalid packet"); + // I'm not sure what's the best between having a 65k bytes buffer ready for any datagram size // or querying the next datagram size every time, for now I'll leave it as is packet->Reset(NetCode_Invalid, std::numeric_limits::max()); @@ -97,6 +149,20 @@ namespace Nz return true; } + /*! + * \brief Sends the data available + * \return true If data sended + * + * \param to IpAddress of the peer + * \param buffer Raw memory to read + * \param size Size of the buffer + * \param sent Optional argument to get the number of bytes sent + * + * \remark Produces a NazaraAssert if peer address is invalid + * \remark Produces a NazaraAssert if protocol of communication is not the same than the peer + * \remark Produces a NazaraAssert if buffer and its size is invalid + */ + bool UdpSocket::Send(const IpAddress& to, const void* buffer, std::size_t size, std::size_t* sent) { NazaraAssert(to.IsValid(), "Invalid ip address"); @@ -113,6 +179,16 @@ namespace Nz return true; } + /*! + * \brief Sends the packet available + * \return true If packet sent + * + * \param to IpAddress of the peer + * \param packet Packet to send + * + * \remark Produces a NazaraError if packet could not be prepared for sending + */ + bool UdpSocket::SendPacket(const IpAddress& to, const NetPacket& packet) { std::size_t size = 0; @@ -127,6 +203,10 @@ namespace Nz return Send(to, ptr, size, nullptr); } + /*! + * \brief Operation to do when closing socket + */ + void UdpSocket::OnClose() { AbstractSocket::OnClose(); @@ -134,6 +214,10 @@ namespace Nz m_boundAddress = IpAddress::Invalid; } + /*! + * \brief Operation to do when opening socket + */ + void UdpSocket::OnOpened() { AbstractSocket::OnOpened(); diff --git a/tests/Engine/Network/IpAddress.cpp b/tests/Engine/Network/IpAddress.cpp new file mode 100644 index 000000000..8bb3e7d16 --- /dev/null +++ b/tests/Engine/Network/IpAddress.cpp @@ -0,0 +1,47 @@ +#include +#include + +SCENARIO("IpAddress", "[NETWORK][IPADDRESS]") +{ + GIVEN("Two default IpAddress") + { + Nz::IpAddress ipAddressV4; + Nz::IpAddress ipAddressV6; + + WHEN("We parse localhost") + { + Nz::String localhostIPv4 = "127.0.0.1"; + Nz::String localhostIPv6 = "::1"; + REQUIRE(ipAddressV4.BuildFromAddress(localhostIPv4.GetConstBuffer())); + REQUIRE(ipAddressV6.BuildFromAddress(localhostIPv6.GetConstBuffer())); + + THEN("It's the loop back") + { + REQUIRE(ipAddressV4.IsLoopback()); + REQUIRE(ipAddressV6.IsLoopback()); + } + } + } + + GIVEN("No IpAddress") + { + WHEN("We get the IP of Nazara") + { + std::vector hostnameInfos = Nz::IpAddress::ResolveHostname(Nz::NetProtocol_IPv4, "nazara.digitalpulsesoftware.net"); + + THEN("Result is not null") + { + REQUIRE(!hostnameInfos.empty()); + } + } + + WHEN("We convert IP to hostname") + { + Nz::IpAddress google(8, 8, 8, 8); + THEN("Google (DNS) is 8.8.8.8") + { + REQUIRE(Nz::IpAddress::ResolveAddress(google) == "google-public-dns-a.google.com"); + } + } + } +} diff --git a/tests/Engine/Network/RUdpConnection.cpp b/tests/Engine/Network/RUdpConnection.cpp new file mode 100644 index 000000000..a39100e94 --- /dev/null +++ b/tests/Engine/Network/RUdpConnection.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include + +SCENARIO("RUdpConnection", "[NETWORK][RUDPCONNECTION]") +{ + GIVEN("Two RUdpConnection, one client, one server") + { + Nz::UInt16 port = 64266; + Nz::RUdpConnection server; + REQUIRE(server.Listen(Nz::NetProtocol_IPv4, port)); + Nz::IpAddress serverIP = server.GetBoundAddress(); + REQUIRE(serverIP.IsValid()); + Nz::RUdpConnection client; + REQUIRE(client.Listen(Nz::NetProtocol_IPv4, port + 1)); + Nz::IpAddress clientIP = client.GetBoundAddress(); + REQUIRE(client.Connect(serverIP)); + REQUIRE(clientIP.IsValid()); + + WHEN("We send data from client") + { + Nz::NetPacket packet(1); + Nz::Vector3f vector123(1.f, 2.f, 3.f); + packet << vector123; + REQUIRE(client.Send(serverIP, Nz::PacketPriority_Immediate, Nz::PacketReliability_Reliable, packet)); + client.Update(); + + THEN("We should get it on the server") + { + Nz::RUdpMessage rudpMessage; + server.Update(); + REQUIRE(server.PollMessage(&rudpMessage)); + Nz::Vector3f result; + rudpMessage.data >> result; + REQUIRE(result == vector123); + } + } + } +} diff --git a/tests/Engine/Network/TCP.cpp b/tests/Engine/Network/TCP.cpp new file mode 100644 index 000000000..63b4fe2df --- /dev/null +++ b/tests/Engine/Network/TCP.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +#include +#include + +#include + +SCENARIO("TCP", "[NETWORK][TCP]") +{ + GIVEN("Two TCP, one client, one server") + { + // Avoid reusing the same socket + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1025, 64245); + + Nz::UInt16 port = dis(gen); + Nz::TcpServer server; + REQUIRE(server.Listen(Nz::NetProtocol_IPv4, port) == Nz::SocketState_Bound); + Nz::IpAddress serverIP = server.GetBoundAddress(); + REQUIRE(serverIP.IsValid()); + Nz::TcpClient client; + REQUIRE(client.Connect(serverIP) == Nz::SocketState_Connecting); + Nz::IpAddress clientIP = client.GetRemoteAddress(); + REQUIRE(clientIP.IsValid()); + + Nz::TcpClient serverToClient; + REQUIRE(server.AcceptClient(&serverToClient)); + + WHEN("We send data from client") + { + Nz::NetPacket packet(1); + Nz::Vector3f vector123(1.f, 2.f, 3.f); + packet << vector123; + REQUIRE(client.SendPacket(packet)); + + THEN("We should get it on the server") + { + Nz::NetPacket resultPacket; + REQUIRE(serverToClient.ReceivePacket(&resultPacket)); + Nz::Vector3f result; + resultPacket >> result; + REQUIRE(result == vector123); + } + } + } +} diff --git a/tests/Engine/Network/UdpSocket.cpp b/tests/Engine/Network/UdpSocket.cpp new file mode 100644 index 000000000..3f199f09e --- /dev/null +++ b/tests/Engine/Network/UdpSocket.cpp @@ -0,0 +1,39 @@ +#include +#include + +#include +#include + +SCENARIO("UdpSocket", "[NETWORK][UDPSOCKET]") +{ + GIVEN("Two UdpSocket, one client, one server") + { + Nz::UInt16 port = 64256; + Nz::UdpSocket server(Nz::NetProtocol_IPv4); + REQUIRE(server.Bind(port) == Nz::SocketState_Bound); + Nz::IpAddress serverIP = server.GetBoundAddress(); + REQUIRE(serverIP.IsValid()); + Nz::UdpSocket client(Nz::NetProtocol_IPv4); + REQUIRE(client.Bind(port + 1) == Nz::SocketState_Bound); + Nz::IpAddress clientIP = client.GetBoundAddress(); + REQUIRE(clientIP.IsValid()); + + WHEN("We send data from client") + { + Nz::NetPacket packet(1); + Nz::Vector3f vector123(1.f, 2.f, 3.f); + packet << vector123; + REQUIRE(client.SendPacket(serverIP, packet)); + + THEN("We should get it on the server") + { + Nz::NetPacket resultPacket; + Nz::IpAddress fromIp; + REQUIRE(server.ReceivePacket(&resultPacket, &fromIp)); + Nz::Vector3f result; + resultPacket >> result; + REQUIRE(result == vector123); + } + } + } +}