Files
CPP_Core/Socket_old/Socket_win.cpp
T
2026-06-16 10:56:40 +08:00

397 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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