Documentation for module: Network

Former-commit-id: d36042138d0883eb118cc9a70f94f3522214dd46
This commit is contained in:
Gawaboumga 2016-05-30 14:22:31 +02:00
parent 96b958d655
commit 0a99058c4d
25 changed files with 1368 additions and 25 deletions

View File

@ -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)

View File

@ -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 <Nazara/Network/ConfigCheck.hpp>
#if defined(NAZARA_STATIC)

View File

@ -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 <type_traits>
// 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

View File

@ -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

View File

@ -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<Nz::IpAddress>
{
/*!
* \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;
}
};
}

View File

@ -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();

View File

@ -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<SequenceIndex>::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]");

View File

@ -79,8 +79,8 @@ namespace Nz
PendingPacket m_pendingPacket;
UInt64 m_keepAliveInterval;
UInt64 m_keepAliveTime;
bool m_isKeepAliveEnabled;
bool m_isLowDelayEnabled;
bool m_isKeepAliveEnabled;
};
}

View File

@ -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;

View File

@ -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

View File

@ -49,7 +49,6 @@ namespace Nz
void OnOpened() override;
IpAddress m_boundAddress;
SocketState m_state;
bool m_isBroadCastingEnabled;
};
}

View File

@ -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;

View File

@ -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();

View File

@ -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");

View File

@ -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<HostnameInfo> IpAddress::ResolveHostname(NetProtocol protocol, const String& hostname, const String& service, ResolveError* error)
{
NazaraAssert(protocol != NetProtocol_Unknown, "Invalid protocol");

View File

@ -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<Mutex>();
return true;
}
/*!
* \brief Uninitializes the NetPacket class
*/
void NetPacket::Uninitialize()
{
s_availableBuffers.clear();

View File

@ -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)

View File

@ -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<HostnameInfo> 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<UInt16>(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)
@ -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()
{
}

View File

@ -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;

View File

@ -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();

View File

@ -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<UInt16>::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();

View File

@ -0,0 +1,47 @@
#include <Nazara/Network/IpAddress.hpp>
#include <Catch/catch.hpp>
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<Nz::HostnameInfo> 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");
}
}
}
}

View File

@ -0,0 +1,40 @@
#include <Nazara/Network/RUdpConnection.hpp>
#include <Catch/catch.hpp>
#include <Nazara/Math/Vector3.hpp>
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);
}
}
}
}

View File

@ -0,0 +1,49 @@
#include <Nazara/Network/TcpClient.hpp>
#include <Nazara/Network/TcpServer.hpp>
#include <Catch/catch.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <Nazara/Network/NetPacket.hpp>
#include <random>
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);
}
}
}
}

View File

@ -0,0 +1,39 @@
#include <Nazara/Network/UdpSocket.hpp>
#include <Catch/catch.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <Nazara/Network/NetPacket.hpp>
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);
}
}
}
}