Initial commit

This commit is contained in:
2026-06-16 10:56:40 +08:00
commit d2f95e0e27
2047 changed files with 619063 additions and 0 deletions
+436
View File
@@ -0,0 +1,436 @@
#ifdef __linux__
#include "Socket_p.h"
#include <netinet/tcp.h>
#include <sys/ioctl.h>
namespace Psc::socket {
NetError NetErrorCategory::map_system_error(int e) noexcept {
switch (e) {
case EINTR:
return NetError::interrupted;
case ECANCELED:
return NetError::operation_canceled;
case EINVAL:
return NetError::invalid_argument;
case EACCES:
return NetError::permission_denied;
case ENOMEM:
return NetError::out_of_memory;
case EMFILE:
case ENFILE:
case ENOBUFS:
return NetError::resource_exhausted;
case EIO:
return NetError::io_error;
case EBADF:
return NetError::bad_file_descriptor;
case ENOTSOCK:
return NetError::not_a_socket;
case EAGAIN:
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
case EWOULDBLOCK:
#endif
return NetError::would_block;
case EINPROGRESS:
return NetError::in_progress;
case EALREADY:
return NetError::already_in_progress;
case EADDRINUSE:
return NetError::address_in_use;
case EADDRNOTAVAIL:
return NetError::address_not_available;
case ENETDOWN:
return NetError::network_down;
case ENETUNREACH:
return NetError::network_unreachable;
case EHOSTUNREACH:
return NetError::host_unreachable;
case EPROTO:
case EPROTOTYPE:
case ENOPROTOOPT:
return NetError::protocol_error;
case ENOTCONN:
return NetError::not_connected;
case EISCONN:
return NetError::already_connected;
case ECONNREFUSED:
return NetError::connection_refused;
case ECONNRESET:
return NetError::connection_reset;
case ECONNABORTED:
return NetError::connection_aborted;
case ETIMEDOUT:
return NetError::timed_out;
case EPIPE:
return NetError::broken_pipe;
default:
return NetError::unknown_error;
}
}
namespace TCP {
Ret<std::optional<Accept_Info>> accept(Socket_FD listen_fd) {
sockaddr_in clientAddr{};
socklen_t clientAddrSize = sizeof(clientAddr);
const int client_fd = ::accept(
listen_fd, reinterpret_cast<sockaddr *>(&clientAddr), &clientAddrSize);
if (client_fd < 0) {
const int e = errno;
// 非阻塞:当前没有新连接(正常情况)
// Linux 下通常是 EAGAIN / EWOULDBLOCK;被信号中断是 EINTR
if (e == EAGAIN
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
|| e == EWOULDBLOCK
#endif
|| e == EINTR) {
return std::nullopt;
}
const auto ne = NetErrorCategory::map_system_error(e);
LOG_FD_Debug(listen_fd, std::string("accept error: ") +
std::system_category().message(e));
return unexpected<Enum_Err<NetError>>(Enum_Err(ne, e));
}
Accept_Info info{};
info.fd = client_fd;
char ipbuf[INET_ADDRSTRLEN]{};
if (::inet_ntop(AF_INET, &clientAddr.sin_addr, ipbuf, sizeof(ipbuf)) ==
nullptr) {
const int e = errno; // inet_ntop 失败时设置 errno
const auto ne = NetErrorCategory::map_system_error(e);
LOG_FD_Debug(listen_fd, std::string("inet_ntop error: ") +
std::system_category().message(e));
::close(client_fd); // 纯 Linux 语义:避免泄漏已 accept 的 fd
return unexpected<Enum_Err<NetError>>(Enum_Err(ne, e));
}
info.sockaddr.ip = ipbuf;
info.sockaddr.port = ntohs(clientAddr.sin_port);
return info;
}
} // namespace TCP
Ret<bool> connect(Socket_FD that, const Sockaddr_In &addr_in) {
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(addr_in.port);
if (::inet_pton(AF_INET, addr_in.ip.c_str(), &addr.sin_addr) <= 0) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
if (::connect(that, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) < 0) {
const int e = errno;
// 非阻塞 connectEINPROGRESS / EALREADY 表示“连接进行中”,不算失败
if (e == EINPROGRESS || e == EALREADY) {
// 这里用你的错误体系表达“进行中”(调用方可用 poll/epoll/select
// 等等待可写)
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::in_progress, e));
// 如果你更希望用返回值表达进行中,也可以改成:return false;
}
const auto ne = NetErrorCategory::map_system_error(e);
return unexpected<Enum_Err<NetError>>(Enum_Err(ne, e));
}
return true;
}
// 纯 Linux 语义:recv_all
// - recv 返回 -1 表示失败,错误在 errno
// - 非阻塞无数据/被信号打断:返回已收到部分(可能为空)
// - recv 返回 0:对端正常关闭(EOF),这里返回 NetError::connection_closed
#include <string>
#include <cerrno>
#include <system_error>
#ifdef _WIN32
// 你这里是 Linux 版,用 errno / ioctlWindows 需要 ioctlsocket + WSAGetLastError
#else
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/socket.h>
#endif
Ret<std::string> recv_all(Socket_FD fd) {
// 1) 先拿内核缓冲区里当前可读的字节数
#ifdef _WIN32
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::not_supported)); // 如需我给 Windows 版我再补
#else
int avail = 0;
if (::ioctl(fd, FIONREAD, &avail) != 0) {
const int e = errno;
const auto ne = NetErrorCategory::map_system_error(e);
return unexpected<Enum_Err<NetError>>(Enum_Err(ne, e));
}
if (avail <= 0) {
// avail==0:要区分“对端关闭” vs “当前没数据(EAGAIN)”
// 用 MSG_PEEK 看看是否已经 EOF 或者只是没数据。
char ch;
for (;;) {
const ssize_t n = ::recv(fd, &ch, 1, MSG_PEEK);
if (n > 0) {
// 有数据但 ioctl 说 0:极少见(竞态),再走一次 ioctl 或直接读 1
avail = 1;
break;
}
if (n == 0) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::connection_closed));
}
// n < 0
const int e = errno;
if (e == EINTR) continue;
if (e == EAGAIN
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
|| e == EWOULDBLOCK
#endif
) {
return std::string{}; // 非阻塞:当前没数据,正常返回空串(或你也可以返回 unexpected<Enum_Err<NetError>>
}
const auto ne = NetErrorCategory::map_system_error(e);
return unexpected<Enum_Err<NetError>>(Enum_Err(ne, e));
}
}
// 2) 一次性读 avail 字节
std::string out;
out.resize(static_cast<size_t>(avail));
size_t off = 0;
while (off < static_cast<size_t>(avail)) {
const ssize_t n = ::recv(fd, out.data() + off,
static_cast<size_t>(avail) - off, 0);
if (n > 0) {
off += static_cast<size_t>(n);
continue;
}
if (n == 0) {
// 读到一半对端关闭:保留已读部分还是报错,看你语义
out.resize(off);
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::connection_closed));
}
const int e = errno;
if (e == EINTR) continue;
// 这里一般不该出现 EAGAIN(因为我们按 avail 读),但竞态下可能发生
if (e == EAGAIN
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
|| e == EWOULDBLOCK
#endif
) {
out.resize(off);
return out; // 返回已读部分
}
out.resize(off);
const auto ne = NetErrorCategory::map_system_error(e);
return unexpected<Enum_Err<NetError>>(Enum_Err(ne, e));
}
// 正常:一次性读完
return out;
#endif
}
namespace UDP {
Ret<std::string> recvfrom(Socket_FD receive_fd, Sockaddr_In *ret,
int chunk_size) {
if (chunk_size <= 0 || ret == nullptr) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
std::string buffer;
buffer.resize(static_cast<size_t>(chunk_size));
sockaddr_in client_addr{};
socklen_t addr_len = sizeof(client_addr);
const ssize_t n =
::recvfrom(receive_fd, buffer.data(), static_cast<size_t>(chunk_size), 0,
reinterpret_cast<sockaddr *>(&client_addr), &addr_len);
if (n < 0) {
const int e = errno;
if (e == EAGAIN
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
|| e == EWOULDBLOCK
#endif
|| e == EINTR) {
return std::string{};
}
const auto ne = NetErrorCategory::map_system_error(e);
LOG_FD_Debug(receive_fd, std::string("recvfrom error: ") +
std::system_category().message(e));
return unexpected<Enum_Err<NetError>>(Enum_Err(ne, e));
}
// n == 0UDP 允许 0 长度 datagram,仍然要填来源地址
char client_ip[INET_ADDRSTRLEN]{};
if (::inet_ntop(AF_INET, &client_addr.sin_addr, client_ip,
sizeof(client_ip)) == nullptr) {
const int e = errno; // inet_ntop 失败会设置 errno
const auto ne = NetErrorCategory::map_system_error(e);
LOG_FD_Debug(receive_fd, std::string("inet_ntop error: ") +
std::system_category().message(e));
return unexpected<Enum_Err<NetError>>(Enum_Err(ne, e));
}
ret->ip = client_ip;
ret->port = ntohs(client_addr.sin_port);
buffer.resize(static_cast<size_t>(n));
return buffer;
}
} // namespace UDP
ERROR_CODE_TYPE last_socket_ec() { return errno; }
using Socket_FD = int;
constexpr int kSocketError = -1;
Ret<void> set_block(Socket_FD fd, bool blocking) {
if (fd == -1)
return Ret<void>();
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
std::cerr << "Failed to get socket flags." << std::endl;
}
if (blocking) {
flags &= ~O_NONBLOCK; // 清除 O_NONBLOCK 标志,设置为阻塞
} else {
flags |= O_NONBLOCK; // 设置 O_NONBLOCK 标志,设置为非阻塞
}
if (fcntl(fd, F_SETFL, flags) == -1) {
LOG_FD_ERROR(fd, "Failed to set socket to " +
std::string(blocking ? "blocking" : "non-blocking") +
" mode.")
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::unknown_error));
}
return Ret<void>();
}
bool is_needed_reconnect_ec(ERROR_CODE_TYPE e) {
switch (e) {
// 连接已失效/网络不可达:建议重连
case ECONNRESET:
case ECONNABORTED:
case ENOTCONN:
case ETIMEDOUT:
case ENETDOWN:
case ENETUNREACH:
case EHOSTUNREACH:
case ECONNREFUSED: // 多见于 connect;这里出现也可视为需重连
case EPIPE: // 也可视为连接不可用(更常见于 send,但有时可一起处理)
return true;
// 现在没数据/可重试/被打断:不重连
case EAGAIN:
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
case EWOULDBLOCK:
#endif
case EINTR:
case EINPROGRESS:
case EALREADY:
return false;
// 其他错误:保守处理为不重连(也可改成 true 更激进)
default:
return false;
}
}
bool is_needed_reconnect(int fd) {
char buf[1];
const ssize_t r = ::recv(fd, buf, sizeof(buf), MSG_PEEK);
return is_needed_reconnect_ec(r);
}
// 根据“系统错误码/返回码”判断:这个 TCP 连接是否应该被认为已失效并关闭。
// 约定:
// - r == 0 :通常表示对端正常关闭(EOF/FIN),应关闭本端连接。
// - r < 0 :表示发生错误;具体原因需要结合 errno(Linux)分类。
// - r > 0 :表示仍可读到数据/连接仍活着,不关闭。
bool is_client_need_close_ec(ERROR_CODE_TYPE e) {
switch (e) {
// ====== 致命错误:连接已不可用,建议关闭 ======
case ECONNRESET:
// 连接被对端复位(RST),对端异常断开:应关闭
return true;
case ECONNABORTED:
// 连接被中止(本端/对端导致),不可继续使用:应关闭
return true;
case ENOTCONN:
// socket 未处于连接状态(例如已经断开或从未连接):应关闭
return true;
case ETIMEDOUT:
// 连接超时(可能是网络断开/对端无响应):通常视为失效,建议关闭
return true;
case EPIPE:
// 管道破裂:常见于 send 时对端已关闭;连接不可用:应关闭
return true;
case ECONNREFUSED:
// 连接被拒绝:通常发生在 connect 阶段;若出现在此处也视为不可用:应关闭
return true;
// ====== 暂态/可恢复错误:不建议立即关闭 ======
case EAGAIN:
// 非阻塞模式下“暂时无数据/暂不可读写”,属于正常现象:不关闭
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
case EWOULDBLOCK:
// 与 EAGAIN 类似,表示暂时会阻塞:不关闭
#endif
return false;
case EINPROGRESS:
// 非阻塞 connect 进行中:不关闭
return false;
case EALREADY:
// 非阻塞 connect 已在进行:不关闭
return false;
case EINTR:
// 系统调用被信号中断:通常应重试,而不是关闭
return false;
default:
// 其他错误:保守策略——不立刻关闭(也可按需求改为 true 更激进)
return false;
}
}
bool is_client_need_close(int fd) {
char buf[1];
int r = ::recv(fd, buf, sizeof(buf), MSG_PEEK);
return is_client_need_close_ec(r);
}
} // namespace Psc::socket
#endif