Adjusted workflow.
This commit is contained in:
478
src/io/socket/TCP_BSD.cpp
Normal file
478
src/io/socket/TCP_BSD.cpp
Normal file
@@ -0,0 +1,478 @@
|
||||
#include "ehs/io/socket/TCP_BSD.h"
|
||||
#include "ehs/io/socket/DNS.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cerrno>
|
||||
|
||||
namespace ehs
|
||||
{
|
||||
TCP::~TCP()
|
||||
{
|
||||
if (hdl == EHS_INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
if (connection)
|
||||
{
|
||||
if (shutdown(hdl, SHUT_RDWR) == -1)
|
||||
EHS_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
if (close(hdl) == -1)
|
||||
EHS_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
TCP::TCP()
|
||||
: hdl(EHS_INVALID_SOCKET)
|
||||
{
|
||||
}
|
||||
|
||||
TCP::TCP(const AddrType addrType)
|
||||
: BaseTCP(addrType), hdl(EHS_INVALID_SOCKET)
|
||||
{
|
||||
TCP::Initialize();
|
||||
}
|
||||
|
||||
TCP::TCP(TCP&& tcp) noexcept
|
||||
: BaseTCP(std::move(tcp)), hdl(tcp.hdl)
|
||||
{
|
||||
tcp.hdl = EHS_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
TCP::TCP(const TCP& tcp)
|
||||
: BaseTCP(tcp), hdl(EHS_INVALID_SOCKET)
|
||||
{
|
||||
}
|
||||
|
||||
TCP& TCP::operator=(TCP&& tcp) noexcept
|
||||
{
|
||||
if (this == &tcp)
|
||||
return *this;
|
||||
|
||||
BaseTCP::operator=(std::move(tcp));
|
||||
|
||||
hdl = tcp.hdl;
|
||||
|
||||
tcp.hdl = EHS_INVALID_SOCKET;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TCP& TCP::operator=(const TCP& tcp)
|
||||
{
|
||||
if (this == &tcp)
|
||||
return *this;
|
||||
|
||||
BaseTCP::operator=(tcp);
|
||||
|
||||
hdl = EHS_INVALID_SOCKET;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TCP::Initialize()
|
||||
{
|
||||
if (IsValid())
|
||||
return;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
hdl = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
hdl = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
else
|
||||
return;
|
||||
|
||||
if (hdl == EHS_INVALID_SOCKET)
|
||||
{
|
||||
UInt_32 code = errno;
|
||||
|
||||
EHS_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Release()
|
||||
{
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
if (connection)
|
||||
{
|
||||
if (shutdown(hdl, SHUT_RDWR) == -1)
|
||||
EHS_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
if (close(hdl) == -1)
|
||||
EHS_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
connection = false;
|
||||
bound = false;
|
||||
listening = false;
|
||||
connected = false;
|
||||
hdl = EHS_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void TCP::Bind(const Str_8& address, UInt_16 port)
|
||||
{
|
||||
if (!IsValid() || bound || connection)
|
||||
return;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
Bind_v6(address, port);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
Bind_v4(address, port);
|
||||
|
||||
this->localAddr = address;
|
||||
this->localPort = port;
|
||||
|
||||
bound = true;
|
||||
}
|
||||
|
||||
void TCP::Listen()
|
||||
{
|
||||
if (connection || !IsValid() || !bound || listening)
|
||||
return;
|
||||
|
||||
int code = listen(hdl, SOMAXCONN);
|
||||
if (code == -1)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Failed to listen with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
listening = true;
|
||||
}
|
||||
|
||||
TCP* TCP::Accept()
|
||||
{
|
||||
if (connection || !IsValid() || !bound || !listening)
|
||||
return nullptr;
|
||||
|
||||
sockaddr_in6 remote = {};
|
||||
UInt_32 addrLen = sizeof(sockaddr_in6);
|
||||
|
||||
TCP* client = new TCP();
|
||||
client->addrType = addrType;
|
||||
client->localAddr = localAddr;
|
||||
client->localPort = localPort;
|
||||
client->connection = true;
|
||||
client->hdl = accept(hdl, (sockaddr*)&remote, &addrLen);
|
||||
|
||||
if (client->hdl == EHS_INVALID_SOCKET)
|
||||
{
|
||||
if (errno != EWOULDBLOCK)
|
||||
EHS_LOG_INT("Error", 0, "Failed to accept client with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
delete client;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (addrLen == sizeof(sockaddr_in6))
|
||||
{
|
||||
char tmpAddr[INET6_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN))
|
||||
{
|
||||
EHS_LOG_INT("Error", 1, "Failed to convert IPv6 address with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
delete client;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
client->remoteAddr = tmpAddr;
|
||||
client->remotePort = ntohs(remote.sin6_port);
|
||||
}
|
||||
else if (addrLen == sizeof(sockaddr_in))
|
||||
{
|
||||
char tmpAddr[INET_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN))
|
||||
{
|
||||
EHS_LOG_INT("Error", 1, "Failed to convert IPv4 address with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
delete client;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
client->remoteAddr = tmpAddr;
|
||||
client->remotePort = ntohs(((sockaddr_in*)&remote)->sin_port);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void TCP::Connect(const Str_8& address, const UInt_16 port)
|
||||
{
|
||||
if (connection || !IsValid() || listening)
|
||||
return;
|
||||
|
||||
remoteHostName = address;
|
||||
remoteAddr = DNS::Resolve(address);
|
||||
remotePort = port;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
Connect_v6(remoteAddr, port);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
Connect_v4(remoteAddr, port);
|
||||
|
||||
connected = true;
|
||||
}
|
||||
|
||||
UInt_64 TCP::Send(const Byte *const buffer, const UInt_32 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Attempted to send while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((!connection && !connected))
|
||||
{
|
||||
EHS_LOG_INT("Error", 1, "Attempted to send while socket is not connected or a connection.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SInt_64 sent = send(hdl, (char*)buffer, size, 0);
|
||||
if (sent == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if (err == ECONNRESET)
|
||||
{
|
||||
Release();
|
||||
EHS_LOG_INT("Information", 0, "Connection dropped.");
|
||||
}
|
||||
else
|
||||
EHS_LOG_INT("Error", 1, "Failed to send with error #" + Str_8::FromNum(err) + ".");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (UInt_64)sent;
|
||||
}
|
||||
|
||||
UInt_64 TCP::Receive(Byte* const buffer, const UInt_32 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((!connection && !connected))
|
||||
{
|
||||
EHS_LOG_INT("Error", 1, "Attempted to receive while socket is not connected or a connection.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SInt_64 received = recv(hdl, (char*)buffer, size, 0);
|
||||
if (received == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if (err == ECONNRESET)
|
||||
{
|
||||
Release();
|
||||
EHS_LOG_INT("Information", 0, "Connection dropped.");
|
||||
}
|
||||
else if (err != EWOULDBLOCK)
|
||||
{
|
||||
EHS_LOG_INT("Error", 2, "Failed to receive with error #" + Str_8::FromNum(err) + ".");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (UInt_64)received;
|
||||
}
|
||||
|
||||
void TCP::SetBlocking(const bool blocking)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
int flags = fcntl(hdl, F_GETFL, 0);
|
||||
if (flags == -1)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Failed to retrieve flags.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (blocking)
|
||||
flags ^= O_NONBLOCK;
|
||||
else
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if (fcntl(hdl, F_SETFL, flags) == -1)
|
||||
EHS_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
bool TCP::IsBlocking() const
|
||||
{
|
||||
int flags = fcntl(hdl, F_GETFL, 0);
|
||||
if (flags == -1)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Failed to retrieve flags.");
|
||||
return true;
|
||||
}
|
||||
|
||||
return !(flags & O_NONBLOCK);
|
||||
}
|
||||
|
||||
bool TCP::IsValid() const
|
||||
{
|
||||
return hdl != EHS_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void TCP::Bind_v6(const Str_8& address, UInt_16 port)
|
||||
{
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
int code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.sin6_addr = in6addr_any;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (code == -1)
|
||||
{
|
||||
EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Bind_v4(const Str_8& address, UInt_16 port)
|
||||
{
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
result.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (code == -1)
|
||||
{
|
||||
EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Connect_v6(const Str_8& address, UInt_16 port)
|
||||
{
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
int code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (code == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if (err == ETIMEDOUT)
|
||||
{
|
||||
EHS_LOG_INT("Information", 2, "Connection attempt timed-out.");
|
||||
}
|
||||
else
|
||||
{
|
||||
EHS_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + ".");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Connect_v4(const Str_8& address, UInt_16 port)
|
||||
{
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (code == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if (err == ETIMEDOUT)
|
||||
{
|
||||
EHS_LOG_INT("Information", 2, "Connection attempt timed-out.");
|
||||
}
|
||||
else
|
||||
{
|
||||
EHS_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + ".");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user