Network: First commit

Former-commit-id: ec8697acc51569f5043e4f70e4cf42f1c5dc487c
This commit is contained in:
Lynix 2015-11-09 15:02:25 +01:00
parent dfa6f06337
commit 1bbf038cc6
37 changed files with 3097 additions and 0 deletions

View File

@ -0,0 +1,19 @@
MODULE.Name = "Network"
MODULE.Libraries = {
"NazaraCore"
}
MODULE.OsFiles.Windows = {
"../src/Nazara/Network/Win32/**.hpp",
"../src/Nazara/Network/Win32/**.cpp"
}
MODULE.OsFiles.Posix = {
"../src/Nazara/Network/Posix/**.hpp",
"../src/Nazara/Network/Posix/**.cpp"
}
MODULE.OsLibraries.Windows = {
"ws2_32"
}

View File

@ -0,0 +1,44 @@
// This file was automatically generated on 09 Nov 2015 at 13:52:47
/*
Nazara Engine - Network module
Copyright (C) 2015 Jérôme "Lynix" Leclercq (Lynix680@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#ifndef NAZARA_GLOBAL_NETWORK_HPP
#define NAZARA_GLOBAL_NETWORK_HPP
#include <Nazara/Network/AbstractSocket.hpp>
#include <Nazara/Network/Algorithm.hpp>
#include <Nazara/Network/Config.hpp>
#include <Nazara/Network/Enums.hpp>
#include <Nazara/Network/IpAddress.hpp>
#include <Nazara/Network/Network.hpp>
#include <Nazara/Network/SocketHandle.hpp>
#include <Nazara/Network/TcpBase.hpp>
#include <Nazara/Network/TcpClient.hpp>
#include <Nazara/Network/TcpServer.hpp>
#include <Nazara/Network/UdpSocket.hpp>
#endif // NAZARA_GLOBAL_NETWORK_HPP

View File

@ -0,0 +1,63 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_ABSTRACTSOCKET_HPP
#define NAZARA_ABSTRACTSOCKET_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Signal.hpp>
#include <Nazara/Network/Config.hpp>
#include <Nazara/Network/Enums.hpp>
#include <Nazara/Network/SocketHandle.hpp>
namespace Nz
{
class NAZARA_NETWORK_API AbstractSocket
{
public:
AbstractSocket(const AbstractSocket&) = delete;
AbstractSocket(AbstractSocket&& abstractSocket);
virtual ~AbstractSocket();
void Close();
void EnableBlocking(bool blocking);
inline SocketError GetLastError() const;
inline SocketHandle GetNativeHandle() const;
inline SocketState GetState() const;
inline SocketType GetType() const;
inline bool IsBlockingEnabled() const;
unsigned int QueryAvailableBytes() const;
// Slots
NazaraSignal(OnStateChange, const AbstractSocket* /*socket*/, SocketState /*oldState*/, SocketState /*newState*/);
protected:
AbstractSocket(SocketType type);
inline void ChangeState(SocketState newState);
virtual void OnClose();
virtual void OnOpened();
bool Open(NetProtocol protocol);
void Open(SocketHandle existingHandle);
NetProtocol m_protocol;
SocketError m_lastError;
SocketHandle m_handle;
SocketState m_state;
SocketType m_type;
bool m_isBlockingEnabled;
};
}
#include <Nazara/Network/AbstractSocket.inl>
#endif // NAZARA_ABSTRACTSOCKET_HPP

View File

@ -0,0 +1,45 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
inline SocketError AbstractSocket::GetLastError() const
{
return m_lastError;
}
inline SocketHandle AbstractSocket::GetNativeHandle() const
{
return m_handle;
}
inline SocketState AbstractSocket::GetState() const
{
return m_state;
}
inline SocketType AbstractSocket::GetType() const
{
return m_type;
}
inline bool AbstractSocket::IsBlockingEnabled() const
{
return m_isBlockingEnabled;
}
inline void AbstractSocket::ChangeState(SocketState newState)
{
if (m_state != newState)
{
SocketState oldState = m_state;
m_state = newState;
OnStateChange(this, oldState, m_state);
}
}
}
#include <Nazara/Network/DebugOff.hpp>

View File

@ -0,0 +1,21 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_ALGORITHM_NETWORK_HPP
#define NAZARA_ALGORITHM_NETWORK_HPP
#include <Nazara/Prerequesites.hpp>
#include <functional>
#include <tuple>
namespace Nz
{
bool ParseIPAddress(const char* addressPtr, UInt8 result[16], UInt16* port = nullptr, bool* isIPv6 = nullptr, const char** endOfRead = nullptr);
}
#include <Nazara/Network/Algorithm.inl>
#endif // NAZARA_ALGORITHM_NETWORK_HPP

View File

@ -0,0 +1,7 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Core module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/Debug.hpp>
#include <Nazara/Network/DebugOff.hpp>

View File

@ -0,0 +1,53 @@
/*
Nazara Engine - Network module
Copyright (C) 2015 Jérôme "Lynix" Leclercq (Lynix680@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#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
// Utilise le MemoryManager pour gérer les allocations dynamiques (détecte les leaks au prix d'allocations/libérations dynamiques plus lentes)
#define NAZARA_NETWORK_MANAGE_MEMORY 0
// Active les tests de sécurité basés sur le code (Conseillé pour le développement)
#define NAZARA_NETWORK_SAFE 1
/// Chaque modification d'un paramètre ci-dessous implique une modification (souvent mineure) du code
/// Vérification des valeurs et types de certaines constantes
#include <Nazara/Network/ConfigCheck.hpp>
#if defined(NAZARA_STATIC)
#define NAZARA_NETWORK_API
#else
#ifdef NAZARA_NETWORK_BUILD
#define NAZARA_NETWORK_API NAZARA_EXPORT
#else
#define NAZARA_NETWORK_API NAZARA_IMPORT
#endif
#endif
#endif // NAZARA_CONFIG_NETWORK_HPP

View File

@ -0,0 +1,20 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#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
#include <type_traits>
// On force la valeur de MANAGE_MEMORY en mode debug
#if defined(NAZARA_DEBUG) && !NAZARA_NETWORK_MANAGE_MEMORY
#undef NAZARA_NETWORK_MANAGE_MEMORY
#define NAZARA_NETWORK_MANAGE_MEMORY 0
#endif
#endif // NAZARA_CONFIG_CHECK_NETWORK_HPP

View File

@ -0,0 +1,8 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/Config.hpp>
#if NAZARA_MODULENAME_MANAGE_MEMORY
#include <Nazara/Core/Debug/NewRedefinition.hpp>
#endif

View File

@ -0,0 +1,9 @@
// Copyright (C) 2015 Jérôme Leclercq
// 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
#if NAZARA_MODULENAME_MANAGE_MEMORY
#undef delete
#undef new
#endif

View File

@ -0,0 +1,61 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_ENUMS_NETWORK_HPP
#define NAZARA_ENUMS_NETWORK_HPP
namespace Nz
{
enum NetProtocol
{
NetProtocol_Any,
NetProtocol_IPv4,
NetProtocol_IPv6,
NetProtocol_Max = NetProtocol_IPv6
};
enum SocketError
{
SocketError_NoError,
SocketError_AddressNotAvailable, //< The address is already in use (when binding/listening)
SocketError_ConnectionClosed, //< The connection has been closed
SocketError_ConnectionRefused, //< The connection attempt was refused
SocketError_DatagramSize, //< The datagram size is over the system limit
SocketError_Internal, //< The error is coming from the engine
SocketError_NetworkError, //< The network system has failed (maybe network is down)
SocketError_NotInitialized, //< Nazara network has not been initialized
SocketError_NotSupported, //< The operation is not supported (e.g. creating a bluetooth socket on a system without any bluetooth adaptater)
SocketError_ResourceError, //< The operating system lacks the resources to proceed (e.g. memory/socket descriptor)
SocketError_UnreachableHost, //< The host is not reachable
SocketError_TimedOut, //< The operation timed out
SocketError_Unknown, //< The last operation failed with an unlisted error code
SocketError_Max = SocketError_Unknown
};
enum SocketState
{
SocketState_Bound, //< The socket is currently bound
SocketState_Connecting, //< The socket is currently connecting
SocketState_Connected, //< The socket is currently connected
SocketState_NotConnected, //< The socket is not connected (or has been disconnected)
SocketState_Max = SocketState_NotConnected
};
enum SocketType
{
SocketType_Raw,
SocketType_TCP,
SocketType_UDP,
SocketType_Max = SocketType_UDP
};
}
#endif // NAZARA_ENUMS_NETWORK_HPP

View File

@ -0,0 +1,102 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_IPADDRESS_HPP
#define NAZARA_IPADDRESS_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/String.hpp>
#include <Nazara/Network/Config.hpp>
#include <Nazara/Network/Enums.hpp>
#include <array>
#include <iosfwd>
namespace Nz
{
struct HostnameInfo;
class NAZARA_NETWORK_API IpAddress
{
public:
using IPv4 = std::array<UInt8, 4>; //< four 8bits blocks
using IPv6 = std::array<UInt16, 8>; //< eight 16bits blocks
inline IpAddress();
inline IpAddress(const IPv4& ip, UInt16 port = 0);
inline IpAddress(const IPv6& ip, UInt16 port = 0);
inline IpAddress(const UInt8& a, const UInt8& b, const UInt8& c, const UInt8& d, UInt16 port = 0);
inline 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 = 0);
inline IpAddress(const char* address);
inline IpAddress(const String& address);
IpAddress(const IpAddress&) = default;
IpAddress(IpAddress&&) = default;
~IpAddress() = default;
bool BuildFromAddress(const char* address);
inline UInt16 GetPort() const;
inline NetProtocol GetProtocol() const;
bool IsLoopback() const;
inline bool IsValid() const;
inline void SetPort(UInt16 port);
inline IPv4 ToIPv4() const;
inline IPv6 ToIPv6() const;
String ToString() const;
inline UInt32 ToUInt32() const;
inline operator bool() const;
IpAddress& operator=(const IpAddress&) = default;
IpAddress& operator=(IpAddress&&) = default;
static String ResolveAddress(const IpAddress& address, String* service = nullptr);
static std::vector<HostnameInfo> ResolveHostname(NetProtocol procol, const String& hostname, const String& protocol = "http");
inline friend std::ostream& operator<<(std::ostream& out, const IpAddress& address);
inline friend bool operator==(const IpAddress& first, const IpAddress& second);
inline friend bool operator!=(const IpAddress& first, const IpAddress& second);
inline friend bool operator<(const IpAddress& first, const IpAddress& second);
inline friend bool operator<=(const IpAddress& first, const IpAddress& second);
inline friend bool operator>(const IpAddress& first, const IpAddress& second);
inline friend bool operator>=(const IpAddress& first, const IpAddress& second);
static IpAddress AnyIpV4;
static IpAddress AnyIpV6;
static IpAddress BroadcastIpV4;
static IpAddress Invalid;
static IpAddress LoopbackIpV4;
static IpAddress LoopbackIpV6;
private:
union
{
IPv4 m_ipv4;
IPv6 m_ipv6;
};
NetProtocol m_protocol;
UInt16 m_port;
bool m_isValid;
};
struct HostnameInfo
{
IpAddress address;
String canonicalName;
int flags;
int family;
int socketType;
};
}
#include <Nazara/Network/IpAddress.inl>
#endif // NAZARA_IPADDRESS_HPP

View File

@ -0,0 +1,205 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Core/Error.hpp>
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
inline IpAddress::IpAddress() :
m_isValid(false)
{
}
inline IpAddress::IpAddress(const IPv4& ip, UInt16 port) :
m_isValid(true),
m_ipv4(ip),
m_protocol(NetProtocol_IPv4),
m_port(port)
{
}
inline IpAddress::IpAddress(const IPv6& ip, UInt16 port) :
m_isValid(true),
m_ipv6(ip),
m_protocol(NetProtocol_IPv6),
m_port(port)
{
}
inline IpAddress::IpAddress(const UInt8& a, const UInt8& b, const UInt8& c, const UInt8& d, UInt16 port) :
IpAddress(IPv4{a, b, c, d}, port)
{
}
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)
{
}
inline IpAddress::IpAddress(const char* address)
{
BuildFromAddress(address);
}
inline IpAddress::IpAddress(const String& address)
{
BuildFromAddress(address.GetConstBuffer());
}
inline UInt16 IpAddress::GetPort() const
{
return m_port;
}
inline NetProtocol IpAddress::GetProtocol() const
{
return m_protocol;
}
inline bool IpAddress::IsValid() const
{
return m_isValid;
}
inline void IpAddress::SetPort(UInt16 port)
{
m_port = port;
}
inline IpAddress::IPv4 IpAddress::ToIPv4() const
{
NazaraAssert(m_isValid && m_protocol == NetProtocol_IPv4, "Address is not a valid IPv4");
return m_ipv4;
}
inline IpAddress::IPv6 IpAddress::ToIPv6() const
{
NazaraAssert(m_isValid && m_protocol == NetProtocol_IPv6, "IP is not a valid IPv6");
return m_ipv6;
}
inline UInt32 IpAddress::ToUInt32() const
{
NazaraAssert(m_isValid && m_protocol == NetProtocol_IPv4, "Address is not a valid IPv4");
return UInt32(m_ipv4[0]) << 24 |
UInt32(m_ipv4[1]) << 16 |
UInt32(m_ipv4[2]) << 8 |
UInt32(m_ipv4[3]) << 0;
}
inline IpAddress::operator bool() const
{
return IsValid();
}
inline std::ostream& operator<<(std::ostream& out, const IpAddress& address)
{
out << "IpAddress(" << address.ToString() << ')';
return out;
}
inline bool operator==(const IpAddress& first, const IpAddress& second)
{
// We need to check the validity of each address before comparing them
if (!first.m_isValid || !second.m_isValid)
return first.m_isValid == second.m_isValid;
// Then the protocol
if (first.m_protocol != second.m_protocol)
return false;
// Each protocol has its variables to compare
switch (first.m_protocol)
{
case NetProtocol_IPv4:
{
if (first.m_ipv4 != second.m_ipv4)
return false;
break;
}
case NetProtocol_IPv6:
{
if (first.m_ipv6 != second.m_ipv6)
return false;
break;
}
}
// Check the port, in case there is one
if (first.m_port != second.m_port)
return false;
return true;
}
inline bool operator!=(const IpAddress& first, const IpAddress& second)
{
return !operator==(first, second);
}
inline bool operator<(const IpAddress& first, const IpAddress& second)
{
// If the second address is invalid, there's no way we're lower than it
if (!second.m_isValid)
return false;
// By this point, the second address is valid, thus check our validity
if (!first.m_isValid)
return true; // Invalid address are lower than valid one
// Compare protocols
if (first.m_protocol != second.m_protocol)
return first.m_protocol < second.m_protocol;
// Compare IP (thanks to std::array comparison operator)
switch (first.m_protocol)
{
case NetProtocol_IPv4:
{
if (first.m_ipv4 != second.m_ipv4)
return first.m_ipv4 < second.m_ipv4;
break;
}
case NetProtocol_IPv6:
{
if (first.m_ipv6 != second.m_ipv6)
return first.m_ipv6 < second.m_ipv6;
break;
}
}
// Compare port
if (first.m_port != second.m_port)
return first.m_port < second.m_port;
return false; //< Same address
}
inline bool operator<=(const IpAddress& first, const IpAddress& second)
{
return !operator<(second, first);
}
inline bool operator>(const IpAddress& first, const IpAddress& second)
{
return second < first;
}
inline bool operator>=(const IpAddress& first, const IpAddress& second)
{
return !operator<(first, second);
}
}
#include <Nazara/Network/DebugOff.hpp>

View File

@ -0,0 +1,33 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_MODULENAME_HPP
#define NAZARA_MODULENAME_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Initializer.hpp>
#include <Nazara/Network/Config.hpp>
namespace Nz
{
class NAZARA_NETWORK_API Network
{
public:
Network() = delete;
~Network() = delete;
static bool Initialize();
static bool IsInitialized();
static void Uninitialize();
private:
static unsigned int s_moduleReferenceCounter;
};
}
#endif // NAZARA_MODULENAME_HPP

View File

@ -0,0 +1,27 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_SOCKETHANDLE_HPP
#define NAZARA_SOCKETHANDLE_HPP
#include <Nazara/Prerequesites.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <basetsd.h>
#endif
namespace Nz
{
#if defined(NAZARA_PLATFORM_WINDOWS)
using SocketHandle = UINT_PTR;
#elif defined(NAZARA_PLATFORM_POSIX)
using SocketHandle = int;
#else
#error Lack of implementation: SocketHandle
#endif
}
#endif // NAZARA_SOCKETHANDLE_HPP

View File

@ -0,0 +1,42 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TCPBASE_HPP
#define NAZARA_TCPBASE_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Network/AbstractSocket.hpp>
namespace Nz
{
class NAZARA_NETWORK_API TcpBase : public AbstractSocket
{
public:
~TcpBase() = default;
inline bool IsLowDelayEnabled() const;
inline bool IsKeepAliveEnabled() const;
// Slots
NazaraSignal(OnStateChange, const TcpBase* /*socket*/, SocketState /*newState*/);
protected:
TcpBase();
TcpBase(TcpBase&& tcpBase);
virtual void OnOpened() override;
SocketState m_state;
UInt64 m_keepAliveInterval;
UInt64 m_keepAliveTime;
bool m_isLowDelayEnabled;
bool m_isKeepAliveEnabled;
};
}
#include <Nazara/Network/TcpBase.inl>
#endif // NAZARA_TCPBASE_HPP

View File

@ -0,0 +1,36 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <utility>
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
inline TcpBase::TcpBase() :
AbstractSocket(SocketType_TCP)
{
}
inline TcpBase::TcpBase(TcpBase&& tcpBase) :
AbstractSocket(std::move(tcpBase)),
m_state(tcpBase.m_state),
m_keepAliveInterval(tcpBase.m_keepAliveInterval),
m_keepAliveTime(tcpBase.m_keepAliveTime),
m_isLowDelayEnabled(tcpBase.m_isLowDelayEnabled),
m_isKeepAliveEnabled(tcpBase.m_isKeepAliveEnabled)
{
}
inline bool TcpBase::IsLowDelayEnabled() const
{
return m_isLowDelayEnabled;
}
inline bool TcpBase::IsKeepAliveEnabled() const
{
return m_isKeepAliveEnabled;
}
}
#include <Nazara/Network/DebugOff.hpp>

View File

@ -0,0 +1,54 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TCPCLIENT_HPP
#define NAZARA_TCPCLIENT_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Signal.hpp>
#include <Nazara/Network/TcpBase.hpp>
#include <Nazara/Network/IpAddress.hpp>
namespace Nz
{
class NAZARA_NETWORK_API TcpClient : public TcpBase
{
friend class TcpServer;
public:
TcpClient() = default;
inline TcpClient(TcpClient&& tcpClient);
~TcpClient() = default;
SocketState Connect(const IpAddress& remoteAddress, UInt64 msTimeout = 3000);
inline void Disconnect();
void EnableLowDelay(bool lowDelay);
void EnableKeepAlive(bool keepAlive, UInt64 msTime = 10000, UInt64 msInterval = 1000);
inline UInt64 GetKeepAliveInterval() const;
inline UInt64 GetKeepAliveTime() const;
inline IpAddress GetRemoteAddress() const;
SocketState QueryState();
bool Receive(void* buffer, std::size_t size, std::size_t* received);
bool Send(const void* buffer, std::size_t size, std::size_t* sent);
private:
void OnClose() override;
void OnOpened() override;
void Reset(SocketHandle handle, const IpAddress& peerAddress);
IpAddress m_peerAddress;
};
}
#include <Nazara/Network/TcpClient.inl>
#endif // NAZARA_TCPCLIENT_HPP

View File

@ -0,0 +1,37 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <utility>
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
inline TcpClient::TcpClient(TcpClient&& tcpClient) :
TcpBase(std::move(tcpClient)),
m_peerAddress(std::move(tcpClient.m_peerAddress))
{
}
inline void TcpClient::Disconnect()
{
Close();
}
inline UInt64 TcpClient::GetKeepAliveInterval() const
{
return m_keepAliveInterval;
}
inline UInt64 TcpClient::GetKeepAliveTime() const
{
return m_keepAliveTime;
}
inline IpAddress TcpClient::GetRemoteAddress() const
{
return m_peerAddress;
}
}
#include <Nazara/Network/DebugOff.hpp>

View File

@ -0,0 +1,43 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_TCPSERVER_HPP
#define NAZARA_TCPSERVER_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Network/TcpBase.hpp>
#include <Nazara/Network/IpAddress.hpp>
namespace Nz
{
class TcpClient;
class NAZARA_NETWORK_API TcpServer : public TcpBase
{
public:
TcpServer() = default;
inline TcpServer(TcpServer&& tcpServer);
~TcpServer() = default;
bool AcceptClient(TcpClient* newClient);
inline IpAddress GetBoundAddress() const;
inline UInt16 GetBoundPort() const;
inline SocketState Listen(NetProtocol protocol, UInt16 port, unsigned int queueSize = 10);
SocketState Listen(const IpAddress& address, unsigned int queueSize = 10);
private:
void OnClose() override;
void OnOpened() override;
IpAddress m_boundAddress;
};
}
#include <Nazara/Network/TcpServer.inl>
#endif // NAZARA_TCPSERVER_HPP

View File

@ -0,0 +1,47 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <utility>
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
inline TcpServer::TcpServer(TcpServer&& tcpServer) :
TcpBase(std::move(tcpServer)),
m_boundAddress(std::move(tcpServer.m_boundAddress))
{
}
inline IpAddress TcpServer::GetBoundAddress() const
{
return m_boundAddress;
}
inline UInt16 TcpServer::GetBoundPort() const
{
return m_boundAddress.GetPort();
}
inline SocketState TcpServer::Listen(NetProtocol protocol, UInt16 port, unsigned int queueSize)
{
NazaraAssert(protocol != NetProtocol_Any, "Any protocol not supported for Listen"); //< TODO
IpAddress any;
switch (protocol)
{
case NetProtocol_IPv4:
any = IpAddress::AnyIpV4;
break;
case NetProtocol_IPv6:
any = IpAddress::AnyIpV6;
break;
}
any.SetPort(port);
return Listen(any, queueSize);
}
}
#include <Nazara/Network/DebugOff.hpp>

View File

@ -0,0 +1,50 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_UDPSOCKET_HPP
#define NAZARA_UDPSOCKET_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Network/AbstractSocket.hpp>
#include <Nazara/Network/IpAddress.hpp>
namespace Nz
{
class NAZARA_NETWORK_API UdpSocket : public AbstractSocket
{
public:
inline UdpSocket();
inline UdpSocket(NetProtocol protocol);
inline UdpSocket(UdpSocket&& udpSocket);
~UdpSocket() = default;
inline SocketState Bind(UInt16 port);
SocketState Bind(const IpAddress& address);
inline bool Create(NetProtocol protocol);
inline IpAddress GetBoundAddress() const;
inline UInt16 GetBoundPort() const;
inline SocketState GetState() const;
unsigned int QueryMaxDatagramSize();
bool Receive(void* buffer, std::size_t size, IpAddress* from, std::size_t* received);
bool Send(const IpAddress& to, const void* buffer, std::size_t size, std::size_t* sent);
private:
void OnClose() override;
void OnOpened() override;
IpAddress m_boundAddress;
SocketState m_state;
};
}
#include <Nazara/Network/UdpSocket.inl>
#endif // NAZARA_UDPSOCKET_HPP

View File

@ -0,0 +1,66 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
inline UdpSocket::UdpSocket() :
AbstractSocket(SocketType_UDP)
{
}
inline UdpSocket::UdpSocket(NetProtocol protocol) :
UdpSocket()
{
Create(protocol);
}
inline UdpSocket::UdpSocket(UdpSocket&& udpSocket) :
AbstractSocket(std::move(udpSocket)),
m_boundAddress(std::move(udpSocket.m_boundAddress)),
m_state(udpSocket.m_state)
{
}
inline SocketState UdpSocket::Bind(UInt16 port)
{
IpAddress any;
switch (m_protocol)
{
case NetProtocol_IPv4:
any = IpAddress::AnyIpV4;
break;
case NetProtocol_IPv6:
any = IpAddress::AnyIpV6;
break;
}
any.SetPort(port);
return Bind(any);
}
bool UdpSocket::Create(NetProtocol protocol)
{
return Open(protocol);
}
inline IpAddress UdpSocket::GetBoundAddress() const
{
return m_boundAddress;
}
inline UInt16 UdpSocket::GetBoundPort() const
{
return m_boundAddress.GetPort();
}
inline SocketState UdpSocket::GetState() const
{
return m_state;
}
}
#include <Nazara/Network/DebugOff.hpp>

View File

@ -0,0 +1,108 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/AbstractSocket.hpp>
#include <Nazara/Core/Error.hpp>
#include <utility>
#include <Nazara/Network/Debug.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif
namespace Nz
{
AbstractSocket::AbstractSocket(SocketType type) :
m_handle(SocketImpl::InvalidHandle),
m_state(SocketState_NotConnected),
m_type(type),
m_isBlockingEnabled(true)
{
}
AbstractSocket::AbstractSocket(AbstractSocket&& abstractSocket) :
m_protocol(abstractSocket.m_protocol),
m_lastError(abstractSocket.m_lastError),
m_handle(abstractSocket.m_handle),
m_state(abstractSocket.m_state),
m_type(abstractSocket.m_type),
m_isBlockingEnabled(abstractSocket.m_isBlockingEnabled)
{
abstractSocket.m_handle = SocketImpl::InvalidHandle;
}
AbstractSocket::~AbstractSocket()
{
Close();
}
void AbstractSocket::Close()
{
if (m_handle != SocketImpl::InvalidHandle)
{
OnClose();
SocketImpl::Close(m_handle);
m_handle = SocketImpl::InvalidHandle;
}
}
void AbstractSocket::EnableBlocking(bool blocking)
{
if (m_isBlockingEnabled != blocking)
{
if (m_handle != SocketImpl::InvalidHandle)
SocketImpl::SetBlocking(m_handle, blocking, &m_lastError);
m_isBlockingEnabled = blocking;
}
}
unsigned int AbstractSocket::QueryAvailableBytes() const
{
if (m_handle == SocketImpl::InvalidHandle)
return 0;
return SocketImpl::QueryAvailableBytes(m_handle);
}
void AbstractSocket::OnClose()
{
ChangeState(SocketState_NotConnected);
}
void AbstractSocket::OnOpened()
{
SocketError error;
if (!SocketImpl::SetBlocking(m_handle, m_isBlockingEnabled, &error))
NazaraWarning("Failed to set socket blocking mode (0x" + String::Number(ERROR, 16) + ')');
}
bool AbstractSocket::Open(NetProtocol protocol)
{
if (m_handle == SocketImpl::InvalidHandle || m_protocol != protocol)
{
SocketHandle handle = SocketImpl::Create(protocol, m_type, &m_lastError);
if (handle == SocketImpl::InvalidHandle)
return false;
m_protocol = protocol;
Open(handle);
}
return true;
}
void AbstractSocket::Open(SocketHandle handle)
{
NazaraAssert(handle != SocketImpl::InvalidHandle, "Invalid handle");
Close();
m_handle = handle;
OnOpened();
}
}

View File

@ -0,0 +1,257 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/Algorithm.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
namespace Detail
{
bool ParseDecimal(const char* str, unsigned int* number, const char** endOfRead)
{
const char* ptr = str;
unsigned int val = 0;
while (*ptr >= '0' && *ptr <= '9')
{
val *= 10;
val += *ptr - '0';
++ptr;
}
if (str == ptr)
return false;
if (number)
*number = val;
if (endOfRead)
*endOfRead = ptr;
return true;
}
bool ParseHexadecimal(const char* str, unsigned int* number, const char** endOfRead)
{
const char* ptr = str;
unsigned int val = 0;
while ((*ptr >= '0' && *ptr <= '9') || ((*ptr & 0x5F) >= 'A' && (*ptr & 0x5F) <= 'F'))
{
val *= 16;
val += (*ptr > '9') ? ((*ptr & 0x5F) - 'A' + 10) : *ptr - '0';
++ptr;
}
if (str == ptr)
return false;
if (number)
*number = val;
if (endOfRead)
*endOfRead = ptr;
return true;
}
}
// 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.
bool ParseIPAddress(const char* addressPtr, UInt8 result[16], UInt16* port, bool* isIPv6, const char** endOfRead)
{
NazaraAssert(addressPtr, "Invalid address string");
NazaraAssert(result, "Invalid result pointer");
//find first colon, dot, and open bracket
const char* colonPtr = std::strchr(addressPtr, ':');
const char* dotPtr = std::strchr(addressPtr, '.');
const char* openBracketPtr = std::strchr(addressPtr, '[');
// we'll consider this to (probably) be IPv6 if we find an open
// bracket, or an absence of dots, or if there is a colon, and it
// precedes any dots that may or may not be there
bool detectedIPv6 = openBracketPtr || !dotPtr || (colonPtr && (!dotPtr || colonPtr < dotPtr));
// OK, now do a little further sanity check our initial guess...
if (detectedIPv6)
{
// if open bracket, then must have close bracket that follows somewhere
const char* closeBracketPtr = std::strchr(addressPtr, ']');
if (openBracketPtr && (!closeBracketPtr || closeBracketPtr < openBracketPtr))
return false;
}
else // probably ipv4
{
// dots must exist, and precede any colons
if (!dotPtr || (colonPtr && colonPtr < dotPtr))
return false;
}
// OK, there should be no correctly formed strings which are miscategorized,
// and now any format errors will be found out as we continue parsing
// according to plan.
if (!detectedIPv6) //try to parse as IPv4
{
// 4 dotted quad decimal; optional port if there is a colon
// since there are just 4, and because the last one can be terminated
// differently, I'm just going to unroll any potential loop.
UInt8* resultPtr = result;
for (unsigned int i = 0; i < 4; ++i)
{
unsigned int value;
if (!Detail::ParseDecimal(addressPtr, &value, &addressPtr) || value > 255) //must be in range and followed by dot and nonempty
return false;
if (i != 3)
{
if (*addressPtr != '.')
return false;
addressPtr++;
}
*resultPtr++ = static_cast<UInt8>(value);
}
}
else // try to parse as IPv6
{
UInt8* resultPtr;
UInt8* zeroLoc;
// up to 8 16-bit hex quantities, separated by colons, with at most one
// empty quantity, acting as a stretchy run of zeros. optional port
// if there are brackets followed by colon and decimal port number.
// A further form allows an ipv4 dotted quad instead of the last two
// 16-bit quantities, but only if in the ipv4 space ::ffff:x:x .
if (openBracketPtr) // start past the open bracket, if it exists
addressPtr = openBracketPtr + 1;
resultPtr = result;
zeroLoc = nullptr; // if we find a 'zero compression' location
bool mappedIPv4 = false;
unsigned int i;
for (i = 0; i < 8; ++i) // we've got up to 8 of these, so we will use a loop
{
const char* savedPtr = addressPtr;
unsigned int value; // get value; these are hex
if (!Detail::ParseHexadecimal(addressPtr, &value, &addressPtr)) // if empty, we are zero compressing; note the loc
{
if (zeroLoc) //there can be only one!
{
// unless it's a terminal empty field, then this is OK, it just means we're done with the host part
if (resultPtr == zeroLoc)
{
--i;
break;
}
return false; // otherwise, it's a format error
}
if (*addressPtr != ':') // empty field can only be via :
return false;
if (i == 0 && *++addressPtr != ':') // leading zero compression requires an extra peek, and adjustment
return false;
zeroLoc = resultPtr;
++addressPtr;
}
else
{
if ('.' == *addressPtr) // special case of ipv4 convenience notation
{
addressPtr = savedPtr;
// who knows how to parse ipv4? we do!
UInt8 result[16];
bool isIPv6;
if (!ParseIPAddress(addressPtr, result, nullptr, &isIPv6, &addressPtr) || isIPv6) // must parse and must be ipv4
return false;
// transfer addrlocal into the present location
for (unsigned int j = 0; j < 4; ++j)
*(resultPtr++) = result[j];
++i; // pretend like we took another short, since the ipv4 effectively is two shorts
mappedIPv4 = true; // remember how we got here for further validation later
break; // totally done with address
}
if (value > 65535) // must be 16 bit quantity
return false;
*(resultPtr++) = value >> 8;
*(resultPtr++) = value & 0xFF;
if (*addressPtr == ':') // typical case inside; carry on
++addressPtr;
else // some other terminating character; done with this parsing parts
break;
}
}
// handle any zero compression we found
if (zeroLoc)
{
std::ptrdiff_t nHead = (int) (zeroLoc - result); // how much before zero compression
std::ptrdiff_t nTail = i * 2 - nHead; // how much after zero compression
std::ptrdiff_t nZeros = 16 - nTail - nHead; // how much zeros
std::memmove(&result[16 - nTail], zeroLoc, nTail); // scootch stuff down
std::memset(zeroLoc, 0, nZeros); // clear the compressed zeros
}
// validation of ipv4 subspace ::ffff:x.x
if (mappedIPv4)
{
static const UInt8 abyPfx[] = {0,0, 0,0, 0,0, 0,0, 0,0, 0xFF,0xFF};
if (std::memcmp(result, abyPfx, sizeof(abyPfx)) != 0)
return false;
}
// close bracket
if (openBracketPtr)
{
if (*addressPtr != ']')
return false;
++addressPtr;
}
}
// if asked to read the port
if (port)
{
if (*addressPtr == ':') // have port part
{
++addressPtr; // past the colon
unsigned int portValue;
if (!Detail::ParseDecimal(addressPtr, &portValue, nullptr) || portValue > 65535)
return false;
if (port)
*port = static_cast<UInt16>(portValue);
}
else // finished just with IP address
*port = 0; // indicate we have no port part
}
if (isIPv6)
*isIPv6 = detectedIPv6;
if (endOfRead)
*endOfRead = addressPtr;
return true;
}
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/Config.hpp>
#if NAZARA_NETWORK_MANAGE_MEMORY
#include <Nazara/Core/MemoryManager.hpp>
#include <new> // Is this necessary ?
void* operator new(std::size_t size)
{
return Nz::MemoryManager::Allocate(size, false);
}
void* operator new[](std::size_t size)
{
return Nz::MemoryManager::Allocate(size, true);
}
void operator delete(void* pointer) noexcept
{
Nz::MemoryManager::Free(pointer, false);
}
void operator delete[](void* pointer) noexcept
{
Nz::MemoryManager::Free(pointer, true);
}
#endif // NAZARA_NETWORK_MANAGE_MEMORY

View File

@ -0,0 +1,164 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/IpAddress.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/StringStream.hpp>
#include <Nazara/Network/Algorithm.hpp>
#include <algorithm>
#include <Nazara/Network/SystemSocket.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/IpAddressImpl.hpp>
#else
#error Missing implementation: Network
#endif
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
bool IpAddress::BuildFromAddress(const char* address)
{
m_isValid = false;
bool isIPv6;
UInt8 result[16];
if (!ParseIPAddress(address, result, &m_port, &isIPv6, nullptr))
return false;
m_isValid = true;
if (isIPv6)
{
m_protocol = NetProtocol_IPv6;
for (unsigned int i = 0; i < 8; ++i)
m_ipv6[i] = UInt32(result[i*2]) << 8 | result[i*2 + 1];
}
else
{
m_protocol = NetProtocol_IPv4;
for (unsigned int i = 0; i < 4; ++i)
m_ipv4[i] = result[i];
}
return true;
}
bool IpAddress::IsLoopback() const
{
if (!m_isValid)
return false;
NazaraAssert(m_protocol <= NetProtocol_Max, "Protocol has value out of enum");
switch (m_protocol)
{
case NetProtocol_IPv4:
return m_ipv4[0] == 127;
case NetProtocol_IPv6:
return m_ipv6 == LoopbackIpV6.m_ipv6; // Only compare the ip value
}
NazaraInternalError("Invalid protocol for IpAddress (0x" + String::Number(m_protocol) + ')');
return false;
}
String IpAddress::ToString() const
{
StringStream stream;
if (m_isValid)
{
NazaraAssert(m_protocol <= NetProtocol_Max, "Protocol has value out of enum");
switch (m_protocol)
{
case NetProtocol_IPv4:
for (unsigned int i = 0; i < 4; ++i)
{
stream << int(m_ipv4[i]);
if (i != 3)
stream << '.';
}
break;
case NetProtocol_IPv6:
// Canonical representation of an IPv6
// https://tools.ietf.org/html/rfc5952
// Find the longest zero sequence
int f0 = -1;
int l0 = -1;
for (unsigned int i = 0; i < 8; ++i)
{
if (m_ipv6[i] == 0)
{
unsigned int j;
for (j = i + 1; j < 8; ++j)
{
if (m_ipv6[j] != 0)
break;
}
if (j - i > std::max<unsigned int>(l0 - f0, 1))
{
f0 = i;
l0 = j;
}
}
}
// We need brackets around our IPv6 address if we have a port
if (m_port != 0)
stream << '[';
for (unsigned int i = 0; i < 8; ++i)
{
if (i == f0)
{
stream << "::";
i = l0;
if (i >= 8)
break;
}
else if (i != 0)
stream << ':';
stream << String::Number(m_ipv6[i], 16).ToLower();
}
if (m_port != 0)
stream << ']';
break;
}
if (m_port != 0)
stream << ':' << m_port;
}
return stream;
}
String IpAddress::ResolveAddress(const IpAddress& address, String* service)
{
String hostname;
IpAddressImpl::ResolveAddress(address, &hostname, service);
return hostname;
}
std::vector<HostnameInfo> IpAddress::ResolveHostname(NetProtocol protocol, const String& hostname, const String& service)
{
return IpAddressImpl::ResolveHostname(protocol, hostname, service);
}
IpAddress IpAddress::AnyIpV4(0, 0, 0, 0);
IpAddress IpAddress::AnyIpV6(0, 0, 0, 0, 0, 0, 0, 0, 0);
IpAddress IpAddress::BroadcastIpV4(255, 255, 255, 255);
IpAddress IpAddress::Invalid;
IpAddress IpAddress::LoopbackIpV4(127, 0, 0, 1);
IpAddress IpAddress::LoopbackIpV6(0, 0, 0, 0, 0, 0, 0, 1);
}

View File

@ -0,0 +1,84 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/Network.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Core.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Network/Config.hpp>
#include <Nazara/Network/Debug.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#else
#error Missing implementation: Network
#endif
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
bool Network::Initialize()
{
if (s_moduleReferenceCounter > 0)
{
s_moduleReferenceCounter++;
return true; // Already initialized
}
// Initialize module dependencies
if (!Core::Initialize())
{
NazaraError("Failed to initialize core module");
return false;
}
s_moduleReferenceCounter++;
CallOnExit onExit(Network::Uninitialize);
// Initialize module here
if (!SocketImpl::Initialize())
{
NazaraError("Failed to initialize socket implementation");
return false;
}
onExit.Reset();
NazaraNotice("Initialized: Network module");
return true;
}
bool Network::IsInitialized()
{
return s_moduleReferenceCounter != 0;
}
void Network::Uninitialize()
{
if (s_moduleReferenceCounter != 1)
{
// Either the module is not initialized, either it was initialized multiple times
if (s_moduleReferenceCounter > 1)
s_moduleReferenceCounter--;
return;
}
s_moduleReferenceCounter = 0;
// Uninitialize module here
SocketImpl::Uninitialize();
NazaraNotice("Uninitialized: Network module");
// Free module dependencies
Core::Uninitialize();
}
unsigned int Network::s_moduleReferenceCounter = 0;
}

View File

@ -0,0 +1,10 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#ifdef NAZARA_PLATFORM_WINDOWS
#include <winsock2.h>
#include <ws2tcpip.h>
#elif defined(NAZARA_PLATFORM_POSIX)
#include <sys/socket.h>
#endif

View File

@ -0,0 +1,30 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/TcpBase.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp>
#include <limits>
#include <Nazara/Network/Debug.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif
namespace Nz
{
void TcpBase::OnOpened()
{
AbstractSocket::OnOpened();
m_isLowDelayEnabled = false; //< Nagle's algorithm, is this enabled everywhere?
m_isKeepAliveEnabled = false; //< default documentation value, OS can change this (TODO: Query OS default value)
m_keepAliveInterval = 1000; //< default documentation value, OS can change this (TODO: Query OS default value)
m_keepAliveTime = 7200000; //< default documentation value, OS can change this (TODO: Query OS default value)
ChangeState(SocketState_NotConnected);
}
}

View File

@ -0,0 +1,207 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/TcpClient.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp>
#include <limits>
#include <Nazara/Network/Debug.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif
namespace Nz
{
SocketState TcpClient::Connect(const IpAddress& remoteAddress, UInt64 msTimeout)
{
NazaraAssert(remoteAddress.IsValid(), "Invalid remote address");
NazaraAssert(remoteAddress.GetPort() != 0, "Remote address has no port");
Open(remoteAddress.GetProtocol());
CallOnExit restoreBlocking;
if (m_isBlockingEnabled)
{
SocketImpl::SetBlocking(m_handle, false);
restoreBlocking.Reset([this] ()
{
SocketImpl::SetBlocking(m_handle, true);
});
}
SocketState state;
if (msTimeout > 0)
state = SocketImpl::Connect(m_handle, remoteAddress, msTimeout, &m_lastError);
else
state = SocketImpl::Connect(m_handle, remoteAddress, &m_lastError);
if (state != SocketState_NotConnected)
m_peerAddress = remoteAddress;
ChangeState(state);
return state;
}
void TcpClient::EnableLowDelay(bool lowDelay)
{
if (m_isLowDelayEnabled != lowDelay)
{
SocketImpl::SetBlocking(m_handle, lowDelay, &m_lastError);
m_isLowDelayEnabled = lowDelay;
}
}
void TcpClient::EnableKeepAlive(bool keepAlive, UInt64 msTime, UInt64 msInterval)
{
if (m_isKeepAliveEnabled != keepAlive || m_keepAliveTime != msTime || m_keepAliveInterval != msInterval)
{
SocketImpl::SetKeepAlive(m_handle, keepAlive, msTime, msInterval, &m_lastError);
m_isKeepAliveEnabled = keepAlive;
m_keepAliveInterval = msInterval;
m_keepAliveTime = msTime;
}
}
SocketState TcpClient::QueryState()
{
// Check our state depending on our last state
switch (m_state)
{
case SocketState_Connecting:
{
// If we were connecting, check how it's going
SocketError getError;
SocketError error = SocketImpl::GetLastError(m_handle, &getError);
if (getError != SocketError_NoError)
break; //< Do not update state if we cannot get socket state
if (error == SocketError_NoError)
{
// No error yet, we're still connecting or connected, check that by connecting again
return Connect(m_peerAddress, 0);
}
else
{
// Our connection attempt failed
m_lastError = error;
ChangeState(SocketState_NotConnected);
}
break;
}
default:
{
// Check our peer address, if it works we're connected
SocketError error;
m_peerAddress = SocketImpl::QueryPeerAddress(m_handle, &error);
if (m_peerAddress == IpAddress::Invalid)
{
// Other errors mean a problem while getting the peer address
if (error == SocketError_ConnectionClosed)
ChangeState(SocketState_NotConnected);
}
else
ChangeState(SocketState_Connected); // If we are not connecting and have a peer address, we are connected
break;
}
}
return m_state;
}
bool TcpClient::Receive(void* buffer, std::size_t size, std::size_t* received)
{
NazaraAssert(buffer && size > 0, "Invalid buffer");
int read;
if (!SocketImpl::Receive(m_handle, buffer, static_cast<int>(size), &read, &m_lastError))
{
switch (m_lastError)
{
case SocketError_ConnectionClosed:
case SocketError_ConnectionRefused:
ChangeState(SocketState_NotConnected);
break;
default:
break;
}
return false;
}
if (received)
*received = read;
ChangeState(SocketState_Connected);
return true;
}
bool TcpClient::Send(const void* buffer, std::size_t size, std::size_t* sent)
{
NazaraAssert(buffer && size > 0, "Invalid buffer");
CallOnExit updateSent;
std::size_t totalByteSent = 0;
if (sent)
{
updateSent.Reset([sent, &totalByteSent] ()
{
*sent = totalByteSent;
});
}
while (totalByteSent < size)
{
int sendSize = static_cast<int>(std::min<std::size_t>(size - totalByteSent, std::numeric_limits<int>::max())); //< Handle very large send
int sentSize;
if (!SocketImpl::Send(m_handle, reinterpret_cast<const UInt8*>(buffer) + totalByteSent, sendSize, &sentSize, &m_lastError))
{
switch (m_lastError)
{
case SocketError_ConnectionClosed:
case SocketError_ConnectionRefused:
ChangeState(SocketState_NotConnected);
break;
default:
break;
}
return false;
}
totalByteSent += sentSize;
}
ChangeState(SocketState_Connected);
return true;
}
void TcpClient::OnClose()
{
TcpBase::OnClose();
m_peerAddress = IpAddress::Invalid;
}
void TcpClient::OnOpened()
{
TcpBase::OnOpened();
m_peerAddress = IpAddress::Invalid;
}
void TcpClient::Reset(SocketHandle handle, const IpAddress& peerAddress)
{
Open(handle);
m_peerAddress = peerAddress;
}
}

View File

@ -0,0 +1,63 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/TcpServer.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Network/TcpClient.hpp>
#include <limits>
#include <Nazara/Network/Debug.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif
namespace Nz
{
bool TcpServer::AcceptClient(TcpClient* newClient)
{
NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Server isn't listening");
NazaraAssert(newClient, "Invalid client socket");
IpAddress clientAddress;
SocketHandle handle = SocketImpl::Accept(m_handle, &clientAddress, &m_lastError);
if (handle != SocketImpl::InvalidHandle)
{
newClient->Reset(handle, clientAddress);
return true;
}
else
return false;
}
SocketState TcpServer::Listen(const IpAddress& address, unsigned int queueSize)
{
NazaraAssert(address.IsValid(), "Invalid address");
Open(address.GetProtocol());
SocketState state = SocketImpl::Listen(m_handle, address, queueSize, &m_lastError);
if (state == SocketState_Bound)
m_boundAddress = SocketImpl::QuerySocketAddress(m_handle);
ChangeState(state);
return state;
}
void TcpServer::OnClose()
{
TcpBase::OnClose();
m_boundAddress = IpAddress::Invalid;
}
void TcpServer::OnOpened()
{
TcpBase::OnOpened();
m_boundAddress = IpAddress::Invalid;
}
}

View File

@ -0,0 +1,79 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/UdpSocket.hpp>
#include <Nazara/Network/Debug.hpp>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif
namespace Nz
{
SocketState UdpSocket::Bind(const IpAddress& address)
{
NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created");
NazaraAssert(address.IsValid(), "Invalid address");
SocketState state = SocketImpl::Bind(m_handle, address, &m_lastError);
if (state == SocketState_Bound)
m_boundAddress = SocketImpl::QuerySocketAddress(m_handle);
ChangeState(state);
return state;
}
unsigned int UdpSocket::QueryMaxDatagramSize()
{
NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Socket hasn't been created");
return SocketImpl::QueryMaxDatagramSize(m_handle, &m_lastError);
}
bool UdpSocket::Receive(void* buffer, std::size_t size, IpAddress* from, std::size_t* received)
{
NazaraAssert(buffer && size > 0, "Invalid buffer");
int read;
if (!SocketImpl::ReceiveFrom(m_handle, buffer, static_cast<int>(size), from, &read, &m_lastError))
return false;
if (received)
*received = read;
return true;
}
bool UdpSocket::Send(const IpAddress& to, const void* buffer, std::size_t size, std::size_t* sent)
{
NazaraAssert(to.IsValid(), "Invalid ip address");
NazaraAssert(to.GetProtocol() == m_protocol, "IP Address has a different protocol than the socket");
NazaraAssert(buffer && size > 0, "Invalid buffer");
int byteSent;
if (!SocketImpl::SendTo(m_handle, buffer, static_cast<int>(size), to, &byteSent, &m_lastError))
return false;
if (sent)
*sent = byteSent;
return true;
}
void UdpSocket::OnClose()
{
m_boundAddress = IpAddress::Invalid;
ChangeState(SocketState_NotConnected);
}
void UdpSocket::OnOpened()
{
m_boundAddress = IpAddress::Invalid;
ChangeState(SocketState_NotConnected);
}
}

View File

@ -0,0 +1,240 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/Win32/IpAddressImpl.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Network/Win32/SocketImpl.hpp>
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
namespace Detail
{
#if NAZARA_CORE_WINDOWS_VISTA
using addrinfoImpl = addrinfoW;
int GetAddressInfo(const String& hostname, const String& service, const addrinfoImpl* hints, addrinfoImpl** results)
{
return GetAddrInfoW(hostname.GetWideString().c_str(), service.GetWideString().c_str(), &hints, &servinfo);
}
int GetHostnameInfo(sockaddr* socketAddress, socklen_t socketLen, String* hostname, String* service)
{
std::array<wchar_t, NI_MAXHOST> hostnameBuffer;
std::array<wchar_t, NI_MAXSERV> serviceBuffer;
int result = GetNameInfoW(socketAddress, socketLen, hostnameBuffer.data(), hostnameBuffer.size(), serviceBuffer.data(), serviceBuffer.size(), NI_NUMERICSERV);
if (result == 0)
{
if (hostname)
hostname->Set(hostnameBuffer.data());
if (service)
service->Set(serviceBuffer.data());
}
return result;
}
void FreeAddressInfo(addrinfoImpl* results)
{
FreeAddrInfoW(results);
}
#else
using addrinfoImpl = addrinfo;
int GetAddressInfo(const String& hostname, const String& service, const addrinfoImpl* hints, addrinfoImpl** results)
{
return getaddrinfo(hostname.GetConstBuffer(), service.GetConstBuffer(), hints, results);
}
int GetHostnameInfo(sockaddr* socketAddress, socklen_t socketLen, String* hostname, String* service)
{
std::array<char, NI_MAXHOST> hostnameBuffer;
std::array<char, NI_MAXSERV> serviceBuffer;
int result = getnameinfo(socketAddress, socketLen, hostnameBuffer.data(), hostnameBuffer.size(), serviceBuffer.data(), serviceBuffer.size(), NI_NUMERICSERV);
if (result == 0)
{
if (hostname)
hostname->Set(hostnameBuffer.data());
if (service)
service->Set(serviceBuffer.data());
}
return result;
}
void FreeAddressInfo(addrinfoImpl* results)
{
freeaddrinfo(results);
}
#endif
}
IpAddress IpAddressImpl::FromAddrinfo(const addrinfo* info)
{
switch (info->ai_family)
{
case AF_INET:
{
sockaddr_in* ipv4 = reinterpret_cast<sockaddr_in*>(info->ai_addr);
auto& rawIpV4 = ipv4->sin_addr.S_un.S_un_b;
return IpAddress(rawIpV4.s_b1, rawIpV4.s_b2, rawIpV4.s_b3, rawIpV4.s_b4, ntohs(ipv4->sin_port));
}
case AF_INET6:
{
sockaddr_in6* ipv6 = reinterpret_cast<sockaddr_in6*>(info->ai_addr);
auto& rawIpV6 = ipv6->sin6_addr.u.Word;
return IpAddress(rawIpV6[0], rawIpV6[1], rawIpV6[2], rawIpV6[3], rawIpV6[4], rawIpV6[5], rawIpV6[6], rawIpV6[7], ntohs(ipv6->sin6_port));
}
}
return IpAddress();
}
IpAddress IpAddressImpl::FromAddrinfo(const addrinfoW* info)
{
switch (info->ai_family)
{
case AF_INET:
{
sockaddr_in* ipv4 = reinterpret_cast<sockaddr_in*>(info->ai_addr);
return FromSockAddr(ipv4);
}
case AF_INET6:
{
sockaddr_in6* ipv6 = reinterpret_cast<sockaddr_in6*>(info->ai_addr);
return FromSockAddr(ipv6);
}
}
return IpAddress();
}
IpAddress IpAddressImpl::FromSockAddr(const sockaddr* address)
{
switch (address->sa_family)
{
case AF_INET:
return FromSockAddr(reinterpret_cast<const sockaddr_in*>(address));
case AF_INET6:
return FromSockAddr(reinterpret_cast<const sockaddr_in6*>(address));
}
return IpAddress();
}
IpAddress IpAddressImpl::FromSockAddr(const sockaddr_in* addressv4)
{
auto& rawIpV4 = addressv4->sin_addr.S_un.S_un_b;
return IpAddress(rawIpV4.s_b1, rawIpV4.s_b2, rawIpV4.s_b3, rawIpV4.s_b4, ntohs(addressv4->sin_port));
}
IpAddress IpAddressImpl::FromSockAddr(const sockaddr_in6* addressv6)
{
auto& rawIpV6 = addressv6->sin6_addr.u.Word;
return IpAddress(rawIpV6[0], rawIpV6[1], rawIpV6[2], rawIpV6[3], rawIpV6[4], rawIpV6[5], rawIpV6[6], rawIpV6[7], ntohs(addressv6->sin6_port));
}
bool IpAddressImpl::ResolveAddress(const IpAddress& ipAddress, String* hostname, String* service)
{
SockAddrBuffer socketAddress;
socklen_t socketAddressLen = ToSockAddr(ipAddress, socketAddress.data());
return Detail::GetHostnameInfo(reinterpret_cast<sockaddr*>(socketAddress.data()), socketAddressLen, hostname, service) == 0;
}
std::vector<HostnameInfo> IpAddressImpl::ResolveHostname(NetProtocol procol, const String& hostname, const String& service)
{
std::vector<HostnameInfo> results;
Detail::addrinfoImpl hints;
std::memset(&hints, 0, sizeof(Detail::addrinfoImpl));
hints.ai_family = SocketImpl::TranslateNetProtocolToAF(procol);
hints.ai_socktype = SOCK_STREAM;
Detail::addrinfoImpl* servinfo;
int rv;
if ((rv = Detail::GetAddressInfo(hostname, service, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return results;
}
CallOnExit onExit([servinfo]()
{
Detail::FreeAddressInfo(servinfo);
});
// loop through all the results and connect to the first we can
for (Detail::addrinfoImpl* p = servinfo; p != nullptr; p = p->ai_next)
{
HostnameInfo result;
result.address = FromAddrinfo(p);
result.canonicalName = String::Unicode(p->ai_canonname);
result.family = p->ai_family;
result.flags = p->ai_flags;
result.socketType = p->ai_socktype;
results.push_back(result);
}
return results;
}
socklen_t IpAddressImpl::ToSockAddr(const IpAddress& ipAddress, void* buffer)
{
if (ipAddress.IsValid())
{
switch (ipAddress.GetProtocol())
{
case NetProtocol_IPv4:
{
sockaddr_in* socketAddress = reinterpret_cast<sockaddr_in*>(buffer);
std::memset(socketAddress, 0, sizeof(sockaddr_in));
socketAddress->sin_family = AF_INET;
socketAddress->sin_port = htons(ipAddress.GetPort());
socketAddress->sin_addr.S_un.S_addr = htonl(ipAddress.ToUInt32());
return sizeof(sockaddr_in);
}
case NetProtocol_IPv6:
{
sockaddr_in6* socketAddress = reinterpret_cast<sockaddr_in6*>(buffer);
std::memset(socketAddress, 0, sizeof(sockaddr_in6));
socketAddress->sin6_family = AF_INET6;
socketAddress->sin6_port = htons(ipAddress.GetPort());
IpAddress::IPv6 address = ipAddress.ToIPv6();
for (unsigned int i = 0; i < 8; ++i)
socketAddress->sin6_addr.u.Word[i] = htons(address[i]);
IN6_ADDR any = in6addr_any;
return sizeof(sockaddr_in6);
}
default:
NazaraInternalError("Unhandled ip protocol (0x" + String::Number(ipAddress.GetProtocol()) + ')');
break;
}
}
NazaraError("Invalid ip address");
return 0;
}
}

View File

@ -0,0 +1,30 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/IpAddress.hpp>
#include <winsock2.h>
#include <ws2tcpip.h>
namespace Nz
{
class IpAddressImpl
{
public:
using SockAddrBuffer = std::array<UInt8, sizeof(sockaddr_in6)>;
IpAddressImpl() = delete;
~IpAddressImpl() = delete;
static IpAddress FromAddrinfo(const addrinfo* info);
static IpAddress FromAddrinfo(const addrinfoW* info);
static IpAddress FromSockAddr(const sockaddr* address);
static IpAddress FromSockAddr(const sockaddr_in* addressv4);
static IpAddress FromSockAddr(const sockaddr_in6* addressv6);
static bool ResolveAddress(const IpAddress& ipAddress, String* hostname, String* service);
static std::vector<HostnameInfo> ResolveHostname(NetProtocol procol, const String& hostname, const String& service);
static socklen_t ToSockAddr(const IpAddress& ipAddress, void* buffer);
};
}

View File

@ -0,0 +1,639 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/Win32/SocketImpl.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Network/Win32/IpAddressImpl.hpp>
#include <Mstcpip.h>
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
SocketHandle SocketImpl::Accept(SocketHandle handle, IpAddress* address, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
IpAddressImpl::SockAddrBuffer nameBuffer;
int bufferLength = static_cast<int>(nameBuffer.size());
SocketHandle newClient = accept(handle, reinterpret_cast<sockaddr*>(&nameBuffer), &bufferLength);
if (newClient != InvalidHandle)
{
if (address)
*address = IpAddressImpl::FromSockAddr(reinterpret_cast<const sockaddr*>(&nameBuffer));
if (error)
*error = SocketError_NoError;
}
else
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
}
return newClient;
}
SocketState SocketImpl::Bind(SocketHandle handle, const IpAddress& address, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
NazaraAssert(address.IsValid(), "Invalid address");
IpAddressImpl::SockAddrBuffer nameBuffer;
int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data());
if (bind(handle, reinterpret_cast<const sockaddr*>(&nameBuffer), bufferLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return SocketState_NotConnected;
}
if (error)
*error = SocketError_NoError;
return SocketState_Bound;
}
SocketHandle SocketImpl::Create(NetProtocol protocol, SocketType type, SocketError* error)
{
NazaraAssert(protocol != NetProtocol_Any, "Any protocol is not supported for socket creation");
NazaraAssert(type <= SocketType_Max, "Type has value out of enum");
SocketHandle handle = socket(TranslateNetProtocolToAF(protocol), TranslateSocketTypeToSock(type), 0);
if (handle == InvalidHandle && error != nullptr)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return handle;
}
void SocketImpl::Close(SocketHandle handle)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
if (closesocket(handle) == SOCKET_ERROR)
NazaraWarning("Failed to close socket: " + Error::GetLastSystemError(WSAGetLastError()));
}
void SocketImpl::ClearErrorCode(SocketHandle handle)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
if (GetLastError(handle, nullptr) < 0)
NazaraWarning("Failed to clear socket error code: " + Error::GetLastSystemError(WSAGetLastError()));
}
SocketState SocketImpl::Connect(SocketHandle handle, const IpAddress& address, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
NazaraAssert(address.IsValid(), "Invalid address");
IpAddressImpl::SockAddrBuffer nameBuffer;
int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data());
if (error)
*error = SocketError_NoError;
// Clear socket error status
ClearErrorCode(handle);
if (connect(handle, reinterpret_cast<const sockaddr*>(nameBuffer.data()), bufferLength) == SOCKET_ERROR)
{
int errorCode = WSAGetLastError();
switch (errorCode) //< Check for "normal errors" first
{
case WSAEALREADY:
case WSAEWOULDBLOCK:
return SocketState_Connecting;
case WSAEISCONN:
return SocketState_Connected;
}
if (error)
{
if (errorCode == WSAEADDRNOTAVAIL)
*error = SocketError_ConnectionRefused; //< ConnectionRefused seems more legit than AddressNotAvailable in connect case
else
*error = TranslateWSAErrorToSocketError(errorCode);
}
return SocketState_NotConnected;
}
return SocketState_Connected;
}
SocketState SocketImpl::Connect(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error)
{
SocketState state = Connect(handle, address, error);
if (state == SocketState_Connecting)
{
// http://developerweb.net/viewtopic.php?id=3196
fd_set localSet;
FD_ZERO(&localSet);
FD_SET(handle, &localSet);
timeval tv;
tv.tv_sec = static_cast<long>(msTimeout / 1000ULL);
tv.tv_usec = static_cast<long>((msTimeout % 1000ULL) * 1000ULL);
int ret = select(0, nullptr, &localSet, &localSet, (msTimeout > 0) ? &tv : nullptr);
if (ret > 0)
{
int code = GetLastErrorCode(handle, error);
if (code < 0) //< GetLastSocketError() failed
return SocketState_NotConnected;
if (code)
{
if (error)
*error = TranslateWSAErrorToSocketError(code);
return SocketState_NotConnected;
}
}
else if (ret == 0)
{
if (error)
*error = SocketError_TimedOut;
return SocketState_NotConnected;
}
else
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return SocketState_NotConnected;
}
if (error)
*error = SocketError_NoError;
state = SocketState_Connected;
}
return state;
}
bool SocketImpl::Initialize()
{
int errorCode = WSAStartup(MAKEWORD(2, 2), &s_WSA);
if (errorCode != 0)
{
NazaraError("Failed to initialize Windows Socket 2.2: " + Error::GetLastSystemError(errorCode));
return false;
}
NazaraDebug("Initialized Windows Socket " + String::Number(LOBYTE(s_WSA.wVersion)) + '.' + String::Number(HIBYTE(s_WSA.wVersion)) + " (" + String(s_WSA.szDescription) + ')');
return true;
}
SocketError SocketImpl::GetLastError(SocketHandle handle, SocketError* error)
{
int code = GetLastErrorCode(handle, error);
if (code < 0)
return SocketError_Internal;
return TranslateWSAErrorToSocketError(code);
}
int SocketImpl::GetLastErrorCode()
{
return WSAGetLastError();
}
int SocketImpl::GetLastErrorCode(SocketHandle handle, SocketError* error)
{
int code;
int codeLength = sizeof(code);
if (getsockopt(handle, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&code), &codeLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return -1;
}
if (error)
*error = SocketError_NoError;
return code;
}
SocketState SocketImpl::Listen(SocketHandle handle, const IpAddress& address, unsigned queueSize, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
NazaraAssert(address.IsValid(), "Invalid address");
IpAddressImpl::SockAddrBuffer nameBuffer;
int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data());
if (bind(handle, reinterpret_cast<const sockaddr*>(&nameBuffer), bufferLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return SocketState_NotConnected;
}
if (listen(handle, queueSize) == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return SocketState_NotConnected;
}
if (error)
*error = SocketError_NoError;
return SocketState_Bound;
}
unsigned int SocketImpl::QueryAvailableBytes(SocketHandle handle, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
u_long availableBytes;
if (ioctlsocket(handle, FIONREAD, &availableBytes) == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
availableBytes = 0;
}
if (error)
*error = SocketError_NoError;
return availableBytes;
}
unsigned int SocketImpl::QueryMaxDatagramSize(SocketHandle handle, SocketError* error)
{
unsigned int code;
int codeLength = sizeof(code);
if (getsockopt(handle, SOL_SOCKET, SO_MAX_MSG_SIZE, reinterpret_cast<char*>(&code), &codeLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return -1;
}
if (error)
*error = SocketError_NoError;
return code;
}
IpAddress SocketImpl::QueryPeerAddress(SocketHandle handle, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
IpAddressImpl::SockAddrBuffer nameBuffer;
int bufferLength = static_cast<int>(nameBuffer.size());
if (getpeername(handle, reinterpret_cast<sockaddr*>(nameBuffer.data()), &bufferLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return IpAddress();
}
if (error)
*error = SocketError_NoError;
return IpAddressImpl::FromSockAddr(reinterpret_cast<sockaddr*>(nameBuffer.data()));
}
IpAddress SocketImpl::QuerySocketAddress(SocketHandle handle, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
IpAddressImpl::SockAddrBuffer nameBuffer;
int bufferLength = static_cast<int>(nameBuffer.size());
if (getsockname(handle, reinterpret_cast<sockaddr*>(nameBuffer.data()), &bufferLength) == SOCKET_ERROR)
{
if (error)
{
int errorCode = WSAGetLastError();
if (errorCode == WSAEINVAL)
*error = SocketError_NoError;
else
*error = TranslateWSAErrorToSocketError(errorCode);
}
return IpAddress();
}
if (error)
*error = SocketError_NoError;
return IpAddressImpl::FromSockAddr(reinterpret_cast<sockaddr*>(nameBuffer.data()));
}
bool SocketImpl::Receive(SocketHandle handle, void* buffer, int length, int* read, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
NazaraAssert(buffer && length > 0, "Invalid buffer");
int byteRead = recv(handle, reinterpret_cast<char*>(buffer), length, 0);
if (byteRead == SOCKET_ERROR)
{
int errorCode = WSAGetLastError();
switch (errorCode)
{
case WSAEWOULDBLOCK:
{
// If we have no data and are not blocking, return true with 0 byte read
byteRead = 0;
break;
}
default:
{
if (error)
*error = TranslateWSAErrorToSocketError(errorCode);
return false; //< Error
}
}
}
else if (byteRead == 0)
{
if (error)
*error = SocketError_ConnectionClosed;
return false; //< Connection has been closed
}
if (read)
*read = byteRead;
if (error)
*error = SocketError_NoError;
return true;
}
bool SocketImpl::ReceiveFrom(SocketHandle handle, void* buffer, int length, IpAddress* from, int* read, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
NazaraAssert(buffer && length > 0, "Invalid buffer");
IpAddressImpl::SockAddrBuffer nameBuffer;
int bufferLength = static_cast<int>(nameBuffer.size());
IpAddress senderIp;
int byteRead = recvfrom(handle, reinterpret_cast<char*>(buffer), length, 0, reinterpret_cast<sockaddr*>(&nameBuffer), &bufferLength);
if (byteRead == SOCKET_ERROR)
{
int errorCode = WSAGetLastError();
switch (errorCode)
{
case WSAEWOULDBLOCK:
{
// If we have no data and are not blocking, return true with 0 byte read
byteRead = 0;
senderIp = IpAddress::Invalid;
break;
}
default:
{
if (error)
*error = TranslateWSAErrorToSocketError(errorCode);
return false; //< Error
}
}
}
else if (byteRead == 0)
{
if (error)
*error = SocketError_ConnectionClosed;
return false; //< Connection closed
}
else // else we received something
senderIp = IpAddressImpl::FromSockAddr(reinterpret_cast<const sockaddr*>(&nameBuffer));
if (from)
*from = senderIp;
if (read)
*read = byteRead;
if (error)
*error = SocketError_NoError;
return true;
}
bool SocketImpl::Send(SocketHandle handle, const void* buffer, int length, int* sent, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
NazaraAssert(buffer && length > 0, "Invalid buffer");
int byteSent = send(handle, reinterpret_cast<const char*>(buffer), length, 0);
if (byteSent == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return false; //< Error
}
if (sent)
*sent = byteSent;
if (error)
*error = SocketError_NoError;
return true;
}
bool SocketImpl::SendTo(SocketHandle handle, const void* buffer, int length, const IpAddress& to, int* sent, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
NazaraAssert(buffer && length > 0, "Invalid buffer");
IpAddressImpl::SockAddrBuffer nameBuffer;
int bufferLength = IpAddressImpl::ToSockAddr(to, nameBuffer.data());
int byteSent = sendto(handle, reinterpret_cast<const char*>(buffer), length, 0, reinterpret_cast<const sockaddr*>(nameBuffer.data()), bufferLength);
if (byteSent == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return false; //< Error
}
if (sent)
*sent = byteSent;
if (error)
*error = SocketError_NoError;
return true;
}
bool SocketImpl::SetBlocking(SocketHandle handle, bool blocking, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
u_long block = (blocking) ? 0 : 1;
if (ioctlsocket(handle, FIONBIO, &block) == SOCKET_ERROR)
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return false; //< Error
}
if (error)
*error = SocketError_NoError;
return true;
}
bool SocketImpl::SetKeepAlive(SocketHandle handle, bool enabled, UInt64 msTime, UInt64 msInterval, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
tcp_keepalive keepAlive;
keepAlive.onoff = (enabled) ? 1 : 0;
keepAlive.keepaliveinterval = static_cast<ULONG>(msInterval);
keepAlive.keepalivetime = static_cast<ULONG>(msTime);
DWORD dummy; //< byteReturned
if (!WSAIoctl(handle, SIO_KEEPALIVE_VALS, &keepAlive, sizeof(keepAlive), nullptr, 0, &dummy, nullptr, nullptr))
{
if (error)
*error = TranslateWSAErrorToSocketError(WSAGetLastError());
return false; //< Error
}
if (error)
*error = SocketError_NoError;
return true;
}
SocketError SocketImpl::TranslateWSAErrorToSocketError(int error)
{
switch (error)
{
case 0:
return SocketError_NoError;
// Engine error
case WSAEACCES:
case WSAEBADF:
case WSAEINVAL:
case WSAEFAULT:
case WSAENOTSOCK:
case WSAEPROTOTYPE:
case WSA_INVALID_HANDLE:
return SocketError_Internal;
case WSAEADDRNOTAVAIL:
case WSAEADDRINUSE:
return SocketError_AddressNotAvailable;
case WSAEAFNOSUPPORT:
case WSAEPFNOSUPPORT:
case WSAEOPNOTSUPP:
case WSAEPROTONOSUPPORT:
case WSAESOCKTNOSUPPORT:
return SocketError_NotSupported;
// Those are not errors and should have been handled before the call
case WSAEALREADY:
case WSAEISCONN:
case WSAEWOULDBLOCK:
return SocketError_Internal;
case WSAECONNREFUSED:
return SocketError_ConnectionRefused;
case WSAEMSGSIZE:
return SocketError_DatagramSize;
case WSAEMFILE:
case WSAENOBUFS:
case WSA_NOT_ENOUGH_MEMORY:
return SocketError_ResourceError;
case WSAENOTCONN:
case WSAESHUTDOWN:
return SocketError_ConnectionClosed;
case WSAEHOSTUNREACH:
return SocketError_UnreachableHost;
case WSAENETDOWN:
case WSAENETUNREACH:
return SocketError_NetworkError;
case WSANOTINITIALISED:
return SocketError_NotInitialized;
case WSAETIMEDOUT:
return SocketError_TimedOut;
}
NazaraWarning("Unhandled WinSock error: " + Error::GetLastSystemError(error) + " (" + String::Number(error) + ')');
return SocketError_Unknown;
}
int SocketImpl::TranslateNetProtocolToAF(NetProtocol protocol)
{
NazaraAssert(protocol <= NetProtocol_Max, "Protocol has value out of enum");
static int addressFamily[] = {
AF_UNSPEC, //< NetProtocol_Any
AF_INET, //< NetProtocol_IPv4
AF_INET6 //< NetProtocol_IPv6
};
static_assert(sizeof(addressFamily) / sizeof(int) == NetProtocol_Max + 1, "Address family array is incomplete");
return addressFamily[protocol];
}
int SocketImpl::TranslateSocketTypeToSock(SocketType type)
{
NazaraAssert(type <= SocketType_Max, "Socket type has value out of enum");
static int socketType[] = {
SOCK_RAW, //< SocketType_Raw
SOCK_STREAM, //< SocketType_TCP
SOCK_DGRAM //< SocketType_UDP
};
static_assert(sizeof(socketType) / sizeof(int) == SocketType_Max + 1, "Socket type array is incomplete");
return socketType[type];
}
void SocketImpl::Uninitialize()
{
WSACleanup();
}
SocketHandle SocketImpl::InvalidHandle = INVALID_SOCKET;
WSADATA SocketImpl::s_WSA;
}

View File

@ -0,0 +1,63 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Network/SocketHandle.hpp>
#include <Nazara/Network/Enums.hpp>
#include <Nazara/Network/IpAddress.hpp>
#include <winsock2.h>
namespace Nz
{
class SocketImpl
{
public:
SocketImpl() = delete;
~SocketImpl() = delete;
static SocketHandle Accept(SocketHandle handle, IpAddress* address, SocketError* error);
static SocketState Bind(SocketHandle handle, const IpAddress& address, SocketError* error);
static SocketHandle Create(NetProtocol protocol, SocketType type, SocketError* error);
static void ClearErrorCode(SocketHandle handle);
static void Close(SocketHandle handle);
static SocketState Connect(SocketHandle handle, const IpAddress& address, SocketError* error);
static SocketState Connect(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error);
static bool Initialize();
static SocketError GetLastError(SocketHandle handle, SocketError* error = nullptr);
static int GetLastErrorCode();
static int GetLastErrorCode(SocketHandle handle, SocketError* error = nullptr);
static SocketState Listen(SocketHandle handle, const IpAddress& address, unsigned queueSize, SocketError* error);
static unsigned int QueryAvailableBytes(SocketHandle handle, SocketError* error = nullptr);
static unsigned int QueryMaxDatagramSize(SocketHandle handle, SocketError* error = nullptr);
static IpAddress QueryPeerAddress(SocketHandle handle, SocketError* error = nullptr);
static IpAddress QuerySocketAddress(SocketHandle handle, SocketError* error = nullptr);
static bool Receive(SocketHandle handle, void* buffer, int length, int* read, SocketError* error);
static bool ReceiveFrom(SocketHandle handle, void* buffer, int length, IpAddress* from, int* read, SocketError* error);
static bool Send(SocketHandle handle, const void* buffer, int length, int* sent, SocketError* error);
static bool SendTo(SocketHandle handle, const void* buffer, int length, const IpAddress& to, int* sent, SocketError* error);
static bool SetBlocking(SocketHandle handle, bool blocking, SocketError* error = nullptr);
static bool SetKeepAlive(SocketHandle handle, bool enabled, UInt64 msTime, UInt64 msInterval, SocketError* error = nullptr);
static SocketError TranslateWSAErrorToSocketError(int error);
static int TranslateNetProtocolToAF(NetProtocol protocol);
static int TranslateSocketTypeToSock(SocketType type);
static void Uninitialize();
static SocketHandle InvalidHandle;
private:
static WSADATA s_WSA;
};
}