Initial socket abstraction

This commit is contained in:
Joshua Ashton 2020-07-24 10:14:08 +01:00
parent 7481419cb0
commit f67a7b697c
2 changed files with 193 additions and 0 deletions

172
src/util/SocketUtil.cpp Normal file
View File

@ -0,0 +1,172 @@
#include "SocketUtil.h"
#ifdef _WIN32
# include <winsock2.h>
# include <Ws2tcpip.h>
#else
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <unistd.h>
#endif
namespace Feather::Sockets
{
class TCPSocketHandle
{
public:
TCPSocketHandle()
{
m_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
// Can't create IPv6 socket? Create an IPv4 one.
if (!(m_ipv6 = IsValid()))
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
}
~TCPSocketHandle()
{
Close();
}
bool IsIPV6() const { return m_ipv6; }
bool MarkReusable()
{
constexpr uint32_t one = 1;
return setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*>(&one),
socklen_t(sizeof(one))) == 0;
}
bool MarkDualBind()
{
constexpr uint32_t zero = 0;
return setsockopt(m_socket, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const char*>(&zero),
sizeof(zero)) == 0;
}
bool Bind(uint16_t port)
{
sockaddr_in6 name_ipv6 = {};
name_ipv6.sin6_family = AF_INET6;
name_ipv6.sin6_port = ntohs(port);
sockaddr_in name_ipv4 = {};
name_ipv4.sin_family = AF_INET;
name_ipv4.sin_port = ntohs(port);
return m_ipv6
? BindGeneric(name_ipv6)
: BindGeneric(name_ipv4);
}
bool Listen()
{
return listen(m_socket, SOMAXCONN) == 0;
}
#ifdef _WIN32
bool IsValid() const { return m_socket != INVALID_SOCKET; }
bool MarkNonBlocking()
{
unsigned long one = 1;
return ioctlsocket(m_socket, FIONBIO, &one) != SOCKET_ERROR;
}
bool Close()
{
return shutdown(m_socket, SD_BOTH) == 0 && closesocket(m_socket) == 0;
}
static bool InitSocketSystem()
{
WSADATA wsa_data;
return WSAStartup(MAKEWORD(1, 1), &wsa_data) == 0;
}
static bool CloseSocketSystem()
{
return WSACleanup() == 0;
}
using SocketType = SOCKET;
#else
bool IsValid() const { return m_socket >= 0; }
bool MarkNonBlocking()
{
return fcntl(fd, F_SETFL, O_NONBLOCK) != -1;
}
bool Close()
{
return shutdown(m_socket, SHUT_RDWR) == 0 && close(m_socket) == 0;
}
static bool InitSocketSystem()
{
return true;
}
static bool CloseSocketSystem()
{
return true;
}
using SocketType = int;
#endif
private:
template <typename T>
bool BindGeneric(const T& name)
{
return bind(m_socket, reinterpret_cast<const sockaddr*>(&name), sizeof(name)) == 0;
}
SocketType m_socket;
bool m_ipv6;
};
TCPSocket::TCPSocket(uint16_t port)
{
auto socket = std::make_unique<TCPSocketHandle>();
if (!socket->IsValid())
return;
if (!socket->MarkReusable())
return;
if (socket->IsIPV6() && !socket->MarkDualBind())
return;
if (!socket->Bind(port))
return;
if (socket->Listen())
return;
m_socket = std::move(socket);
}
TCPSocket::~TCPSocket()
{
}
namespace
{
class SocketInitializer
{
public:
SocketInitializer() { TCPSocketHandle::InitSocketSystem(); }
~SocketInitializer() { TCPSocketHandle::CloseSocketSystem(); }
};
static SocketInitializer s_socketInitializer;
}
}

21
src/util/SocketUtil.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <cstdint>
#include <memory>
namespace Feather::Sockets
{
class TCPSocketHandle;
class TCPSocket
{
public:
TCPSocket(uint16_t port);
~TCPSocket();
inline bool IsValid() const { return m_socket != nullptr; }
private:
std::unique_ptr<TCPSocketHandle> m_socket;
};
}