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
+136
View File
@@ -0,0 +1,136 @@
#pragma once
#ifndef PLANTSUNCAT_ERRC_H
#define PLANTSUNCAT_ERRC_H
#include "Core/Base/export.h"
namespace Psc::socket {
enum class NetError : unsigned char {
// ===== 通用 / 取消 / 参数 =====
unknown_error, // 被信号/中断打断(EINTR / WSAEINTR
interrupted, // 被信号/中断打断(EINTR / WSAEINTR
operation_canceled, // 取消(ECANCELED / WSAECANCELLED
invalid_argument, // 参数非法(EINVAL / WSAEINVAL
not_supported, // 不支持(EOPNOTSUPP/ENOTSUP 等)
permission_denied, // 权限不足(EACCES
out_of_memory, // 内存不足(ENOMEM
resource_exhausted, // 资源耗尽(EMFILE/ENFILE/ENOBUFS 等)
// ===== I/O / 系统 =====
io_error, // 其他 I/O 错误(EIO 等)
bad_file_descriptor, // fd 无效(EBADF
not_a_socket, // 不是 socketENOTSOCK / WSAENOTSOCK)
// ===== 非阻塞 / 状态 =====
would_block, // 非阻塞下暂时无数据/不可写(EAGAIN/EWOULDBLOCK / WSAEWOULDBLOCK
in_progress, // 操作进行中(EINPROGRESS / WSAEINPROGRESS
already_in_progress, // 已经在进行(EALREADY / WSAEALREADY
// ===== 地址 / 协议 =====
address_in_use, // 地址端口被占用(EADDRINUSE / WSAEADDRINUSE
address_not_available, // 地址不可用(EADDRNOTAVAIL / WSAEADDRNOTAVAIL
network_down, // 网络子系统 downENETDOWN / WSAENETDOWN
network_unreachable, // 网络不可达(ENETUNREACH / WSAENETUNREACH
host_unreachable, // 主机不可达(EHOSTUNREACH / WSAEHOSTUNREACH
protocol_error, // 协议/选项错误(EPROTO/EPROTOTYPE/ENOPROTOOPT...
// ===== 连接语义(TCP 核心)=====
not_connected, // 未连接(ENOTCONN / WSAENOTCONN
already_connected, // 已连接(EISCONN / WSAEISCONN
connection_refused, // 连接被拒绝(ECONNREFUSED / WSAECONNREFUSED
connection_reset, // 连接被重置(ECONNRESET / WSAECONNRESET
connection_aborted, // 连接被中止(ECONNABORTED / WSAECONNABORTED
connection_closed, // 对端正常关闭/EOF(recv 返回 0 的语义)
timed_out, // 超时(ETIMEDOUT / WSAETIMEDOUT
broken_pipe, // 写入已关闭连接(EPIPEWindows 近似用 shutdown/notconn/reset
// ===== DNS / 名称解析(可选但建议)=====
dns_not_found, // host 不存在/找不到(HOST_NOT_FOUND
dns_temporary_failure, // 临时失败(TRY_AGAIN
dns_failure, // 其他解析失败(NO_RECOVERY / 语义较泛)
};
class NetErrorCategory final : public std::error_category {
public:
[[nodiscard]] const char* name() const noexcept override {
// enum_type_name 返回 string_view,静态存储即可
static constexpr auto name_sv = magic_enum::enum_type_name<NetError>();
return name_sv.data();
}
[[nodiscard]] std::string message(int ev) const override {
auto opt = magic_enum::enum_name(static_cast<NetError>(ev));
if (!opt.empty())
return std::string(opt);
return "unknown enum error";
}
[[nodiscard]] bool equivalent(const std::error_code& ec, int cond) const noexcept override {
if (ec.category() != std::system_category()) return false;
return map_system_error(ec.value()) == static_cast<NetError>(cond);
}
static NetError map_system_error(int e) noexcept;
};
inline const std::error_category& net_category() {
static NetErrorCategory cat;
return cat;
}
ERROR_CODE_TYPE last_socket_ec();
template <typename T> using Ret = expected<T, Enum_Err<NetError>>;
extern BaseLogger *socket_logger;
#if WIN32
using Socket_FD = unsigned long long;
#else
using Socket_FD = int;
#endif
// 默认IPV4 TCP
enum class Socket_Type { TCP, UDP, Null };
struct Sockaddr_In {
std::string ip;
std::uint32_t port{};
[[nodiscard]] std::string to_string() const;
Sockaddr_In() = default;
Sockaddr_In(const std::string &ip, std::uint32_t port) {
this->ip = ip;
this->port = port;
}
// 定义小于运算符,便于存入 std::set
bool operator<(const Sockaddr_In &other) const {
// 先比较 IP 地址
if (ip != other.ip) {
return ip < other.ip;
}
// 如果 IP 地址相同,再比较端口号
return port < other.port;
}
bool operator==(const Sockaddr_In &other) const noexcept {
return ip == other.ip && port == other.port;
}
bool operator!=(const Sockaddr_In &other) const noexcept {
return !(*this == other);
}
};
struct Accept_Info {
Sockaddr_In sockaddr;
Socket_FD fd{};
[[nodiscard]] std::string to_string() const {
return "fd:" + std::to_string(fd) + ":[" + sockaddr.to_string() + "]";
}
};
}
#endif
+421
View File
@@ -0,0 +1,421 @@
#include "Core/Bit.h"
#include "Core/RET.hpp"
#include "Core/Statistics/Frequency_Limit.h"
#include "Socket_p.h"
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
#ifdef __linux__
#include <netinet/tcp.h>
#endif
#undef max
#undef min
namespace Psc::socket {
BaseLogger *socket_logger = nullptr;
std::string Sockaddr_In::to_string() const {
return ip + ":" + std::to_string(port);
}
JSON Socket_Base::to_Json() const {
JSON ret = JSON::object();
ret.append({"fd", socket_fd});
ret.append({"address",
address.has_value() ? address.value().to_string() : "nullopt"});
return ret;
}
void log_error(BaseLogger *logger, const std::string &POS,
Psc::socket::Socket_FD fd, const std::string &MSG) {
static Frequency_Limit_Multi lm;
bool ok = lm.test(std::to_string(fd) + "_" + Psc::to_string(last_socket_ec()) + POS);
if (!ok)
return;
std::string msg =
MSG + std::string(" ") + Psc::get_error_message() + " " + POS + "\n";
if (!logger) {
std::ostringstream oss;
oss << "fd:" << fd << " " << msg << std::flush;
std::cout << oss.str();
} else {
static Log_Type log_type({VAR_STR_1(fd)});
logger->error("", log_type, msg);
}
}
Ret<Socket_FD> create_socket_fd(Socket_Type type) {
if (type == Socket_Type::Null) {
socket_logger->c_debug({}, {}, "严重错误 Socket type is Null: " + to_string(type));
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
int t = (type == Socket_Type::TCP) ? SOCK_STREAM : SOCK_DGRAM;
#if defined(__linux__) && defined(SOCK_CLOEXEC)
// 尽量在 socket() 阶段原子设置(避免 fork/exec 竞争窗口)
t |= SOCK_CLOEXEC;
#endif
Socket_FD fd = ::socket(AF_INET, t, 0);
if (fd == INVALID_SOCKET) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::unknown_error));
}
return fd;
}
Ret<void> set_buffer_size(Socket_FD that, size_t buffer_size) {
// setsockopt(SO_RCVBUF) 在各平台基本都要求 int
if (buffer_size > static_cast<size_t>(std::numeric_limits<int>::max())) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
int sz = static_cast<int>(buffer_size);
// Windows 的 optval 类型是 const char*POSIX 是 const void*
#ifdef _WIN32
const char* optval = reinterpret_cast<const char*>(&sz);
#else
const void* optval = reinterpret_cast<const void*>(&sz);
#endif
if (::setsockopt(that, SOL_SOCKET, SO_RCVBUF, optval,
static_cast<socklen_t>(sizeof(sz))) == SOCKET_ERROR) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::unknown_error));
}
return {};
}
Ret<void> close(Socket_FD that) {
if (that != -1) {
#ifdef _WIN32
int r = ::closesocket(that);
#else
int r = ::close(that); // 使用 POSIX 的 close 函数
#endif
if (r == SOCKET_ERROR) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::unknown_error));
}
}
return Ret<void>();
}
Ret<void> bind(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) {
static Frequency_Limit fl;
if (fl.test()) {
//LOG_FD_Debug(that, VAR_STR_2(addr_in.ip, addr_in.port) + "不合法的地址!");
}
// 标准错误码:参数非法
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
if (::bind(that, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) {
static Frequency_Limit fl;
if (fl.test()) {
//LOG_FD_Debug(that, VAR_STR_2(addr_in.ip, addr_in.port) + "绑定ip端口错误!");
}
// 标准错误码:直接上报系统错误(Windows=WSAGetLastError, Linux=errno
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::unknown_error));
}
return {};
}
Ret<int> send(Socket_FD connected_fd, void *data, std::size_t size) {
#ifdef _WIN32
int flags = 0;
#else
int flags = MSG_NOSIGNAL;
#endif
const int bytes_sent =
::send(connected_fd, (const char *)data, static_cast<int>(size), flags);
if (bytes_sent >= 0) {
return bytes_sent; // 可能是部分发送,交给上层处理
}
#ifdef _WIN32
const int e = ::WSAGetLastError();
// 暂态:直接忽略(不报错),让上层保留缓冲下次继续发
if (e == WSAEWOULDBLOCK || e == WSAEINTR || e == WSAEINPROGRESS || e == WSAEALREADY) {
return 0;
}
#else
const int e = errno;
// 暂态:直接忽略(不报错)
if (e == EAGAIN
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
|| e == EWOULDBLOCK
#endif
|| e == EINTR || e == EINPROGRESS || e == EALREADY) {
return 0;
}
#endif
// 其它错误:返回错误(带 native 错误码,供上层决定是否 close)
const auto ne = NetErrorCategory::map_system_error(e);
return unexpected<Enum_Err<NetError>>(Enum_Err(ne, e));
}
Ret<int> send(Socket_FD connected_fd, const std::string &data) {
if (data.empty()) {
return 0;
}
return send(connected_fd, (uint8_t*)data.data(), data.size());
}
namespace TCP {
Ret<void> set_no_delay(Socket_FD that, bool no_delay) {
int flag = no_delay ? 1 : 0;
auto ret = setsockopt(that, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<char *>(&flag), sizeof(flag));
if (ret < 0) {
LOG_FD_Debug(that, "设置socket阻塞属性错误!")
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
return Ret<void>();
}
Ret<void> set_keep_alive(Socket_FD that, bool enable) {
int flag = enable ? 1 : 0;
auto ret = setsockopt(that, SOL_SOCKET, SO_KEEPALIVE,
reinterpret_cast<char *>(&flag), sizeof(flag));
if (ret < 0) {
LOG_FD_Debug(that, VAR_STR_2(that, enable) + "无法设置 SO_KEEPALIVE!")
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
return Ret<void>();
}
Ret<void> listen(Socket_FD that, int backlog) {
if (::listen(that, backlog) == SOCKET_ERROR) {
auto ec = last_socket_ec();
LOG_FD_Debug(that,
VAR_STR_2(that, backlog) +
"listen 错误");
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
return Ret<void>();
}
} // namespace TCP
namespace UDP {
Ret<int> sendto(Socket_FD that, const std::string &client_ip,
uint16_t client_port, const std::string &data) {
#ifdef _WIN32
int flags = 0;
#else
int flags = MSG_NOSIGNAL; // UDP 一般不需要,但保留也无妨
#endif
if (data.empty()) {
return 0; // 发送 0 字节:正常
}
sockaddr_in client_addr{};
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(client_port);
if (inet_pton(AF_INET, client_ip.c_str(), &client_addr.sin_addr) <= 0) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
int bytes_sent = ::sendto(
that, data.data(), static_cast<int>(data.size()), flags,
reinterpret_cast<const sockaddr *>(&client_addr), sizeof(client_addr));
if (bytes_sent == SOCKET_ERROR) {
auto ec = last_socket_ec();
LOG_FD_Debug(that, std::string("sendto error: ") + get_error_message());
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::unknown_error));
}
// UDP sendto 成功时通常 >0;若出现 0,多半是 data 为空(已提前返回)
return bytes_sent;
}
} // namespace UDP
void get_fd_set(const std::vector<Socket_FD> &fds, fd_set &ret_fd_set,
int &max_fd) {
FD_ZERO(&ret_fd_set); // 清空读集合
// 初始化最大文件描述符为 -1
max_fd = -1;
// 将所有套接字添加到fd_set中,并找到最大套接字描述符
for (Socket_FD fd : fds) {
FD_SET(fd, &ret_fd_set);
if (fd > max_fd) {
max_fd = fd; // 更新最大文件描述符
}
}
}
timeval get_timeout(int timeout_ms) {
timeval timeout{};
timeout.tv_sec = timeout_ms / 1000; // 秒
timeout.tv_usec = (timeout_ms % 1000) * 1000; // 微秒
return timeout;
}
Ret<std::vector<Socket_FD>>
select_read(const std::vector<Socket_FD> &recv_fd_vector, int timeout_ms) {
std::vector<Socket_FD> ret;
fd_set fds;
int max_fd = -1;
get_fd_set(recv_fd_vector, fds, max_fd);
timeval timeout = get_timeout(timeout_ms);
int result = select(max_fd + 1, &fds, nullptr, nullptr, &timeout);
if (result <= 0) {
return ret;
}
for (Socket_FD fd : recv_fd_vector) {
if (FD_ISSET(fd, &fds)) {
ret.push_back(fd);
}
}
return ret;
}
Ret<std::vector<Socket_FD>> select_write(std::vector<Socket_FD> fd_vector,
int timeout_ms) {
std::vector<Socket_FD> ret;
fd_set fds;
int max_fd = -1;
get_fd_set(fd_vector, fds, max_fd);
timeval timeout = get_timeout(timeout_ms);
int result = select(max_fd + 1, nullptr, &fds, nullptr, &timeout);
if (result <= 0) {
return ret;
}
for (Socket_FD fd : fd_vector) {
if (FD_ISSET(fd, &fds)) {
ret.push_back(fd);
}
}
return ret;
}
Ret<bool> select_write(Socket_FD that, int timeout_ms) {
auto r = select_write(std::vector<Socket_FD>{that}, timeout_ms);
if (!r) {
return false;
}
return !r.value().empty();
}
Ret<bool> select_read(Socket_FD that, int timeout_ms) {
auto r = select_read(std::vector<Socket_FD>{that}, timeout_ms);
if (!r) {
return false;
}
return !r.value().empty();
}
Ret<Sockaddr_In> get_dest_addr(Socket_FD that) {
Sockaddr_In ret;
sockaddr_in addr{};
socklen_t addrLen = sizeof(addr);
if (::getpeername(that, reinterpret_cast<sockaddr*>(&addr), &addrLen) == SOCKET_ERROR) {
LOG_FD_Debug(that, "获取目标地址错误!");
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
char ipbuf[INET_ADDRSTRLEN]{};
if (!::inet_ntop(AF_INET, &addr.sin_addr, ipbuf, sizeof(ipbuf))) {
// inet_ntop 失败:返回标准错误
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
ret.ip = ipbuf;
ret.port = ntohs(addr.sin_port);
return ret;
}
Ret<Sockaddr_In> get_addr(Socket_FD that) {
Sockaddr_In ret;
sockaddr_in localAddr{};
socklen_t addrLen = sizeof(localAddr);
if (::getsockname(that, reinterpret_cast<sockaddr*>(&localAddr), &addrLen) == SOCKET_ERROR) {
LOG_FD_Debug(that, "获取socket地址错误!");
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::unknown_error)); // 或 get_sys_ex()
}
char ipbuf[INET_ADDRSTRLEN]{};
if (!::inet_ntop(AF_INET, &localAddr.sin_addr, ipbuf, sizeof(ipbuf))) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument));
}
ret.ip = ipbuf;
ret.port = ntohs(localAddr.sin_port);
return ret;
}
std::string socket_base_info(Socket_FD that) {
auto addr = get_addr(that);
auto dest_addr = get_dest_addr(that);
auto s1 = addr ? addr->to_string() : Psc::to_string(addr.error());
auto s2 = dest_addr ? dest_addr->to_string() : to_string(dest_addr.error());
return "[" + s1 + "]=>[" + s2 + "]";
}
Ret<void> set_reuse(Socket_FD that, bool enable) {
int flag = enable ? 1 : 0;
auto ret = setsockopt(that, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<char *>(&flag), sizeof(flag));
if (ret < 0) {
LOG_FD_Debug(that, VAR_STR_2(that, enable) +
"无法设置 SO_REUSEADDR!")
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument, ret));
}
return Ret<void>();
}
Ret<void> set_debug(Socket_FD that, bool enable) {
int flag = enable ? 1 : 0;
auto ret = setsockopt(that, SOL_SOCKET, SO_DEBUG,
reinterpret_cast<char *>(&flag), sizeof(flag));
if (ret < 0) {
LOG_FD_Debug(that, VAR_STR_2(that, enable) + "无法设置 SO_DEBUG!")
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument, ret));
}
return Ret<void>();
}
Ret<int> get_socket_opt(Socket_FD fd, int optname) {
int optionValue;
socklen_t optionLen = sizeof(optionValue);
auto r = getsockopt(fd, SOL_SOCKET, optname,
reinterpret_cast<char *>(&optionValue), &optionLen);
if (r == SOCKET_ERROR) {
LOG_FD_Debug(fd, std::to_string(optname) + "获取套接字错误!")
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument, r));
}
return optionValue;
}
Ret<void> set_reuse_addr(Socket_FD that, bool enable) {
int opt = enable ? 1 : 0;
auto r = setsockopt(that, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt,
sizeof(opt));
if (r == SOCKET_ERROR) {
return unexpected<Enum_Err<NetError>>(Enum_Err(NetError::invalid_argument, r));
}
return Ret<void>();
}
} // namespace Psc::socket
#undef SOCKET_END
+115
View File
@@ -0,0 +1,115 @@
#ifndef SOCKET_H
#define SOCKET_H
#include "../Base/expected.h"
#include "../Base/export.h"
#include "../Base/codec.h"
#include "../JSON.h"
#include "Core/spdlog/export.h"
#include "NetError.h"
#include <cstdint>
#include <set>
/*
套接字连接过程 客户端(发数据端口)/服务端(收数据端口)
1. 创建套接字
2. 绑定地址和端口 (bind)
3. 监听连接 (listen) / 接受连接 (accept)
4. send() recv()
5. 关闭连接 (close)
*/
namespace Psc::socket {
Ret<Socket_FD> create_socket_fd(Socket_Type type);
Ret<void> set_reuse(Socket_FD that, bool enable);
// 开启了一个调试模式,允许内核在网络操作中记录详细的信息。可以通过查看内核日志或tcpdump、Wireshark、strace来实际观察这些调试信息。
Ret<void> set_debug(Socket_FD that, bool enable);
Ret<void> set_block(Socket_FD that, bool blocking);
Ret<void> set_reuse_addr(Socket_FD that, bool enable);
Ret<void> set_buffer_size(Socket_FD that, size_t buffer_size);
Ret<std::vector<Socket_FD>> select_write(std::vector<Socket_FD> fds,
int timeout_ms);
Ret<std::vector<Socket_FD>> select_read(const std::vector<Socket_FD> &recv_fd_vector, int timeout_ms);
Ret<bool> select_write(Socket_FD that, int timeout_ms);
Ret<bool> select_read(Socket_FD that, int timeout_ms);
Ret<Sockaddr_In> get_addr(Socket_FD that);
Ret<Sockaddr_In> get_dest_addr(Socket_FD that);
std::string socket_base_info(Socket_FD that);
// 对于 TCP 套接字,fd 是一个已经连接的套接字(通过 connect() 或 accept()
// 获得的)。 对于 UDP 套接字,你可以使用 connect()
// 指定一个默认的目标地址(然后使用 send()),或者直接使用 sendto()
// 来指定目标地址。
// NetError::in_progress
// NetError::invalid_argument
Ret<bool> connect(Socket_FD that, const Sockaddr_In &addr);
Ret<void> close(Socket_FD that);
Ret<int> send(Socket_FD connected_fd, const std::string &data);
Ret<int> send(Socket_FD connected_fd, void* data, std::size_t size);
Ret<std::string> recv_all(Socket_FD connected_fd);
Ret<std::string> recv_once(Socket_FD fd, int chunk_size);
Ret<void> bind(Socket_FD that, const Sockaddr_In &addr);
Ret<std::string> recv_all_until_idle(Socket_FD that, int chunk_size, size_t max_bytes, int idle_timeout_ms, bool eof_is_ok );
namespace TCP {
Ret<void> set_no_delay(Socket_FD that, bool no_delay);
Ret<void> set_keep_alive(Socket_FD that, bool enable);
Ret<std::optional<Accept_Info>> accept(Socket_FD that);
Ret<void> listen(Socket_FD that, int backlog);
} // namespace TCP
namespace UDP {
Ret<int> sendto(Socket_FD that, const std::string &client_ip,
uint16_t client_port, const std::string &data);
Ret<std::string> recvfrom(Socket_FD receive_fd, Sockaddr_In *ret,
int chunk_size = 1024 * 1024);
} // namespace UDP
template <typename T> std::string Ret_To_String(const Ret<T> &r) {
auto ec = r.error();
std::ostringstream oss;
oss << "" << to_string(ec.nerr) << "】[" << "]";
if (ec.native) {
auto& nec = ec.native;
oss << " native【" << nec.category().name() << ":" << nec.value() << "】[" << Psc::platform_2_utf8(nec.message()) << "]";
}
return oss.str();
}
class Socket_Base {
public:
virtual ~Socket_Base() = default;
std::optional<Sockaddr_In> address;
Socket_FD socket_fd = -1;
virtual std::string to_string() { return address->to_string(); }
[[nodiscard]] JSON to_Json() const;
virtual void close() {
auto r = socket::close(socket_fd);
if (!r) {
socket_logger->c_debug({}, {}, to_string() + "关闭失败!");
}
socket_fd = -1;
}
};
bool is_needed_reconnect_ec(ERROR_CODE_TYPE r);
bool is_needed_reconnect(Socket_FD that);
bool is_client_need_close_ec(ERROR_CODE_TYPE r);
bool is_client_need_close(Socket_FD that);
} // namespace Psc::socket
#endif // SOCKET_H
+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
+49
View File
@@ -0,0 +1,49 @@
#ifndef SOCKET_P_H
#define SOCKET_P_H
#include "Socket.h"
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
namespace {
struct InitializeWinsock {
InitializeWinsock() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed." << std::endl;
}
}
~InitializeWinsock() { WSACleanup(); }
} Temp;
} // namespace
namespace Psc {}
#define socklen_t int
#else
#include <arpa/inet.h>
#include <cstring>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdexcept>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#define SOCKET_ERROR -1
#endif
namespace Psc::socket {
void log_error(BaseLogger *logger, const std::string &POS,
Psc::socket::Socket_FD fd, const std::string &MSG);
}
#define LOG_FD_ERROR(fd, MSG) log_error(socket_logger, LOG_POS, fd, MSG);
#define LOG_FD_Debug(fd, MSG) log_error(socket_logger, LOG_POS, fd, MSG);
#endif
+397
View File
@@ -0,0 +1,397 @@
#ifdef WIN32
#include "Socket_p.h"
namespace Psc::socket {
Ret<bool> connect(SOCKET s, const Sockaddr_In& addr_in) {
SOCKADDR_IN addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(static_cast<u_short>(addr_in.port));
if (::inet_pton(AF_INET, addr_in.ip.c_str(), &addr.sin_addr) != 1) {
return unexpected(Enum_Err(NetError::invalid_argument));
}
if (::connect(s, reinterpret_cast<const SOCKADDR*>(&addr), sizeof(addr)) == 0) {
return true; // 立即成功
}
const int e = ::WSAGetLastError();
// 统一按“非阻塞 connect 语义”:这些不算失败
switch (e) {
case WSAEWOULDBLOCK:
case WSAEINPROGRESS:
case WSAEALREADY:
return true; // 已发起连接,后续用 SO_ERROR 判定最终结果
default:
break;
}
return unexpected(Enum_Err(NetErrorCategory::map_system_error(e), e));
}
Ret<void> set_block(Socket_FD that, bool blocking) {
if (that == -1)
return Ret<void>();
u_long mode = blocking ? 0 : 1; // 0 为阻塞, 1 为非阻塞
if (ioctlsocket(that, FIONBIO, &mode) != 0) {
LOG_FD_Debug(that, VAR_STR_2(that, blocking) + "设置socket阻塞属性错误!")
}
return Ret<void>();
}
NetError NetErrorCategory::map_system_error(int e) noexcept {
switch (e) {
case WSAEINTR: return NetError::interrupted;
case WSAECANCELLED: return NetError::operation_canceled;
case WSAEINVAL: return NetError::invalid_argument;
case WSAEACCES: return NetError::permission_denied;
case WSAENOBUFS: return NetError::resource_exhausted;
case WSAEBADF: return NetError::bad_file_descriptor;
case WSAENOTSOCK: return NetError::not_a_socket;
case WSAEWOULDBLOCK: return NetError::would_block;
case WSAEINPROGRESS: return NetError::in_progress;
case WSAEALREADY: return NetError::already_in_progress;
case WSAEADDRINUSE: return NetError::address_in_use;
case WSAEADDRNOTAVAIL: return NetError::address_not_available;
case WSAENETDOWN: return NetError::network_down;
case WSAENETUNREACH: return NetError::network_unreachable;
case WSAEHOSTUNREACH: return NetError::host_unreachable;
case WSAENOTCONN: return NetError::not_connected;
case WSAEISCONN: return NetError::already_connected;
case WSAECONNREFUSED: return NetError::connection_refused;
case WSAECONNRESET: return NetError::connection_reset;
case WSAECONNABORTED: return NetError::connection_aborted;
case WSAETIMEDOUT: return NetError::timed_out;
default: return NetError::unknown_error;
}
}
ERROR_CODE_TYPE last_socket_ec() {
int wsa = ::WSAGetLastError();
return wsa;
}
namespace TCP {
Ret<std::optional<Accept_Info>> accept(SOCKET listen_fd) {
SOCKADDR_IN clientAddr{};
int clientAddrSize = sizeof(clientAddr);
SOCKET client_fd =
::accept(listen_fd, reinterpret_cast<SOCKADDR*>(&clientAddr), &clientAddrSize);
if (client_fd == INVALID_SOCKET) {
int e = ::WSAGetLastError();
if (e == WSAEWOULDBLOCK || e == WSAEINTR) {
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) {
int e = ::WSAGetLastError();
const auto ne = NetErrorCategory::map_system_error(e);
LOG_FD_Debug(listen_fd, std::string("inet_ntop error: ") + std::system_category().message(e));
return unexpected(Enum_Err(ne, e));
}
info.sockaddr.ip = ipbuf;
info.sockaddr.port = ntohs(clientAddr.sin_port);
return info;
}
}
Ret<std::string> recv_all(SOCKET fd) {
u_long avail = 0;
if (::ioctlsocket(fd, FIONREAD, &avail) != 0) {
int e = ::WSAGetLastError();
return unexpected(Enum_Err(NetErrorCategory::map_system_error(e), e));
}
if (avail == 0) {
// 无法仅靠 FIONREAD 区分“没数据”还是“已关闭但没数据”
// 如果你需要区分:可用 select 读集合 + 再 recv(1字节, MSG_PEEK)
return std::string{};
}
size_t to_read = avail;
std::string buf;
buf.resize(to_read);
size_t total = 0;
while (total < to_read) {
int n = ::recv(fd, buf.data() + total, (int)(to_read - total), 0);
if (n == SOCKET_ERROR) {
int e = ::WSAGetLastError();
// 理论上 FIONREAD>0 后不该 WSAEWOULDBLOCK,但并发/竞态下可能发生
if (e == WSAEWOULDBLOCK || e == WSAEINTR) {
break; // 立刻返回已读部分
}
buf.resize(total);
return unexpected(Enum_Err(NetErrorCategory::map_system_error(e), e));
}
if (n == 0) {
// 对端关闭
buf.resize(total);
if (!buf.empty()) return buf;
return unexpected(Enum_Err(NetError::connection_closed));
}
total += (size_t)n;
}
buf.resize(total);
//std::cout << "bufsize:" << buf.size() << std::endl;
return buf;
}
Ret<std::string> recv_once(Socket_FD fd, int max_bytes) {
if (max_bytes <= 0) return unexpected(Enum_Err(NetError::invalid_argument));
std::string buf;
buf.resize((size_t)max_bytes);
int n = ::recv(fd, buf.data(), max_bytes, 0);
if (n == SOCKET_ERROR) {
int e = ::WSAGetLastError();
if (e == WSAEWOULDBLOCK || e == WSAEINTR) return std::string{};
return unexpected(Enum_Err(NetErrorCategory::map_system_error(e), e));
}
if (n == 0) return unexpected(Enum_Err(NetError::connection_closed));
buf.resize((size_t)n);
return buf;
}
Ret<std::string> recv_all_until_idle(
Socket_FD fd,
int chunk_size,
size_t max_bytes,
int idle_timeout_ms, // 多久没新数据就退出
bool eof_is_ok = true // n==0 是否当作正常结束
) {
if (chunk_size <= 0 || max_bytes == 0) {
return unexpected(Enum_Err(NetError::invalid_argument));
}
std::string buffer;
buffer.reserve(std::min<size_t>(max_bytes, 64 * 1024));
size_t total = 0;
while (total < max_bytes) {
// 用 select 等待可读(避免阻塞卡死;也给 idle 超时提供退出点)
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
TIMEVAL tv;
tv.tv_sec = idle_timeout_ms / 1000;
tv.tv_usec = (idle_timeout_ms % 1000) * 1000;
int s = ::select(0, &rfds, nullptr, nullptr, &tv);
if (s == 0) {
// 超时:认为“暂时读不到了”,退出返回已读
return buffer;
}
if (s == SOCKET_ERROR) {
int e = ::WSAGetLastError();
return unexpected(Enum_Err(NetErrorCategory::map_system_error(e), e));
}
const int want = (int)std::min<size_t>((size_t)chunk_size, max_bytes - total);
buffer.resize(total + (size_t)want);
int n = ::recv(fd, buffer.data() + total, want, 0);
if (n == SOCKET_ERROR) {
int e = ::WSAGetLastError();
if (e == WSAEWOULDBLOCK || e == WSAEINTR) {
buffer.resize(total);
return buffer;
}
buffer.resize(total);
return unexpected(Enum_Err(NetErrorCategory::map_system_error(e), e));
}
if (n == 0) {
buffer.resize(total);
if (eof_is_ok) return buffer;
return unexpected(Enum_Err(NetError::connection_closed));
}
total += (size_t)n;
buffer.resize(total);
}
// 达到上限:防止对端一直发导致无限读
return buffer;
}
bool is_client_need_close_ec(ERROR_CODE_TYPE e) {
switch (e) {
// 连接已失效/不再可用:应关闭
case WSAECONNRESET: // 对端复位
case WSAECONNABORTED: // 连接被中止
case WSAENOTCONN: // 未连接
case WSAETIMEDOUT: // 超时(可视为应关闭)
case WSAESHUTDOWN: // 本端已 shutdown,收发被禁止
return true;
// 连接拒绝通常发生在 connect 阶段;服务端 recv 上很少见。
// 这里按你原逻辑,仍视为应关闭(等价于“不可用”)。
case WSAECONNREFUSED:
return true;
// 非阻塞无数据/暂态:不关闭
case WSAEWOULDBLOCK:
case WSAEINPROGRESS:
case WSAEALREADY:
case WSAEINTR:
return false;
// 其他错误:保守策略,不直接关闭(也可改成 true 更激进)
default:
return false;
}
}
bool is_client_need_close(SOCKET s) {
char buf[1];
int r = ::recv(s, buf, sizeof(buf), MSG_PEEK);
if (r == 0) {
// 对端正常关闭(FIN/EOF
return true;
}
if (r == SOCKET_ERROR) {
const int e = ::WSAGetLastError();
return Psc::socket::is_client_need_close_ec(e);
}
// r > 0:还能读到数据,连接正常
return false;
}
bool is_needed_reconnect_ec(ERROR_CODE_TYPE e) {
switch (e) {
// 这些表示连接已失效/网络不可达:建议重连
case WSAECONNRESET: // 远端强制关闭
case WSAECONNABORTED: // 连接被中止
case WSAENOTCONN: // 未连接
case WSAETIMEDOUT: // 超时
case WSAENETDOWN: // 网络子系统不可用
case WSAENETUNREACH: // 网络不可达
case WSAEHOSTUNREACH: // 主机不可达
case WSAECONNREFUSED: // 连接被拒绝(多见于 connect 阶段;这里出现也可视为需重连)
return true;
// 这些表示“现在没数据/可重试/被打断”:不重连
case WSAEWOULDBLOCK: // 非阻塞暂时无数据
case WSAEINPROGRESS: // 操作进行中
case WSAEALREADY: // 已在进行
case WSAEINTR: // 被中断
return false;
// 其他错误:按你的策略决定。这里保守处理为“不重连”
default:
return false;
}
}
bool is_needed_reconnect(SOCKET s) {
char buf[1];
int r = ::recv(s, buf, sizeof(buf), MSG_PEEK);
if (r == 0) {
// 对端正常关闭(FIN/EOF
return true;
}
if (r == SOCKET_ERROR) {
const int e = ::WSAGetLastError();
return is_needed_reconnect_ec(e);
}
// r > 0:能窥探到数据,连接仍然活着
return false;
}
namespace UDP {
Ret<std::string> recvfrom(SOCKET 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<size_t>(chunk_size));
SOCKADDR_IN client_addr{};
int addr_len = sizeof(client_addr);
int n = ::recvfrom(receive_fd,
buffer.data(),
chunk_size,
0,
reinterpret_cast<SOCKADDR*>(&client_addr),
&addr_len);
if (n == SOCKET_ERROR) {
const int e = ::WSAGetLastError();
// 非阻塞:暂时无数据 / 被中断:维持你原来的语义,返回空串
if (e == WSAEWOULDBLOCK || e == WSAEINTR) {
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 == 0UDP 允许 0 长度 datagram,依然需要填来源地址
char client_ip[INET_ADDRSTRLEN]{};
if (::inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN) == nullptr) {
const int e = ::WSAGetLastError();
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<size_t>(n));
return buffer;
}
}
} // namespace Psc::socket
#endif
+248
View File
@@ -0,0 +1,248 @@
#include "TCP_Client.h"
#include "Core/Statistics/Frequency_Limit.h"
#include "Socket_p.h"
#include <chrono>
#include <string>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <errno.h>
#endif
namespace Psc::socket {
static bool fd_writable_now(int fd) {
#ifdef _WIN32
fd_set wfds;
FD_ZERO(&wfds);
FD_SET((SOCKET)fd, &wfds);
timeval tv{};
tv.tv_sec = 0; tv.tv_usec = 0;
int r = select(0, nullptr, &wfds, nullptr, &tv);
return r > 0 && FD_ISSET((SOCKET)fd, &wfds);
#else
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
timeval tv{};
tv.tv_sec = 0; tv.tv_usec = 0;
int r = select(fd + 1, nullptr, &wfds, nullptr, &tv);
return r > 0 && FD_ISSET(fd, &wfds);
#endif
}
void TCP_Client::create() {
send_buffer.init(max_buffer_size);
auto rsf = socket::create_socket_fd(Socket_Type::TCP);
if (!rsf) {
last_err = rsf.error();
socket_logger->c_debug("Tcp_Client::create", {}, LOG_POS + last_err.to_string());
return;
}
socket_fd = rsf.value();
set_state(Not_Set_Field);
auto r = socket::set_reuse(socket_fd, true)
.and_then([this]() { return socket::set_block(socket_fd, false); })
.and_then([this]() { return socket::TCP::set_keep_alive(socket_fd, true); })
.and_then([this]() { return socket::TCP::set_no_delay(socket_fd, true); })
.and_then([this]() { return socket::set_reuse_addr(socket_fd, true); })
;
if (!r) {
last_err = r.error();
socket_logger->error("Tcp_Client::create", {}, Psc::to_string(last_err) + " " + LOG_POS);
(void)socket::close(socket_fd);
socket_fd = -1;
set_state(Not_Created);
return;
}
// 这里最好检查 dest_address 是否已设置(你自己加一个标志位也行)
set_state(Reconnecting);
next_reconnect_tp = std::chrono::steady_clock::now();
tick(); // 立即尝试一次
}
void TCP_Client::schedule_recreate(Enum_Err<NetError> e) {
last_err = e;
(void)socket::close(socket_fd);
set_state(Not_Created);
next_reconnect_tp = std::chrono::steady_clock::now() + std::chrono::milliseconds(reconnect_backoff_ms);
}
bool TCP_Client::start_connect() {
if (socket_fd == -1) return false;
auto r = socket::connect(socket_fd, dest_address);
if (!r) {
last_err = r.error();
if (last_err.nerr == NetError::in_progress) {
set_state(Connecting);
return false; // 还没完成
}
if (last_err.nerr == NetError::already_connected) {
set_state(Wait_Check);
return true;
}
socket_logger->c_debug({}, {}, "连接失败:"+ VAR_STR_2(last_err, *this) + "\n");
// 其它错误:进入退避重连
schedule_recreate(last_err);
return false;
}
return false;
}
bool TCP_Client::check_writeable() {
return true;
}
void TCP_Client::tick() {
if (socket_fd == -1) return;
auto st = state.load();
auto now = std::chrono::steady_clock::now();
if (st == Not_Created) {
create();
return;
}
if (st == Reconnecting) {
if (now >= next_reconnect_tp) {
(void) start_connect();
}
return;
}
if (st == Connecting || st == Wait_Check) {
set_state(Connected);
return;
}
if (st == Connected) {
flush_send_buffer();
return;
}
}
std::string TCP_Client::read() {
if (socket_fd == -1) return "";
// 如果还没连上,先推进连接状态
tick();
if (state.load(std::memory_order_relaxed) != Connected) return "";
auto ret = socket::recv_all(socket_fd);
if (!ret) {
Enum_Err e = ret.error();
// 下面这几个名字按你 NetError 实际枚举改:
// - would_block: 非阻塞没数据,不算错
// - in_progress/not_connected: 连接没完成
if (e.nerr == NetError::would_block) return "";
static Frequency_Limit drop_log_fl;
if (drop_log_fl.test()) {
socket_logger->c_debug("Tcp_Client::read recv失败", {},
to_string() + " " + Ret_To_String(ret) + " " + LOG_POS);
}
// 断线类错误:进入退避重连(必要时你也可以 close+重建fd)
schedule_recreate(e);
return "";
}
auto r = ret.value();
return r;
}
void TCP_Client::flush_send_buffer() {
if (socket_fd == -1) return;
if (state.load(std::memory_order_relaxed) != Connected) return;
if (send_buffer.empty()) return;
constexpr uint32_t patch_len = 1500;
std::string buf;
buf.resize(patch_len);
while (true) {
uint32_t n = send_buffer.peek_best_effort(buf.data(), patch_len);
if (n == 0) break;
auto r = socket::send(socket_fd, buf.data(), n);
if (!r) {
last_err = r.error();
if (last_err.nerr == NetError::would_block) break;
if (last_err.nerr == NetError::connection_aborted) {
schedule_recreate(last_err);
}
else if (last_err.nerr == NetError::connection_reset) {
schedule_recreate(last_err);
} else {
socket_logger->c_debug("Tcp_Client::send", {}, "触发重连 " + VAR_STR_2(last_err, *this));
schedule_recreate(last_err);
}
return;
}
send_buffer.skip_best_effort((uint32_t)r.value());
}
}
void TCP_Client::send(const std::string& data) {
if (data.empty()) return;
(void)send_buffer.write_best_effort((const uint8_t*)data.data(), (uint32_t)data.size());
if (state != Connected) return;
tick();
flush_send_buffer();
}
std::string TCP_Client::to_string() {
return VAR_STR_4(socket_fd, address, dest_address, state);
}
void TCP_Client::close() {
if (socket_fd != -1) {
auto r = socket::close(socket_fd);
(void)r;
socket_fd = -1;
set_state(Not_Created);
}
}
void TCP_Client::set_state(State s) {
auto old_state = state.load();
std::ostringstream oss;
oss << to_string() << "状态变化:" << Psc::to_string(s) << "===>" << Psc::to_string(old_state) << std::endl;
socket_logger->debug({}, {}, oss.str());
if (on_state_change) on_state_change(state, s);
state = s;
}
TCP_Client::~TCP_Client() {
TCP_Client::close();
}
} // namespace Psc::socket
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include "Core/Base/RingBuffer.hpp"
#include "Core/Statistics/Statistics.h"
#include "Socket.h"
#include <atomic>
#include <chrono>
namespace Psc::socket {
class TCP_Client : public Socket_Base {
public:
~TCP_Client() override;
void set_buffer_size(size_t size) { max_buffer_size = size; }
void set_dest_address(const Sockaddr_In& a) { dest_address = a; }
void create();
// 建议:外部周期性调用一次(比如主循环/线程每 1~10ms)
void tick();
std::string read();
void send(const std::string& data);
std::string to_string() override;
enum State {
Not_Created,
Not_Set_Field,
Reconnecting,
Connecting,
Wait_Check,
Connected
};
void close() override;
std::atomic<State> state{Not_Created};
void set_state(State s);
std::function<void(State old_state, State new_state)> on_state_change = nullptr;
Enum_Err<NetError> last_err = Enum_Err(NetError::unknown_error);
protected:
size_t max_buffer_size = 1024 * 1024;
StreamRingBuffer_ST send_buffer;
Sockaddr_In dest_address{};
int reconnect_backoff_ms = 200;
std::chrono::steady_clock::time_point next_reconnect_tp{};
// 内部流程
void schedule_recreate(Enum_Err<NetError> e);
bool start_connect(); // 发起 connect(可能 EINPROGRESS
bool check_writeable(); // EINPROGRESS 后检查是否真连上
void flush_send_buffer(); // 尝试把 ringbuffer 发出去
};
} // namespace Psc::socket
+315
View File
@@ -0,0 +1,315 @@
#include "TCP_Server.h"
#include "Core/Statistics/Frequency_Limit.h"
#include "Core/Statistics/Statistics.h"
#include "Core/transmit_protocol/core/statistics.h"
#include "Socket_p.h"
#include <unordered_set>
namespace Psc::socket {
TCP_Server::~TCP_Server() {
if (socket_fd != -1) {
socket_logger->c_debug({},{}, "析构关闭的Server_Socket" + TCP_Server::to_string());
TCP_Server::close();
}
}
TCP_Server &TCP_Server::set_tcp_no_delay(bool _no_delay) {
this->no_delay = _no_delay; return *this;
}
void TCP_Server::close() {
if (socket_fd == -1) {
socket_logger->c_debug({},{}, "警告 重复关闭的Server_Socket,提前退出!" + LOG_POS);
return;
}
std::ostringstream oss;
oss << to_string() << "开始关闭, 剩余连接数:" << tcp_clients.size() << " ";
for (auto &kv : tcp_clients) {
const Socket_FD fd = kv.first;
auto &conn = kv.second;
auto &info = conn->info;
oss << info.to_string();
auto r = Psc::socket::close(fd);
if (!r) {
oss << info.to_string() + "连接关闭异常" + Psc::to_string(r.error())
<< " ";
}
}
auto ret = Psc::socket::close(socket_fd);
if (!ret) {
oss << to_string() + "关闭异常" + Psc::to_string(ret.error()) << " ";
}
oss << " 关闭完毕!";
socket_fd = -1;
tcp_clients.clear();
socket_logger->c_debug("Server_Socket", {}, oss.str());
state = Not_Created;
}
TCP_Server& TCP_Server::create() {
auto ret = socket::create_socket_fd(Socket_Type::TCP);
if (!ret) {
socket_logger->c_debug("创建socket失败!", {}, to_string() + VAR_STR_1(state));
return *this;
}
state = Not_Set_Field;
socket_fd = ret.value();
auto r2 =
socket::set_block(socket_fd, false)
.and_then(
[this]() { return socket::set_reuse_addr(socket_fd, true); })
.and_then([this]() {
return socket::TCP::set_no_delay(socket_fd, no_delay);
})
// .and_then([this]() { return socket::set_debug(socket_fd, true); })
.and_then([this]() {
return socket::set_buffer_size(socket_fd, recv_system_buffer_size);
});
if (!r2) {
socket_logger->c_debug("设置属性失败!", {}, to_string() + VAR_STR_2(socket_fd, state));
}
state = Not_bind;
return *this;
}
TCP_Server &TCP_Server::set_connect_system_buffer_size(size_t size) {
this->connect_system_buffer_size = size;
return *this;
}
TCP_Server &TCP_Server::set_connect_user_buffer_size(size_t size) {
this->connect_user_buffer_size = size;
return *this;
}
TCP_Server &TCP_Server::set_recv_system_buffer_size(size_t size) {
this->recv_system_buffer_size = size;
return *this;
}
bool TCP_Server::listen(const std::string &ip, std::uint32_t port) {
return listen(Psc::socket::Sockaddr_In(ip, port));
}
std::string TCP_Server::to_string() {
return "Server_Socket:[" + address->to_string() + "]";
}
void TCP_Server::flush_clients() {
if (state != Working) {
return;
}
if (socket_fd == -1)
return;
auto cur = accept();
while (cur) {
const Socket_FD fd = cur->info.fd;
// insert_or_assign 避免 operator[] 的二次构造/二次查找
auto [it, inserted] = tcp_clients.insert_or_assign(fd, cur);
auto &t = it->second;
socket_logger->c_debug(
"", {}, to_string() + " 添加了连接 [" + t->to_string() + "]");
cur = accept();
}
}
std::shared_ptr<TCP_Connect> TCP_Server::accept() const {
if (socket_fd == -1)
return nullptr;
auto rc = socket::TCP::accept(socket_fd);
if (!rc) {
socket_logger->error("accept", {}, Ret_To_String(rc) + " " + LOG_POS);
return nullptr;
}
auto cur = rc.value(); // std::optional<Accept_Info>
if (!cur)
return nullptr;
auto cfg =
socket::set_block(cur->fd, false)
.and_then([&]() { return socket::set_reuse_addr(cur->fd, true); })
.and_then(
[&]() { return socket::set_buffer_size(cur->fd, connect_system_buffer_size); });
if (!cfg) {
// 先记录“配置失败原因”
socket_logger->error("accept post-config failed", {},
Ret_To_String(cfg) + " " + LOG_POS);
// 再尝试关闭,并记录“关闭是否成功”
auto cr = socket::close(cur->fd);
if (!cr) {
socket_logger->error("accept post-config close failed", {},
Ret_To_String(cr) + " " + LOG_POS);
}
return nullptr;
}
auto ret = std::make_shared<TCP_Connect>();
ret->info = *cur;
ret->send_buffer.init(connect_user_buffer_size);
return ret;
}
bool TCP_Server::listen(const Sockaddr_In &address) {
this->address = address;
auto ret = socket::bind(socket_fd, address);
if (!ret) {
state = Not_bind;
return false;
}
auto r = socket::TCP::listen(socket_fd, 100);
if (!r) {
state = Not_Listen;
return false;
}
state = Working;
return true;
}
void TCP_Server::write_to_all_clients(const std::string &data) {
if (socket_fd == -1)
return;
// 去重,避免同一 fd 被多次 close/erase
std::unordered_set<Socket_FD> need_close_fds;
for (auto &kv : tcp_clients) {
const Socket_FD fd = kv.first;
auto &conn = kv.second;
Accept_Info &info = conn->info;
bool ok = conn->send_buffer.write(data.c_str(), data.size());
if (!ok) {
conn->lose_speed.update(data.size());
static Frequency_Limit fl;
if (fl.test()) {
socket_logger->c_debug(
"lose", {},
"警告:" + info.sockaddr.to_string() +
" 发送缓冲已满,已丢弃本次数据。 pending=" +
std::to_string(data.size()) +
" add=" + std::to_string(data.size()) + " max_pending=" +
std::to_string(max_pending_send_bytes) + get_error_message() + " " + LOG_POS);
}
// 不return 仍然尝试发送
} else {
conn->push_speed.update(data.size());
}
std::string buf;
buf.resize(2000);
while (true) {
auto peek = conn->send_buffer.peek_best_effort(buf.data(), buf.size());
//if (peek < 500) break;
if (peek == 0)
break;
auto r_send = socket::send(fd, buf.data(), peek);
if (r_send.has_value()) {
const size_t n = static_cast<size_t>(r_send.value());
conn->send_num.update(n);
conn->send_buffer.skip_best_effort(n);
if (n != peek) {
break;
}
continue;
}
const int sys_ec = r_send.error().native.value();
if (socket::is_client_need_close_ec(sys_ec)) {
need_close_fds.insert(fd);
break;
}
break;
}
}
// 3) 统一关闭(不在遍历 map 时 erase)
for (auto fd : need_close_fds) {
auto it = tcp_clients.find(fd);
if (it == tcp_clients.end())
continue;
auto info = it->second->info;
std::string log = to_string() + " 清除了连接 [" + info.to_string() + "]";
auto r = socket::close(fd);
log += r ? " 成功!" : (" 失败!" + Psc::to_string(r.error()));
tcp_clients.erase(it);
socket_logger->c_debug("", {}, log);
}
}
std::vector<TCP_Server::Read_Info> TCP_Server::read_from_all_clients() {
if (socket_fd == -1)
return {};
std::vector<Read_Info> ret;
auto r_readable = socket::select_read(client_fds(), 0);
if (!r_readable)
return {};
auto readable = r_readable.value();
for (auto fd : readable) {
auto it = tcp_clients.find(fd);
if (it == tcp_clients.end())
continue;
auto &conn = it->second;
auto &info = conn->info;
auto r = socket::recv_all(fd);
if (r.has_value()) {
ret.push_back(Read_Info{info, r.value()});
continue;
}
const int ec = r.error().native.value();
if (is_client_need_close_ec(ec)) {
auto r2 = socket::close(fd);
if (!r2) {
socket_logger->c_debug("tcp服务端", {},
to_string() + VAR_STR_1(fd) + info.to_string() +
"关闭连接失败!!!!" + LOG_POS);
}
tcp_clients.erase(it);
}
}
return ret;
}
std::vector<Socket_FD> TCP_Server::client_fds() {
std::vector<Socket_FD> ret;
ret.reserve(tcp_clients.size());
for (auto &c : tcp_clients)
ret.push_back(c.first);
return ret;
}
std::vector<std::shared_ptr<TCP_Connect>> TCP_Server::get_all_clients() {
std::vector<std::shared_ptr<TCP_Connect>> ret;
ret.reserve(tcp_clients.size());
for (auto &c : tcp_clients)
ret.push_back(c.second);
return ret;
}
} // namespace Psc::socket
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include "Core/Base/RingBuffer.hpp"
#include "Core/Statistics/Statistics.h"
#include "Socket.h"
namespace Psc::socket {
class TCP_Connect {
public:
Accept_Info info;
[[nodiscard]] std::string to_string() const {
return info.to_string();
}
StreamRingBuffer_ST send_buffer;
Speed_Statistics push_speed;
Speed_Statistics lose_speed;
Value_Statistics send_num;
};
class TCP_Server : public Socket_Base {
public:
~TCP_Server() override;
TCP_Server& set_tcp_no_delay(bool no_delay);
void close() override;
TCP_Server& create();
TCP_Server& set_connect_system_buffer_size(size_t size);
TCP_Server& set_connect_user_buffer_size(size_t size);
TCP_Server& set_recv_system_buffer_size(size_t size);
bool listen(const std::string &ip, std::uint32_t port);
std::string to_string() override;
void write_to_all_clients(const std::string &data);
struct Read_Info {
Accept_Info info;
std::string data;
};
enum State {
Not_Created,
Not_Set_Field,
Not_bind,
Not_Listen,
Working
};
State state = Not_Created;
std::vector<Read_Info> read_from_all_clients();
std::vector<Socket_FD> client_fds();
std::vector<std::shared_ptr<TCP_Connect>> get_all_clients();
void flush_clients();
[[nodiscard]] std::shared_ptr<TCP_Connect> accept() const;
bool listen(const Sockaddr_In &address);
protected:
std::map<Socket_FD, std::shared_ptr<TCP_Connect>> tcp_clients;
size_t connect_user_buffer_size = 1024 * 1024;
size_t connect_system_buffer_size = 4096 * 10;
size_t max_pending_send_bytes = 1024 * 1024;
size_t recv_system_buffer_size = 1024 * 1024;
bool no_delay = true;
};
}
+82
View File
@@ -0,0 +1,82 @@
// UDP_Client.cpp
#include "UDP_Client.h"
namespace Psc::socket {
void UDP_Client::create() {
// 关闭旧 fd
if (socket_fd != -1) {
(void)socket::close(socket_fd);
socket_fd = -1;
}
connected = false;
auto rsf = socket::create_socket_fd(Socket_Type::UDP);
if (!rsf) {
// 你工程里有 logger 的话在这里打日志;否则静默失败
return;
}
socket_fd = rsf.value();
// 常见选项(失败也不致命:按你的风格你可以选择 fail_fast 或记录日志后 return
auto r = socket::set_reuse(socket_fd, true).and_then([this]() {
return socket::set_block(socket_fd, false);
});
if (!r) {
socket_logger->c_debug({}, {}, to_string() + "设置属性失败,强制退出!");
}
}
bool UDP_Client::connect() {
if (socket_fd == -1) {
create();
if (socket_fd == -1) return false;
}
auto r = socket::connect(socket_fd, dest_address);
if (!r) {
connected = false;
return false;
}
connected = r.value(); // 通常为 true
return connected;
}
std::string UDP_Client::read() {
if (socket_fd == -1) return {};
Sockaddr_In peer{};
auto r = socket::UDP::recvfrom(socket_fd, &peer, read_chunk_size);
if (!r) {
// 这里可以按需:返回空串 / 抛异常 / 记录日志
return {};
}
return r.value();
}
void UDP_Client::send(const std::string& data) {
if (socket_fd == -1) {
create();
if (socket_fd == -1) return;
}
// 为了不依赖 Sockaddr_In 的 ip/port 字段,这里走 “UDP connect + send()”
if (!connected) {
if (!connect()) return;
}
(void)socket::send(socket_fd, data);
}
void UDP_Client::close() {
if (socket_fd != -1) {
(void)socket::close(socket_fd);
socket_fd = -1;
}
connected = false;
state = Not_Created;
}
std::string UDP_Client::to_string() {
return VAR_STR_5(dest_address.to_string(), socket_fd, connected, blocking, read_chunk_size);
}
} // namespace Psc::socket
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "Socket.h"
namespace Psc::socket {
class UDP_Client {
public:
void set_dest_address(const Sockaddr_In& _dest_address) { dest_address = _dest_address; }
void set_dest_address(std::string ip, std::uint32_t port) { dest_address = Sockaddr_In(ip, port); }
void create();
bool connect();
std::string read();
void send(const std::string& data);
Sockaddr_In dest_address;
void close();
std::string to_string();
enum State {
Not_Created,
Not_Set_Field,
Working,
};
State state = Not_Created;
private:
Socket_FD socket_fd = -1;
bool connected = false;
bool blocking = true; // 如需非阻塞,可改成参数/成员 setter
int read_chunk_size = 1024 * 1024;
};
}
// 重启的观测
+149
View File
@@ -0,0 +1,149 @@
// UDP_Server.cpp
#include "UDP_Server.h"
#include <algorithm>
namespace Psc::socket {
static bool same_endpoint(const Sockaddr_In& a, const Sockaddr_In& b) {
return a.ip == b.ip && a.port == b.port;
}
void UDP_Server::create() {
bound = false;
has_last_peer = false;
clients.clear();
auto rsf = socket::create_socket_fd(Socket_Type::UDP);
if (!rsf) {
return;
}
socket_fd = rsf.value();
state = Not_Set_Field;
// 只能非阻塞
auto r2 = socket::set_block(socket_fd, false)
.and_then([this]() { return socket::set_reuse_addr(socket_fd, true); })
.and_then([this]() { return socket::set_debug(socket_fd, true); })
.and_then([this]() { return socket::set_buffer_size(socket_fd, buffer_size); });
if (!r2) {
state = Not_Bind;
}
socket_fd = rsf.value();
}
bool UDP_Server::bind() {
if (socket_fd == -1) {
create();
if (socket_fd == -1) return false;
}
auto r = socket::bind(socket_fd, bind_address);
if (!r) {
bound = false;
return false;
}
state = Working;
bound = true;
return true;
}
// std::string UDP_Server::read() {
// if (!bound) {
// if (!bind()) return {};
// }
//
// Sockaddr_In peer{};
// auto r = socket::UDP::recvfrom(socket_fd, &peer, read_chunk_size);
// if (!r) {
// return {};
// }
//
// last_peer = peer;
// has_last_peer = true;
//
// // 收集 client:去重加入
// const bool exists =
// std::any_of(clients.begin(), clients.end(),
// [&](const Sockaddr_In& c) { return same_endpoint(c, peer); });
// if (!exists) {
// clients.push_back(peer);
// }
//
// return r.value();
// }
void UDP_Server::tick() {
if (!bound) {
if (!bind()) return;
}
Sockaddr_In peer{};
auto r = socket::UDP::recvfrom(socket_fd, &peer, read_chunk_size);
if (!r) return;
if (peer.ip == "" && peer.port == 0) return; // 不知道为什么会有这个
last_peer = peer;
has_last_peer = true;
// 连接/断开切换:存在 => 断开(移除),不存在 => 连接(加入)
auto it = std::find_if(clients.begin(), clients.end(),
[&](const Sockaddr_In& c) { return same_endpoint(c, peer); });
if (it == clients.end()) {
// 第一次:连接
clients.push_back(peer);
// 可选:你可以在这里记录事件类型
// last_event = PeerConnected;
} else {
// 第二次:断开
clients.erase(it);
// last_event = PeerDisconnected;
}
// 如果“第一次/第二次包”只是控制含义,直接不向上层返回数据:
return;
// 如果你仍想把该包内容交给上层处理,改成:
// return r.value();
}
void UDP_Server::close() {
if (socket_fd != -1) {
(void)socket::close(socket_fd);
socket_fd = -1;
state = Not_Created;
}
bound = false;
has_last_peer = false;
clients.clear();
}
void UDP_Server::reply_last_peer(const std::string& data) {
if (socket_fd == -1 || !bound || !has_last_peer) return;
(void)socket::UDP::sendto(socket_fd, last_peer.ip, last_peer.port, data);
}
void UDP_Server::send_to(const std::string& ip, uint16_t port, const std::string& data) {
if (socket_fd == -1) {
create();
if (socket_fd == -1) return;
}
if (!bound) {
if (!bind()) return;
}
(void)socket::UDP::sendto(socket_fd, ip, port, data);
}
void UDP_Server::write_to_all_clients(const std::string& msg) {
if (socket_fd == -1) return;
if (!bound) {
if (!bind()) return;
}
for (const auto& c : clients) {
(void)socket::UDP::sendto(socket_fd, c.ip, c.port, msg);
}
}
}
+43
View File
@@ -0,0 +1,43 @@
// UDP_Server.h
#pragma once
#include "Socket.h"
namespace Psc::socket {
class UDP_Server {
public:
void set_bind_address(const Sockaddr_In& addr) { bind_address = addr; }
void set_bind_address(const std::string& ip, std::uint32_t port) {
bind_address = Sockaddr_In(ip, port);
}
[[nodiscard]] std::string to_string() const {
return bind_address.to_string();
}
void create();
bool bind();
enum State {
Not_Created,
Not_Set_Field,
Not_Bind,
Working,
};
State state = Not_Created;
void tick();
void close();
// 回复到最近一次 read() 的对端(如果你想做 echo/server 常用)
void reply_last_peer(const std::string& data);
// 直接发给指定地址(需要你自己提供 ip/port)
void send_to(const std::string& ip, uint16_t port, const std::string& data);
void write_to_all_clients(const std::string & msg);
Sockaddr_In bind_address;
std::vector<Sockaddr_In> clients;
Socket_FD socket_fd = -1;
bool bound = false;
int read_chunk_size = 1024 * 1024;
Sockaddr_In last_peer{};
bool has_last_peer = false;
size_t buffer_size = 4096 * 10;
};
} // namespace Psc::socket