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,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;
};
}