/* * File: net/unique_socket.hpp * Purpose: RAII socket concept in modern C++ * Author: Amlal El Mahrouss (amlal@nekernel.org) * Copyright 2025, Amlal El Mahrouss, licensed under the Boost Software License. */ #pragma once #include #include #include #include #include #include #include #include #ifdef OCL_WINDOWS #error !!! "Windows is not supported yet for " !!! #endif // _WIN32 namespace ocl::net { class unique_socket final { public: using socket_type = int; using error_type = bool; using condition_type = bool; using socket_state = int; private: socket_type socket_{}; condition_type is_server_{false}; error_type bad_{false}; public: unique_socket() = default; ~unique_socket() { this->destroy(); } unique_socket& operator=(const unique_socket&) = delete; unique_socket(const unique_socket&) = delete; unique_socket& operator=(unique_socket&& other) noexcept { if (this != &other) { destroy(); socket_ = other.socket_; is_server_ = other.is_server_; bad_ = other.bad_; other.socket_ = 0; other.bad_ = true; } return *this; } unique_socket(unique_socket&& other) noexcept : socket_(other.socket_), is_server_(other.is_server_), bad_(other.bad_) { other.socket_ = 0; other.bad_ = true; } static constexpr auto local_address_ip4 = "127.0.0.1"; static constexpr auto backlog_count = 5U; const error_type& bad() { return bad_; } unique_socket::socket_state state() noexcept { socket_state error = 0; socklen_t len = sizeof(error); getsockopt(socket_, SOL_SOCKET, SO_ERROR, &error, &len); return error; } unique_socket accept() noexcept { socket_type cl_{-1}; if (this->is_server_) cl_ = ::accept(socket_, nullptr, nullptr); unique_socket ret_sock; ret_sock.socket_ = cl_; return std::move(ret_sock); } unique_socket read_server_buffer(char* out, std::size_t len) { if (!out || !len) return {}; if (!is_server_) { return {}; } auto ret_sock = accept(); if (ret_sock.socket_ == -1) throw std::invalid_argument("no connection to accept."); auto ret = ::recv(ret_sock.socket_, static_cast(out), len, 0); ret_sock.bad_ = ret < 0L; return ret_sock; } void read_client_buffer(char* out, std::size_t len) { if (!out || !len) return; if (is_server_) return; auto ret = ::recv(this->socket_, static_cast(out), len, 0); this->bad_ = ret < 0L; } void write_from_buffer(const char* out, std::size_t len) { if (!out) return; if (!len) return; auto ret = ::send(socket_, out, len, 0); bad_ = ret < 0L; } template static unique_socket make_socket(const std::string& address, const bool is_server) { unique_socket sock; if (!sock.construct(address.c_str(), is_server)) throw std::invalid_argument("invalid socket argument"); return sock; } private: template bool construct(const char* addr = unique_socket::local_address_ip4, const bool& is_server = false) noexcept { static_assert(af != 0, "Address family is zero"); static_assert(kind != 0, "Kind is zero"); socket_ = ::socket(af, kind, 0); is_server_ = is_server; if (socket_ == -1) return false; struct sockaddr_in addr_; std::memset(&addr_, 0, sizeof(struct sockaddr_in)); addr_.sin_addr.s_addr = ::inet_addr(addr); addr_.sin_port = htons(port); addr_.sin_family = af; if (!is_server) { const auto ret = ::connect(socket_, reinterpret_cast(&addr_), sizeof(addr_)); return ret == 0L; } int ret = ::bind(socket_, (struct sockaddr*)&addr_, sizeof(addr_)); bad_ = ret == -1; ::listen(socket_, unique_socket::backlog_count); return bad_ == false; } bool destroy() noexcept { if (!socket_) return false; ::shutdown(socket_, SHUT_RDWR); ::close(socket_); socket_ = 0L; return true; } }; } // namespace ocl::net