#include #ifdef _WIN32 // Windows #include #include #define ioctl ioctlsocket #define WOULDBLOCK WSAEWOULDBLOCK #define MSG_DONTWAIT 0 #else // Linux/Unix #include #include #include #include #include #include #include #define closesocket close #define WOULDBLOCK EWOULDBLOCK #endif #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif namespace sp { namespace io { TcpSocket::IOInterface() : m_Handle(static_cast(INVALID_SOCKET)), m_Status(Status::Disconnected) {} TcpSocket::IOInterface(const std::string& a_Host, std::uint16_t a_Port) : IOInterface() { Connect(a_Host, a_Port); } TcpSocket::IOInterface(IOInterface&& a_Other) { std::swap(m_Handle, a_Other.m_Handle); std::swap(m_Status, a_Other.m_Status); } TcpSocket::~IOInterface() {} void TcpSocket::Connect(const std::string& a_Host, std::uint16_t a_Port) { struct addrinfo hints {}; struct addrinfo* result = nullptr; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; m_Status = Status::Error; if (getaddrinfo(a_Host.c_str(), std::to_string(static_cast(a_Port)).c_str(), &hints, &result) != 0) { throw SocketError("Failed to get address info"); } m_Handle = static_cast(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); if (m_Handle < 0) { throw SocketError("Failed to create socket"); } struct addrinfo* ptr = nullptr; for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) { struct sockaddr* sockaddr = ptr->ai_addr; if (connect(m_Handle, sockaddr, sizeof(sockaddr_in)) == 0) { break; } } freeaddrinfo(result); if (!ptr) { throw SocketError("Could not find a suitable interface for connecting"); } m_Status = Status::Connected; } DataBuffer TcpSocket::Read(std::size_t a_Amount) { DataBuffer buffer(a_Amount); std::size_t totalRecieved = 0; while (totalRecieved < a_Amount) { int recvAmount = recv(m_Handle, reinterpret_cast(buffer.data() + totalRecieved), static_cast(a_Amount - totalRecieved), 0); if (recvAmount <= 0) { #if defined(_WIN32) || defined(WIN32) int err = WSAGetLastError(); #else int err = errno; #endif if (err == WOULDBLOCK) { // we are in non blocking mode and nothing is available return {}; } Disconnect(); m_Status = Status::Error; throw SocketError("Error while reading"); } totalRecieved += recvAmount; } return buffer; } void TcpSocket::Write(const sp::DataBuffer& a_Data) { if (GetStatus() != Status::Connected) return; std::size_t sent = 0; while (sent < a_Data.GetSize()) { int cur = send(m_Handle, reinterpret_cast(a_Data.data() + sent), static_cast(a_Data.GetSize() - sent), 0); if (cur <= 0) { Disconnect(); m_Status = Status::Error; return; } sent += static_cast(cur); } } bool TcpSocket::SetBlocking(bool a_Block) { unsigned long mode = !a_Block; if (ioctl(m_Handle, FIONBIO, &mode) < 0) { return false; } return true; } TcpSocket::Status TcpSocket::GetStatus() const { return m_Status; } void TcpSocket::Disconnect() { if (m_Handle > 0) closesocket(m_Handle); m_Status = Status::Disconnected; } TcpSocket& TcpSocket::operator=(IOInterface&& a_Other) { std::swap(m_Handle, a_Other.m_Handle); std::swap(m_Status, a_Other.m_Status); return *this; } } // namespace io } // namespace sp