422 lines
12 KiB
C++
422 lines
12 KiB
C++
#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
|