First implementation of Posix Network

Former-commit-id: 2b73870d8eef4dc92038224164396390ac43df46
This commit is contained in:
Gawaboumga 2015-12-30 15:20:07 +01:00
parent 7f4a7c1012
commit 1d04ac8f13
10 changed files with 1169 additions and 2 deletions

View File

@ -9,6 +9,8 @@
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#elif defined(NAZARA_PLATFORM_POSIX)
#include <Nazara/Network/Posix/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif

View File

@ -12,6 +12,8 @@
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/IpAddressImpl.hpp>
#elif defined(NAZARA_PLATFORM_POSIX)
#include <Nazara/Network/Posix/IpAddressImpl.hpp>
#else
#error Missing implementation: Network
#endif

View File

@ -12,6 +12,8 @@
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#elif defined(NAZARA_PLATFORM_POSIX)
#include <Nazara/Network/Posix/SocketImpl.hpp>
#else
#error Missing implementation: Network
#endif

View File

@ -0,0 +1,309 @@
// 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/Posix/IpAddressImpl.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Network/Posix/SocketImpl.hpp>
#include <cstring>
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
namespace Detail
{
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, int flags)
{
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(), flags);
if (result == 0)
{
if (hostname)
hostname->Set(hostnameBuffer.data());
if (service)
service->Set(serviceBuffer.data());
}
return result;
}
void FreeAddressInfo(addrinfoImpl* results)
{
freeaddrinfo(results);
}
IpAddress::IPv4 convertSockaddrToIPv4(const in_addr& addr)
{
union byteToInt
{
UInt8 b[sizeof(uint32_t)];
uint32_t i;
};
byteToInt hostOrder;
hostOrder.i = ntohl(addr.s_addr);
return { hostOrder.b[3], hostOrder.b[2], hostOrder.b[1], hostOrder.b[0] };
}
IpAddress::IPv6 convertSockaddr6ToIPv6(const in6_addr& addr)
{
union byteToInt
{
UInt8 b[sizeof(uint32_t)];
uint32_t i;
};
IpAddress::IPv6 ipv6Addr;
for (auto i = 0; i < 4; ++i)
{
byteToInt hostOrder;
hostOrder.i = 0;
std::copy(addr.s6_addr + 4 * i, addr.s6_addr + 4 * (i + 1), hostOrder.b);
ipv6Addr[2 * i] = (hostOrder.b[3] << 8) + hostOrder.b[2];
ipv6Addr[2 * i + 1] = (hostOrder.b[1] << 8) + hostOrder.b[0];
}
return ipv6Addr;
}
}
IpAddress IpAddressImpl::FromAddrinfo(const addrinfo* 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::Invalid;
}
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::Invalid;
}
IpAddress IpAddressImpl::FromSockAddr(const sockaddr_in* addressv4)
{
IpAddress::IPv4 ip4Address = Detail::convertSockaddrToIPv4(addressv4->sin_addr);
return IpAddress(ip4Address, ntohs(addressv4->sin_port));
}
IpAddress IpAddressImpl::FromSockAddr(const sockaddr_in6* addressv6)
{
IpAddress::IPv6 ip6Address = Detail::convertSockaddr6ToIPv6(addressv6->sin6_addr);
return IpAddress(ip6Address, ntohs(addressv6->sin6_port));
}
bool IpAddressImpl::ResolveAddress(const IpAddress& ipAddress, String* hostname, String* service, ResolveError* error)
{
SockAddrBuffer socketAddress;
socklen_t socketAddressLen = ToSockAddr(ipAddress, socketAddress.data());
if (Detail::GetHostnameInfo(reinterpret_cast<sockaddr*>(socketAddress.data()), socketAddressLen, hostname, service, NI_NUMERICSERV) != 0)
{
if (error)
*error = TranslateEAIErrorToResolveError(errno);
return false;
}
if (error)
*error = ResolveError_NoError;
return true;
}
std::vector<HostnameInfo> IpAddressImpl::ResolveHostname(NetProtocol procol, const String& hostname, const String& service, ResolveError* error)
{
std::vector<HostnameInfo> results;
Detail::addrinfoImpl hints;
std::memset(&hints, 0, sizeof(Detail::addrinfoImpl));
hints.ai_family = SocketImpl::TranslateNetProtocolToAF(procol);
hints.ai_flags = AI_CANONNAME;
hints.ai_socktype = SOCK_STREAM;
Detail::addrinfoImpl* servinfo;
if (Detail::GetAddressInfo(hostname, service, &hints, &servinfo) != 0)
{
if (error)
*error = TranslateEAIErrorToResolveError(errno);
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.protocol = TranslatePFToNetProtocol(p->ai_family);
result.socketType = TranslateSockToNetProtocol(p->ai_socktype);
results.push_back(result);
}
if (error)
*error = ResolveError_NoError;
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_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)
{
UInt16 networkOrder = htons(address[i]);
socketAddress->sin6_addr.s6_addr[2 * i] = networkOrder / 256;
socketAddress->sin6_addr.s6_addr[2 * i + 1] = networkOrder % 256;
}
return sizeof(sockaddr_in6);
}
default:
NazaraInternalError("Unhandled ip protocol (0x" + String::Number(ipAddress.GetProtocol()) + ')');
break;
}
}
NazaraError("Invalid ip address");
return 0;
}
NetProtocol IpAddressImpl::TranslatePFToNetProtocol(int family)
{
switch (family)
{
case PF_INET:
return NetProtocol_IPv4;
case PF_INET6:
return NetProtocol_IPv6;
default:
return NetProtocol_Unknown;
}
}
SocketType IpAddressImpl::TranslateSockToNetProtocol(int socketType)
{
switch (socketType)
{
case SOCK_STREAM:
return SocketType_TCP;
case SOCK_DGRAM:
return SocketType_UDP;
case SOCK_RAW:
return SocketType_Raw;
default:
return SocketType_Unknown;
}
}
ResolveError IpAddressImpl::TranslateEAIErrorToResolveError(int error)
{
// http://man7.org/linux/man-pages/man3/gai_strerror.3.html
switch (error)
{
case 0:
return ResolveError_NoError;
// Engine error
case EAI_BADFLAGS:
case EAI_SYSTEM:
return ResolveError_Internal;
case EAI_FAMILY:
case EAI_SERVICE:
case EAI_SOCKTYPE:
return ResolveError_ProtocolNotSupported;
case EAI_NONAME:
return ResolveError_NotFound;
case EAI_FAIL:
return ResolveError_NonRecoverable;
case EAI_NODATA:
return ResolveError_NotInitialized;
case EAI_MEMORY:
return ResolveError_ResourceError;
case EAI_AGAIN:
return ResolveError_TemporaryFailure;
}
NazaraWarning("Unhandled EAI error: " + Error::GetLastSystemError(error) + " (" + String::Number(error) + ") as " + gai_strerror(error));
return ResolveError_Unknown;
}
}

View File

@ -0,0 +1,32 @@
// 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 <netdb.h>
#include <netinet/in.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 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, ResolveError* error);
static std::vector<HostnameInfo> ResolveHostname(NetProtocol procol, const String& hostname, const String& service, ResolveError* error);
static socklen_t ToSockAddr(const IpAddress& ipAddress, void* buffer);
static NetProtocol TranslatePFToNetProtocol(int family);
static SocketType TranslateSockToNetProtocol(int socketType);
static ResolveError TranslateEAIErrorToResolveError(int error);
};
}

View File

@ -0,0 +1,746 @@
// 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/Posix/SocketImpl.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/Network/Posix/IpAddressImpl.hpp>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>
#include <Nazara/Network/Debug.hpp>
namespace Nz
{
constexpr int SOCKET_ERROR = -1;
SocketHandle SocketImpl::Accept(SocketHandle handle, IpAddress* address, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
IpAddressImpl::SockAddrBuffer nameBuffer;
socklen_t bufferLength = sizeof(sockaddr_in);
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 = TranslateErrnoToResolveError(GetLastErrorCode());
}
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 = TranslateErrnoToResolveError(GetLastErrorCode());
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 = TranslateErrnoToResolveError(GetLastErrorCode());
return handle;
}
void SocketImpl::Close(SocketHandle handle)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
if (close(handle) == SOCKET_ERROR)
NazaraWarning("Failed to close socket: " + Error::GetLastSystemError(GetLastErrorCode()));
}
void SocketImpl::ClearErrorCode(SocketHandle handle)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
if (GetLastError(handle, nullptr) < 0)
NazaraWarning("Failed to clear socket error code: " + Error::GetLastSystemError(GetLastErrorCode()));
}
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 = GetLastErrorCode();
switch (errorCode) //< Check for "normal errors" first
{
case EALREADY:
case EINPROGRESS:
return SocketState_Connecting;
case EISCONN:
return SocketState_Connected;
}
if (error)
{
if (errorCode == EADDRNOTAVAIL)
*error = SocketError_ConnectionRefused; //< ConnectionRefused seems more legit than AddressNotAvailable in connect case
else
*error = TranslateErrnoToResolveError(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 == SOCKET_ERROR)
{
int code = GetLastErrorCode(handle, error);
if (code < 0) //< GetLastErrorCode() failed
return SocketState_NotConnected;
if (code)
{
if (error)
*error = TranslateErrnoToResolveError(code);
return SocketState_NotConnected;
}
}
else if (ret == 0)
{
if (error)
*error = SocketError_TimedOut;
return SocketState_NotConnected;
}
else
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return SocketState_NotConnected;
}
if (error)
*error = SocketError_NoError;
state = SocketState_Connected;
}
return state;
}
bool SocketImpl::Initialize()
{
return true;
}
SocketError SocketImpl::GetLastError(SocketHandle handle, SocketError* error)
{
int code = GetLastErrorCode(handle, error);
if (code < 0)
return SocketError_Internal;
return TranslateErrnoToResolveError(code);
}
int SocketImpl::GetLastErrorCode()
{
return errno;
}
int SocketImpl::GetLastErrorCode(SocketHandle handle, SocketError* error)
{
int code;
unsigned int codeLength = sizeof(code);
if (getsockopt(handle, SOL_SOCKET, SO_ERROR, &code, &codeLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
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 = TranslateErrnoToResolveError(GetLastErrorCode());
return SocketState_NotConnected;
}
if (listen(handle, queueSize) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
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 (ioctl(handle, FIONREAD, &availableBytes) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return 0;
}
if (error)
*error = SocketError_NoError;
return availableBytes;
}
bool SocketImpl::QueryBroadcasting(SocketHandle handle, SocketError* error)
{
bool code;
unsigned int codeLength = sizeof(code);
if (getsockopt(handle, SOL_SOCKET, SO_BROADCAST, &code, &codeLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return false;
}
if (error)
*error = SocketError_NoError;
return code;
}
bool SocketImpl::QueryKeepAlive(SocketHandle handle, SocketError* error)
{
bool code;
unsigned int codeLength = sizeof(code);
if (getsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &code, &codeLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return false;
}
if (error)
*error = SocketError_NoError;
return code;
}
unsigned int SocketImpl::QueryMaxDatagramSize(SocketHandle handle, SocketError* error)
{
unsigned int code;
unsigned int codeLength = sizeof(code);
if (getsockopt(handle, IPPROTO_IP, IP_MTU, &code, &codeLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return -1;
}
if (error)
*error = SocketError_NoError;
return code;
}
bool SocketImpl::QueryNoDelay(SocketHandle handle, SocketError* error)
{
bool code;
unsigned int codeLength = sizeof(code);
if (getsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &code, &codeLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return false;
}
if (error)
*error = SocketError_NoError;
return code;
}
IpAddress SocketImpl::QueryPeerAddress(SocketHandle handle, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
IpAddressImpl::SockAddrBuffer nameBuffer;
socklen_t bufferLength = sizeof(sockaddr_in);
if (getpeername(handle, reinterpret_cast<sockaddr*>(nameBuffer.data()), &bufferLength) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
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;
socklen_t bufferLength = sizeof(sockaddr_in);
if (getsockname(handle, reinterpret_cast<sockaddr*>(nameBuffer.data()), &bufferLength) == SOCKET_ERROR)
{
if (error)
{
int errorCode = GetLastErrorCode();
if (errorCode == EINVAL)
*error = SocketError_NoError;
else
*error = TranslateErrnoToResolveError(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 = GetLastErrorCode();
switch (errorCode)
{
case EWOULDBLOCK:
{
// If we have no data and are not blocking, return true with 0 byte read
byteRead = 0;
break;
}
default:
{
if (error)
*error = TranslateErrnoToResolveError(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;
socklen_t bufferLength = sizeof(sockaddr_in);
IpAddress senderIp;
int byteRead = recvfrom(handle, buffer, length, 0, reinterpret_cast<sockaddr*>(&nameBuffer), &bufferLength);
if (byteRead == SOCKET_ERROR)
{
int errorCode = GetLastErrorCode();
switch (errorCode)
{
case EWOULDBLOCK:
{
// 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 = TranslateErrnoToResolveError(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 = TranslateErrnoToResolveError(GetLastErrorCode());
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 = TranslateErrnoToResolveError(GetLastErrorCode());
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 (ioctl(handle, FIONBIO, &block) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return false; //< Error
}
if (error)
*error = SocketError_NoError;
return true;
}
bool SocketImpl::SetBroadcasting(SocketHandle handle, bool broadcasting, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
bool option = broadcasting;
if (setsockopt(handle, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<const char*>(&option), sizeof(option)) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
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");
int keepAlive = enabled ? 1 : 0;
int keepIdle = msTime / 1000; // Linux works with seconds.
int keepInterval = msInterval / 1000; // Linux works with seconds.
if (setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &keepAlive , sizeof(keepAlive)) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return false; //< Error
}
if (setsockopt(handle, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(keepIdle)) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return false; //< Error
}
if (setsockopt(handle, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(keepInterval)) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return false; //< Error
}
if (error)
*error = SocketError_NoError;
return true;
}
bool SocketImpl::SetNoDelay(SocketHandle handle, bool nodelay, SocketError* error)
{
NazaraAssert(handle != InvalidHandle, "Invalid handle");
int option = nodelay ? 1 : 0;
if (setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option)) == SOCKET_ERROR)
{
if (error)
*error = TranslateErrnoToResolveError(GetLastErrorCode());
return false; //< Error
}
if (error)
*error = SocketError_NoError;
return true;
}
SocketError SocketImpl::TranslateErrnoToResolveError(int error)
{
switch (error)
{
case 0:
return SocketError_NoError;
// Engine error
case EACCES:
case EBADF:
case EINVAL:
case EFAULT:
case ENOTSOCK:
case EPROTOTYPE:
return SocketError_Internal;
case EADDRNOTAVAIL:
case EADDRINUSE:
return SocketError_AddressNotAvailable;
case EAFNOSUPPORT:
case EPFNOSUPPORT:
case EOPNOTSUPP:
case EPROTONOSUPPORT:
case ESOCKTNOSUPPORT:
return SocketError_NotSupported;
// Those are not errors and should have been handled before the call
case EALREADY:
case EISCONN:
case EWOULDBLOCK:
return SocketError_Internal;
case ECONNREFUSED:
return SocketError_ConnectionRefused;
case EMSGSIZE:
return SocketError_DatagramSize;
case EMFILE:
case ENOBUFS:
case ENOMEM:
return SocketError_ResourceError;
case ENOTCONN:
case ESHUTDOWN:
return SocketError_ConnectionClosed;
case EHOSTUNREACH:
return SocketError_UnreachableHost;
case ENETDOWN:
case ENETUNREACH:
return SocketError_NetworkError;
case ENODATA:
return SocketError_NotInitialized;
case ETIMEDOUT:
return SocketError_TimedOut;
}
NazaraWarning("Unhandled POSIX 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
-1 //< NetProtocol_Unknown
};
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
-1 //< SocketType_Unknown
};
static_assert(sizeof(socketType) / sizeof(int) == SocketType_Max + 1, "Socket type array is incomplete");
return socketType[type];
}
void SocketImpl::Uninitialize()
{
}
SocketHandle SocketImpl::InvalidHandle = -1;
SocketImpl::socketID SocketImpl::s_socket;
}

View File

@ -0,0 +1,68 @@
// 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>
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 bool QueryBroadcasting(SocketHandle handle, SocketError* error = nullptr);
static bool QueryKeepAlive(SocketHandle handle, SocketError* error = nullptr);
static unsigned int QueryMaxDatagramSize(SocketHandle handle, SocketError* error = nullptr);
static bool QueryNoDelay(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 SetBroadcasting(SocketHandle handle, bool broadcasting, SocketError* error = nullptr);
static bool SetKeepAlive(SocketHandle handle, bool enabled, UInt64 msTime, UInt64 msInterval, SocketError* error = nullptr);
static bool SetNoDelay(SocketHandle handle, bool nodelay, SocketError* error = nullptr);
static SocketError TranslateErrnoToResolveError(int error);
static int TranslateNetProtocolToAF(NetProtocol protocol);
static int TranslateSocketTypeToSock(SocketType type);
static void Uninitialize();
static SocketHandle InvalidHandle;
private:
using socketID = int;
static socketID s_socket;
};
}

View File

@ -10,6 +10,8 @@
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#elif defined(NAZARA_PLATFORM_POSIX)
#include <Nazara/Network/Posix/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif

View File

@ -11,6 +11,8 @@
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#elif defined(NAZARA_PLATFORM_POSIX)
#include <Nazara/Network/Posix/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif

View File

@ -7,6 +7,8 @@
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Network/Win32/SocketImpl.hpp>
#elif defined(NAZARA_PLATFORM_POSIX)
#include <Nazara/Network/Posix/SocketImpl.hpp>
#else
#error Missing implementation: Socket
#endif