I'm coding a chat application from scratch in modern C++. I'm currently building an interface for interacting with sockets. I want it to be cross platform. Am I approaching platform independence correctly? Also is my error handling acceptable? Please tell me what is wrong and what should I improve or change.
Socket.h
#include <string_view>
#include <cstdint>
#include <optional>
#include <memory>
bool InitNetworking();
class TcpSocket
{
public:
TcpSocket() = default;
virtual ~TcpSocket() = default;
TcpSocket(const TcpSocket &) = delete;
TcpSocket(TcpSocket &&) = delete;
void operator=(const TcpSocket &) = delete;
void operator=(TcpSocket &&) = delete;
virtual void *GetHandle() = 0;
virtual bool Connect(std::string_view ip_address, std::uint16_t port) = 0;
virtual std::shared_ptr<TcpSocket> Accept(std::string_view ip_address, std::uint16_t port) = 0;
virtual int Send(TcpSocket &sock, std::string_view message) = 0;
virtual std::string Receive() = 0;
static std::unique_ptr<TcpSocket> Create(std::string_view name, void *socket_handle = nullptr);
private:
};
Socket.cpp
#include "Socket.h"
#include "Win32TcpSocket.h"
#ifdef _WIN32
static WSADATA wsa;
bool InitNetworking()
{
if (!WSAStartup(MAKEWORD(2, 2), &wsa))
return true;
return false;
}
std::unique_ptr<TcpSocket> TcpSocket::Create(std::string_view name, void *socket_handle)
{
auto ptr = Win32TcpSocket::Create(name, (SOCKET)socket_handle);
return ptr;
}
#endif
Win32TcpSocket.h
#include <string_view>
#include <cstdint>
#include <memory>
#include "Socket.h"
#include <WinSock2.h>
#include <WS2tcpip.h>
class Win32TcpSocket : public TcpSocket
{
public:
Win32TcpSocket() = default;
virtual ~Win32TcpSocket();
virtual void *GetHandle() override;
virtual bool Connect(std::string_view ip_address, std::uint16_t port) override;
virtual std::shared_ptr<TcpSocket> Accept(std::string_view ip_address, std::uint16_t port) override;
virtual int Send(TcpSocket &sock, std::string_view message) override;
virtual std::string Receive() override;
static std::unique_ptr<Win32TcpSocket> Create(std::string_view name, SOCKET socket_handle = 0);
private:
SOCKET m_socket = INVALID_SOCKET;
std::string_view m_name = "";
};
Win32TcpSocket.cpp
#include <iostream>
#include <exception>
#include <stdexcept>
std::unique_ptr<Win32TcpSocket> Win32TcpSocket::Create(std::string_view name, SOCKET socket_handle)
{
auto ptr = std::make_unique<Win32TcpSocket>();
if (!socket_handle)
{
ptr->m_socket = socket(AF_INET, SOCK_STREAM, 0);
if (ptr->m_socket == INVALID_SOCKET)
throw std::runtime_error("Invalid socket!");
}
else
{
ptr->m_socket = socket_handle;
}
ptr->m_name = name;
return ptr;
}
void *Win32TcpSocket::GetHandle()
{
return (void*)m_socket;
}
Win32TcpSocket::~Win32TcpSocket()
{
if(m_socket != INVALID_SOCKET)
closesocket(m_socket);
m_socket = INVALID_SOCKET;
}
bool Win32TcpSocket::Connect(std::string_view ip_address, std::uint16_t port)
{
sockaddr_in hint = {};
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, ip_address.data(), &hint.sin_addr);
int connect_result = connect(m_socket, (sockaddr *)&hint, sizeof(hint));
if (connect_result == SOCKET_ERROR)
{
return false;
}
return true;
}
std::shared_ptr<TcpSocket> Win32TcpSocket::Accept(std::string_view ip_address, std::uint16_t port)
{
sockaddr_in server_address{};
server_address.sin_family = AF_INET;
inet_pton(AF_INET, ip_address.data(), &server_address.sin_addr);
server_address.sin_port = htons(port);
int err = bind(m_socket, (struct sockaddr *)&server_address, sizeof(server_address));
if (err == SOCKET_ERROR)
{
return nullptr;
}
err = WSAGetLastError();
if (listen(m_socket, SOMAXCONN) == SOCKET_ERROR)
{
return nullptr;
}
sockaddr_in client_address = {};
int sz = sizeof(client_address);
SOCKET client = accept(m_socket, (struct sockaddr *)&client_address, &sz);
if (client == INVALID_SOCKET)
{
return nullptr;
}
sockaddr_in hints{};
int hint_size = sizeof(hints);
err = getpeername(client, (sockaddr *)&hints, &hint_size);
if (err == INVALID_SOCKET)
{
return nullptr;
}
char buff[256]{};
inet_ntop(AF_INET, &(hints.sin_addr), buff, 256);
auto ptr = Win32TcpSocket::Create(buff, client);
return ptr;
}
int Win32TcpSocket::Send(TcpSocket &sock, std::string_view message)
{
SOCKET sockfd = (SOCKET)sock.GetHandle();
if (sockfd != INVALID_SOCKET)
{
return send(sockfd, message.data(), (int)message.length(), 0);
}
return 0;
}
std::string Win32TcpSocket::Receive()
{
char buff[256]{};
int bytes = recv(m_socket, buff, 256, 0);
return buff;
}