#ifdef __linux__ #include "Socket_p.h" #include #include 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> accept(Socket_FD listen_fd) { sockaddr_in clientAddr{}; socklen_t clientAddrSize = sizeof(clientAddr); const int client_fd = ::accept( listen_fd, reinterpret_cast(&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(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(ne, e)); } info.sockaddr.ip = ipbuf; info.sockaddr.port = ntohs(clientAddr.sin_port); return info; } } // namespace TCP Ret 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::invalid_argument)); } if (::connect(that, reinterpret_cast(&addr), sizeof(addr)) < 0) { const int e = errno; // 非阻塞 connect:EINPROGRESS / EALREADY 表示“连接进行中”,不算失败 if (e == EINPROGRESS || e == EALREADY) { // 这里用你的错误体系表达“进行中”(调用方可用 poll/epoll/select // 等等待可写) return unexpected>(Enum_Err(NetError::in_progress, e)); // 如果你更希望用返回值表达进行中,也可以改成:return false; } const auto ne = NetErrorCategory::map_system_error(e); return unexpected>(Enum_Err(ne, e)); } return true; } // 纯 Linux 语义:recv_all // - recv 返回 -1 表示失败,错误在 errno // - 非阻塞无数据/被信号打断:返回已收到部分(可能为空) // - recv 返回 0:对端正常关闭(EOF),这里返回 NetError::connection_closed #include #include #include #ifdef _WIN32 // 你这里是 Linux 版,用 errno / ioctl;Windows 需要 ioctlsocket + WSAGetLastError #else #include #include #include #endif Ret recv_all(Socket_FD fd) { // 1) 先拿内核缓冲区里当前可读的字节数 #ifdef _WIN32 return unexpected>(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(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::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>) } const auto ne = NetErrorCategory::map_system_error(e); return unexpected>(Enum_Err(ne, e)); } } // 2) 一次性读 avail 字节 std::string out; out.resize(static_cast(avail)); size_t off = 0; while (off < static_cast(avail)) { const ssize_t n = ::recv(fd, out.data() + off, static_cast(avail) - off, 0); if (n > 0) { off += static_cast(n); continue; } if (n == 0) { // 读到一半对端关闭:保留已读部分还是报错,看你语义 out.resize(off); return unexpected>(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(ne, e)); } // 正常:一次性读完 return out; #endif } namespace UDP { Ret recvfrom(Socket_FD receive_fd, Sockaddr_In *ret, int chunk_size) { if (chunk_size <= 0 || ret == nullptr) { return unexpected>(Enum_Err(NetError::invalid_argument)); } std::string buffer; buffer.resize(static_cast(chunk_size)); sockaddr_in client_addr{}; socklen_t addr_len = sizeof(client_addr); const ssize_t n = ::recvfrom(receive_fd, buffer.data(), static_cast(chunk_size), 0, reinterpret_cast(&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(ne, e)); } // n == 0:UDP 允许 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(ne, e)); } ret->ip = client_ip; ret->port = ntohs(client_addr.sin_port); buffer.resize(static_cast(n)); return buffer; } } // namespace UDP ERROR_CODE_TYPE last_socket_ec() { return errno; } using Socket_FD = int; constexpr int kSocketError = -1; Ret set_block(Socket_FD fd, bool blocking) { if (fd == -1) return Ret(); 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::unknown_error)); } return Ret(); } 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