summaryrefslogtreecommitdiffhomepage
path: root/include/ocl/unique_socket.hpp
diff options
context:
space:
mode:
authorAmlal El Mahrouss <amlal@nekernel.org>2025-12-04 06:14:14 -0500
committerAmlal El Mahrouss <amlal@nekernel.org>2025-12-04 06:14:14 -0500
commite85b8a1cf1624a9cbf3d719dd202ad57d43c2f4b (patch)
tree89f7c1d07eed253f97141102041d70b12dfcd48e /include/ocl/unique_socket.hpp
parentd43fed8fb9eb369adc70a57bc2a9552d36485241 (diff)
chore: modularize OCL to OCL.core, upcoming OCL.fix
Signed-off-by: Amlal El Mahrouss <amlal@nekernel.org>
Diffstat (limited to 'include/ocl/unique_socket.hpp')
-rw-r--r--include/ocl/unique_socket.hpp211
1 files changed, 211 insertions, 0 deletions
diff --git a/include/ocl/unique_socket.hpp b/include/ocl/unique_socket.hpp
new file mode 100644
index 0000000..a8e532e
--- /dev/null
+++ b/include/ocl/unique_socket.hpp
@@ -0,0 +1,211 @@
+/*
+ * File: 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 <ocl/detail/config.hpp>
+#include <ocl/detail/net_config.hpp>
+
+namespace ocl
+{
+ class unique_socket;
+
+ inline constexpr auto socket_null = 0;
+
+ 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)
+ {
+ this->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 any_address = "0.0.0.0";
+ static constexpr auto backlog_count = 20U;
+
+ 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* in, std::size_t len)
+ {
+ if (!in || !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_, in, len, 0);
+ ret_sock.bad_ = ret < 0L;
+
+ return ret_sock;
+ }
+
+ void read_client_buffer(char* in, std::size_t len)
+ {
+ if (!in || !len)
+ return;
+
+ if (is_server_)
+ return;
+
+ auto ret = ::recv(this->socket_, in, 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(this->socket_, out, len, 0);
+ this->bad_ = ret < 0L;
+ }
+
+ template <uint16_t port>
+ static unique_socket make_socket(const std::string& address, const bool is_server)
+ {
+ if (unique_socket sock; sock.construct<AF_INET, SOCK_STREAM, port>(address.c_str(), is_server))
+ return sock;
+
+ throw std::invalid_argument("invalid socket argument");
+ }
+
+ private:
+ template <uint16_t af, uint16_t kind, uint16_t port>
+ bool construct(const char* addr = unique_socket::any_address, 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));
+
+ if (addr == unique_socket::any_address)
+ addr_.sin_addr.s_addr = INADDR_ANY;
+ else
+ 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<struct sockaddr*>(&addr_), sizeof(addr_));
+ bad_ = ret == -1;
+ return bad_ == false;
+ }
+
+ int ret = ::bind(socket_, (struct sockaddr*)&addr_, sizeof(addr_));
+
+ bad_ = ret == -1;
+
+ if (bad_)
+ return false;
+
+ ret = ::listen(socket_, unique_socket::backlog_count);
+
+ bad_ = ret == -1;
+
+ return bad_ == false;
+ }
+
+ bool destroy() noexcept
+ {
+ if (!socket_)
+ return false;
+
+ ::close(socket_);
+
+ socket_ = 0L;
+
+ return true;
+ }
+ };
+
+ template <typename TS>
+ concept IsValidSocket = requires(TS& sock) {
+ { sock.bad() };
+ };
+} // namespace ocl