Created
May 20, 2023 14:55
-
-
Save Tosainu/84a68ac10d575e9466a77c2c0f238ba7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <concepts> | |
#include <iostream> | |
#include <tuple> | |
#include <type_traits> | |
#include <utility> | |
extern "C" { | |
#include <sys/socket.h> | |
#include <sys/un.h> | |
#include <netinet/in.h> | |
#include <netinet/ip.h> | |
#include <unistd.h> | |
} | |
namespace nyan { | |
class file_descriptor { | |
int fd_; | |
public: | |
file_descriptor() noexcept : fd_{-1} {}; | |
explicit file_descriptor(int fd) noexcept : fd_{fd} {}; | |
file_descriptor(const file_descriptor&) = delete; | |
file_descriptor& operator=(const file_descriptor&) = delete; | |
file_descriptor(file_descriptor&& fd) noexcept : fd_{std::exchange(fd.fd_, -1)} {} | |
file_descriptor& operator=(file_descriptor&& fd) noexcept { | |
if (fd_ >= 0) { | |
::close(fd_); | |
} | |
fd_ = std::exchange(fd.fd_, -1); | |
return *this; | |
} | |
int raw_fd() const noexcept { | |
return fd_; | |
} | |
operator bool() const noexcept { | |
return fd_ >= 0; | |
} | |
}; | |
template <class S> | |
struct socket_base : public file_descriptor { | |
static constexpr int domain = S::domain; | |
using file_descriptor::file_descriptor; | |
socket_base(int type, int protocol) noexcept | |
: file_descriptor{::socket(domain, type, protocol)} {} | |
}; | |
template <class T> | |
concept Address = requires(T addr) // | |
{ | |
typename T::native_type; | |
{ addr.addr } -> std::same_as<std::add_lvalue_reference_t<typename T::native_type>>; | |
}; | |
template <class T> | |
concept Socket = requires(const T sock) // | |
{ | |
typename T::endpoint_type; | |
requires Address<typename T::endpoint_type>; | |
}; | |
template <Socket S> | |
inline auto listen(const socket_base<S>& socket, int backlog) { | |
return ::listen(socket.raw_fd(), backlog); | |
} | |
template <Socket S> | |
inline auto bind(const socket_base<S>& socket, const typename S::endpoint_type& addr) { | |
return ::bind(socket.raw_fd(), reinterpret_cast<const ::sockaddr*>(&addr.addr), | |
sizeof(typename S::endpoint_type::native_type)); | |
} | |
template <Socket S> | |
inline auto accept(const socket_base<S>& socket, typename S::endpoint_type& addr, int flags = 0) { | |
::socklen_t len = sizeof(typename S::endpoint_type::native_type); | |
return S{::accept4(socket.raw_fd(), reinterpret_cast<::sockaddr*>(&addr.addr), &len, flags)}; | |
} | |
template <Socket S> | |
inline auto connect(const socket_base<S>& socket, const typename S::endpoint_type& addr) { | |
return ::connect(socket.raw_fd(), reinterpret_cast<const ::sockaddr*>(&addr.addr), | |
sizeof(typename S::endpoint_type::native_type)); | |
} | |
namespace af_inet { | |
struct endpoint { | |
using native_type = ::sockaddr_in; | |
native_type addr; | |
}; | |
struct socket : public socket_base<socket> { | |
static constexpr int domain = AF_INET; | |
using endpoint_type = endpoint; | |
using socket_base::socket_base; | |
}; | |
} // namespace af_inet | |
namespace af_unix { | |
struct endpoint { | |
using native_type = ::sockaddr_un; | |
native_type addr; | |
}; | |
struct socket : public socket_base<socket> { | |
static constexpr int domain = AF_UNIX; | |
using endpoint_type = endpoint; | |
using socket_base::socket_base; | |
}; | |
} // namespace af_unix | |
} // namespace nyan | |
auto main() -> int { | |
auto s = nyan::af_inet::socket{SOCK_STREAM, 0}; | |
bind(s, nyan::af_inet::endpoint{}); | |
listen(s, 20); | |
for (;;) { | |
nyan::af_inet::endpoint peer{}; | |
auto ss = accept(s, peer, SOCK_CLOEXEC); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment