Reactor/lib/Socket.hpp
2025-06-27 18:01:00 -05:00

319 lines
8.1 KiB
C++

#pragma once
#include "InetAddress.hpp"
#include "Utilities.hpp"
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>
namespace reactor {
class Socket : public NonCopyable
{
private:
int fd_;
void setNonBlockAndCloseOnExec()
{
int flags = fcntl(fd_, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(fd_, F_SETFL, flags);
flags = fcntl(fd_, F_GETFD, 0);
flags |= FD_CLOEXEC;
fcntl(fd_, F_SETFD, flags);
}
public:
explicit Socket(int fd) : fd_(fd)
{
LOG_TRACE << "Socket created with fd=" << fd_;
}
~Socket()
{
if (fd_ >= 0) {
close(fd_);
LOG_TRACE << "Socket fd=" << fd_ << " closed";
}
}
Socket(Socket&& other) noexcept : fd_(other.fd_)
{
other.fd_ = -1;
LOG_TRACE << "Socket moved fd=" << fd_;
}
Socket& operator=(Socket&& other) noexcept
{
if (this != &other) {
if (fd_ >= 0) {
close(fd_);
LOG_TRACE << "Socket fd=" << fd_ << " closed in move assignment";
}
fd_ = other.fd_;
other.fd_ = -1;
}
return *this;
}
static Socket createTcp(bool ipv6 = false)
{
int fd = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (fd < 0) {
LOG_FATAL << "Failed to create TCP socket: " << strerror(errno);
abort();
}
LOG_DEBUG << "Created TCP socket fd=" << fd << " ipv6=" << ipv6;
return Socket(fd);
}
static Socket createUdp(bool ipv6 = false)
{
int fd = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (fd < 0) {
LOG_FATAL << "Failed to create UDP socket: " << strerror(errno);
abort();
}
LOG_DEBUG << "Created UDP socket fd=" << fd << " ipv6=" << ipv6;
return Socket(fd);
}
void bind(const InetAddress& addr)
{
int ret = ::bind(fd_, addr.getSockAddr(), addr.getSockLen());
if (ret < 0) {
LOG_FATAL << "Socket bind to " << addr.toIpPort() << " failed: " << strerror(errno);
abort();
}
LOG_INFO << "Socket fd=" << fd_ << " bound to " << addr.toIpPort();
}
void listen(int backlog = SOMAXCONN)
{
int ret = ::listen(fd_, backlog);
if (ret < 0) {
LOG_FATAL << "Socket listen failed: " << strerror(errno);
abort();
}
LOG_INFO << "Socket fd=" << fd_ << " listening with backlog=" << backlog;
}
int accept(InetAddress& peerAddr)
{
sockaddr_in6 addr;
socklen_t len = sizeof(addr);
int connfd = accept4(fd_, reinterpret_cast<sockaddr*>(&addr), &len, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (connfd >= 0) {
if (addr.sin6_family == AF_INET) {
peerAddr = InetAddress(*reinterpret_cast<sockaddr_in*>(&addr));
} else {
peerAddr = InetAddress(addr);
}
LOG_DEBUG << "Socket fd=" << fd_ << " accepted connection fd=" << connfd
<< " from " << peerAddr.toIpPort();
} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
LOG_ERROR << "Socket accept failed: " << strerror(errno);
}
return connfd;
}
int connect(const InetAddress& addr)
{
int ret = ::connect(fd_, addr.getSockAddr(), addr.getSockLen());
if (ret < 0 && errno != EINPROGRESS) {
LOG_ERROR << "Socket connect to " << addr.toIpPort() << " failed: " << strerror(errno);
} else {
LOG_DEBUG << "Socket fd=" << fd_ << " connecting to " << addr.toIpPort();
}
return ret;
}
void setReuseAddr(bool on = true)
{
int optval = on ? 1 : 0;
if (setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
LOG_ERROR << "setsockopt SO_REUSEADDR failed: " << strerror(errno);
} else {
LOG_TRACE << "Socket fd=" << fd_ << " SO_REUSEADDR=" << on;
}
}
void setReusePort(bool on = true)
{
int optval = on ? 1 : 0;
if (setsockopt(fd_, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) {
LOG_ERROR << "setsockopt SO_REUSEPORT failed: " << strerror(errno);
} else {
LOG_TRACE << "Socket fd=" << fd_ << " SO_REUSEPORT=" << on;
}
}
void setTcpNoDelay(bool on = true)
{
int optval = on ? 1 : 0;
if (setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) < 0) {
LOG_ERROR << "setsockopt TCP_NODELAY failed: " << strerror(errno);
} else {
LOG_TRACE << "Socket fd=" << fd_ << " TCP_NODELAY=" << on;
}
}
void setKeepAlive(bool on = true)
{
int optval = on ? 1 : 0;
if (setsockopt(fd_, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) < 0) {
LOG_ERROR << "setsockopt SO_KEEPALIVE failed: " << strerror(errno);
} else {
LOG_TRACE << "Socket fd=" << fd_ << " SO_KEEPALIVE=" << on;
}
}
void setTcpKeepAlive(int idle, int interval, int count)
{
if (setsockopt(fd_, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)) < 0 ||
setsockopt(fd_, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)) < 0 ||
setsockopt(fd_, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count)) < 0) {
LOG_ERROR << "setsockopt TCP_KEEP* failed: " << strerror(errno);
} else {
LOG_TRACE << "Socket fd=" << fd_ << " TCP keepalive: idle=" << idle
<< " interval=" << interval << " count=" << count;
}
}
void setRecvBuffer(int size)
{
if (setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) {
LOG_ERROR << "setsockopt SO_RCVBUF failed: " << strerror(errno);
} else {
LOG_TRACE << "Socket fd=" << fd_ << " SO_RCVBUF=" << size;
}
}
void setSendBuffer(int size)
{
if (setsockopt(fd_, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) {
LOG_ERROR << "setsockopt SO_SNDBUF failed: " << strerror(errno);
} else {
LOG_TRACE << "Socket fd=" << fd_ << " SO_SNDBUF=" << size;
}
}
ssize_t read(void* buf, size_t len)
{
ssize_t n = ::read(fd_, buf, len);
if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
LOG_ERROR << "Socket read failed: " << strerror(errno);
}
return n;
}
ssize_t write(const void* buf, size_t len)
{
ssize_t n = ::write(fd_, buf, len);
if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
LOG_ERROR << "Socket write failed: " << strerror(errno);
}
return n;
}
ssize_t sendTo(const void* buf, size_t len, const InetAddress& addr)
{
ssize_t n = sendto(fd_, buf, len, 0, addr.getSockAddr(), addr.getSockLen());
if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
LOG_ERROR << "Socket sendto failed: " << strerror(errno);
}
return n;
}
ssize_t recvFrom(void* buf, size_t len, InetAddress& addr)
{
sockaddr_in6 sockaddr;
socklen_t addrlen = sizeof(sockaddr);
ssize_t n = recvfrom(fd_, buf, len, 0, reinterpret_cast<struct sockaddr*>(&sockaddr), &addrlen);
if (n >= 0) {
if (sockaddr.sin6_family == AF_INET) {
addr = InetAddress(*reinterpret_cast<sockaddr_in*>(&sockaddr));
} else {
addr = InetAddress(sockaddr);
}
} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
LOG_ERROR << "Socket recvfrom failed: " << strerror(errno);
}
return n;
}
void shutdownWrite()
{
if (shutdown(fd_, SHUT_WR) < 0) {
LOG_ERROR << "Socket shutdown write failed: " << strerror(errno);
} else {
LOG_DEBUG << "Socket fd=" << fd_ << " shutdown write";
}
}
void shutdownRead()
{
if (shutdown(fd_, SHUT_RD) < 0) {
LOG_ERROR << "Socket shutdown read failed: " << strerror(errno);
} else {
LOG_DEBUG << "Socket fd=" << fd_ << " shutdown read";
}
}
int getSocketError()
{
int optval;
socklen_t optlen = sizeof(optval);
if (getsockopt(fd_, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
return errno;
}
return optval;
}
int fd() const { return fd_; }
static InetAddress getLocalAddr(int sockfd)
{
sockaddr_in6 addr;
socklen_t addrlen = sizeof(addr);
if (getsockname(sockfd, reinterpret_cast<sockaddr*>(&addr), &addrlen) < 0) {
LOG_ERROR << "getsockname failed: " << strerror(errno);
return InetAddress();
}
if (addr.sin6_family == AF_INET) {
return InetAddress(*reinterpret_cast<sockaddr_in*>(&addr));
}
return InetAddress(addr);
}
static InetAddress getPeerAddr(int sockfd)
{
sockaddr_in6 addr;
socklen_t addrlen = sizeof(addr);
if (getpeername(sockfd, reinterpret_cast<sockaddr*>(&addr), &addrlen) < 0) {
LOG_ERROR << "getpeername failed: " << strerror(errno);
return InetAddress();
}
if (addr.sin6_family == AF_INET) {
return InetAddress(*reinterpret_cast<sockaddr_in*>(&addr));
}
return InetAddress(addr);
}
bool isSelfConnected()
{
return getLocalAddr(fd_) == getPeerAddr(fd_);
}
};
} // namespace reactor