Network: First commit
Former-commit-id: ec8697acc51569f5043e4f70e4cf42f1c5dc487c
This commit is contained in:
240
src/Nazara/Network/Win32/IpAddressImpl.cpp
Normal file
240
src/Nazara/Network/Win32/IpAddressImpl.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
30
src/Nazara/Network/Win32/IpAddressImpl.hpp
Normal file
30
src/Nazara/Network/Win32/IpAddressImpl.hpp
Normal 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);
|
||||
};
|
||||
}
|
||||
639
src/Nazara/Network/Win32/SocketImpl.cpp
Normal file
639
src/Nazara/Network/Win32/SocketImpl.cpp
Normal 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;
|
||||
}
|
||||
63
src/Nazara/Network/Win32/SocketImpl.hpp
Normal file
63
src/Nazara/Network/Win32/SocketImpl.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user