397 lines
12 KiB
C++
397 lines
12 KiB
C++
#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 == 0:UDP 允许 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 |