File socket_utils.hpp
File List > connections > socket_utils.hpp
Go to the documentation of this file
// Copyright 2026 TIER IV, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <nebula_core_common/util/errno.hpp>
#include <nebula_core_common/util/expected.hpp>
#include <arpa/inet.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <array>
#include <cstdint>
#include <exception>
#include <stdexcept>
#include <string>
#include <string_view>
#include <variant>
namespace nebula::drivers::connections
{
class SocketError : public std::exception
{
public:
explicit SocketError(int err_no) : what_{util::errno_to_string(err_no)} {}
explicit SocketError(const std::string_view & msg) : what_(msg) {}
[[nodiscard]] const char * what() const noexcept override { return what_.c_str(); }
private:
std::string what_;
};
class UsageError : public std::runtime_error
{
public:
explicit UsageError(const std::string & msg) : std::runtime_error(msg) {}
};
class SockFd
{
static const int g_uninitialized = -1;
int sock_fd_;
public:
SockFd() : sock_fd_{g_uninitialized} {}
explicit SockFd(int sock_fd) : sock_fd_{sock_fd} {}
SockFd(SockFd && other) noexcept : sock_fd_{other.sock_fd_} { other.sock_fd_ = g_uninitialized; }
SockFd(const SockFd &) = delete;
SockFd & operator=(const SockFd &) = delete;
SockFd & operator=(SockFd && other) noexcept
{
if (this != &other) {
if (sock_fd_ != g_uninitialized) ::close(sock_fd_);
sock_fd_ = other.sock_fd_;
other.sock_fd_ = g_uninitialized;
}
return *this;
};
~SockFd()
{
if (sock_fd_ == g_uninitialized) return;
::close(sock_fd_);
}
[[nodiscard]] int get() const { return sock_fd_; }
template <typename T>
[[nodiscard]] util::expected<std::monostate, SocketError> setsockopt(
int level, int optname, const T & optval)
{
int result = ::setsockopt(sock_fd_, level, optname, &optval, sizeof(T));
if (result == -1) return SocketError(errno);
return std::monostate{};
}
};
struct Endpoint
{
in_addr ip;
uint16_t port;
[[nodiscard]] sockaddr_in to_sockaddr() const
{
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr = ip;
addr.sin_port = htons(port);
return addr;
}
};
inline util::expected<in_addr, UsageError> parse_ip(const std::string & ip)
{
in_addr parsed_addr{};
int result = inet_pton(AF_INET, ip.c_str(), &parsed_addr);
if (result <= 0) return UsageError("Invalid IP address given");
return parsed_addr;
}
inline util::expected<std::string, SocketError> to_string(const in_addr & addr)
{
std::array<char, INET_ADDRSTRLEN> buf{0};
if (inet_ntop(AF_INET, &addr, buf.data(), INET_ADDRSTRLEN) == nullptr) {
return SocketError(errno);
}
return std::string(buf.data());
}
inline util::expected<bool, int> is_socket_ready(int fd, int timeout_ms, int events = POLLIN)
{
pollfd pfd{fd, static_cast<std::int16_t>(events), 0};
int status{-1};
do {
status = poll(&pfd, 1, timeout_ms);
} while (status == -1 && errno == EINTR);
if (status == -1) return errno;
return (status > 0) && (pfd.revents & events);
}
} // namespace nebula::drivers::connections