//go:build windows package yu_sys import ( yu_strconv "gogs.qqck.cn/s/tools/strconv" "net" "net/netip" "syscall" "unsafe" ) //go:cgo_import_dynamic WSAStartup WSAStartup "ws2_32.dll" //go:linkname WSAStartup WSAStartup var WSAStartup Address //go:cgo_import_dynamic socket socket "ws2_32.dll" //go:linkname Socket socket var Socket Address //go:cgo_import_dynamic ioctlsocket ioctlsocket "ws2_32.dll" //go:linkname Ioctlsocket ioctlsocket var Ioctlsocket Address //go:cgo_import_dynamic connect connect "ws2_32.dll" //go:linkname Connect connect var Connect Address //go:cgo_import_dynamic select select "ws2_32.dll" //go:linkname Select select var Select Address //go:cgo_import_dynamic send send "ws2_32.dll" //go:linkname Send send var Send Address //go:cgo_import_dynamic recv recv "ws2_32.dll" //go:linkname Recv recv var Recv Address //go:cgo_import_dynamic closesocket closesocket "ws2_32.dll" //go:linkname Closesocket closesocket var Closesocket Address func init() { var j_wsadata syscall.WSAData WSAStartup.Call(0x02_02, unsafe.Pointer(&j_wsadata)) // 调用方可以使用的最高版本的 Windows 套接字规范。 高序字节指定次要版本号;低序字节指定主版本号。 } const ( AF_UNSPEC = 0 // 地址系列未指定。 AF_INET = 2 // Internet 协议版本 4 (IPv4) 地址系列。 AF_IPX = 6 // IPX/SPX 地址系列。 仅当安装了 NWLink IPX/SPX NetBIOS 兼容传输协议时,才支持此地址系列。Windows Vista 及更高版本不支持此地址系列。 AF_APPLETALK = 16 // AppleTalk 地址系列。 仅当安装了 AppleTalk 协议时,才支持此地址系列。Windows Vista 及更高版本不支持此地址系列。 AF_NETBIOS = 17 // NetBIOS 地址系列。 仅当安装了适用于 NetBIOS 的 Windows 套接字提供程序时,才支持此地址系列。32 位版本的 Windows 支持 NetBIOS 的 Windows 套接字提供程序。 默认情况下,此提供程序安装在 32 位版本的 Windows 上。64 位版本的 Windows 不支持 NetBIOS 的 Windows 套接字提供程序,包括 Windows 7、Windows Server 2008、Windows Vista、Windows Server 2003 或 Windows XP。适用于 NetBIOS 的 Windows 套接字提供程序仅支持 类型 参数设置为 SOCK_DGRAM的套接字。适用于 NetBIOS 的 Windows 套接字提供程序与 NetBIOS 编程接口不直接相关。 Windows Vista、Windows Server 2008 及更高版本不支持 NetBIOS 编程接口。 AF_INET6 = 23 // Internet 协议版本 6 (IPv6) 地址系列。 AF_IRDA = 26 // IrDA (IrDA) 地址系列。仅当计算机安装了红外端口和驱动程序时,才支持此地址系列。 AF_BTH = 32 // 蓝牙地址系列。如果计算机安装了蓝牙适配器和驱动程序,则 SP2 或更高版本的 Windows XP 支持此地址系列。 ) const ( SOCK_STREAM = 1 // 一种套接字类型,它通过 OOB 数据传输机制提供排序的、可靠的双向、基于连接的字节流。 此套接字类型对 Internet 地址系列 (AF_INET 或 AF_INET6) 使用传输控制协议 (TCP) 。 SOCK_DGRAM = 2 // 支持数据报的套接字类型,这些数据报是固定 (通常较小) 最大长度的无连接、不可靠的缓冲区。 此套接字类型对 Internet 地址系列 (AF_INET 或 AF_INET6) 使用用户数据报协议 (UDP) 。 SOCK_RAW = 3 // 一种套接字类型,它提供允许应用程序操作下一层协议标头的原始套接字。 若要操作 IPv4 标头,必须在套接字上设置 IP_HDRINCL 套接字选项。 若要操作 IPv6 标头,必须在套接字上设置 IPV6_HDRINCL 套接字选项。 SOCK_RDM = 4 // 提供可靠消息数据报的套接字类型。 此类型的一个示例是 Windows 中的实用常规多播 (PGM) 多播协议实现,通常称为 可靠多播编程。 仅当安装了可靠多播协议时,才支持此 类型 值。 SOCK_SEQPACKET = 5 // 提供基于数据报的伪流数据包的套接字类型。 ) const ( IPPROTO_ICMP = 1 // Internet 控制消息协议 (ICMP) 。 当 af 参数 AF_UNSPEC、 AF_INET或 AF_INET6 且 类型 参数 SOCK_RAW 或未指定时,此值可能为。 Windows XP 及更高版本支持此 协议 值。 IPPROTO_IGMP = 2 // Internet 组管理协议 (IGMP) 。 当 af 参数 AF_UNSPEC、 AF_INET或 AF_INET6 且 类型 参数 SOCK_RAW 或未指定时,此值可能为 。Windows XP 及更高版本支持此 协议 值。 BTHPROTO_RFCOMM = 3 // 蓝牙射频通信 (蓝牙 RFCOMM) 协议。 当 af 参数 AF_BTH 且类型参数 SOCK_STREAM 时,这是一个可能的值。 具有 SP2 或更高版本的 Windows XP 支持此 协议 值。 IPPROTO_TCP = 6 // 传输控制协议 (TCP) 。 当 af 参数 AF_INET 或 AF_INET6 且类型参数 SOCK_STREAM 时,这是一个可能的值。 IPPROTO_UDP = 17 // 用户数据报协议 (UDP) 。 当 af 参数 AF_INET 或 AF_INET6 且类型参数 SOCK_DGRAM 时,这是一个可能的值。 IPPROTO_ICMPV6 = 58 // Internet 控制消息协议版本 6 (ICMPv6) 。 当 af 参数 AF_UNSPEC、 AF_INET 或 AF_INET6 且 类型 参数 SOCK_RAW 或未指定时,此值可能为 。Windows XP 及更高版本支持此 协议 值。 IPPROTO_RM = 113 // 可靠多播的 PGM 协议。 当 af 参数 AF_INET 且类型参数 SOCK_RDM 时,这是一个可能的值。 在 Windows Vista 及更高版本发布的Windows SDK中,此协议也称为IPPROTO_PGM。仅当安装了可靠多播 协议 时,才支持此协议值。 ) const INVALID_SOCKET = 0 const FIONBIO = 2147772030 // 在 套接字上启用或禁用非阻止模式。 lpvInBuffer 参数指向一个无符号长 (QoS) ,如果要启用非阻止模式,则为非零;如果要禁用该模式,则为零。 创建套接字时,它将在阻止模式下运行 (即) 禁用非阻止模式。 这与 BSD 套接字一致。WSAAsyncSelect 或 WSAEventSelect 例程自动将套接字设置为非阻止模式。 如果已在套接字上发出 WSAAsyncSelect 或 WSAEventSelect ,则使用 WSAIoctl 将套接字重新设置为阻止模式的任何尝试都将失败,WSAEINVAL 会失败。 若要将套接字重新设置为阻止模式,应用程序必须首先通过调用 lEvent 参数等于零的 WSAAsyncSelect 来禁用 WSAAsyncSelect,或者通过调用 WSAEventSelect(lNetworkEvents 参数等于零)来禁用 WSAEventSelect。 const SOCKET_ERROR = -1 type Sockaddr interface { Sockaddr() (ptr unsafe.Pointer, len uintptr) } type Sockaddr_in struct { Family uint16 Port uint16 Addr [4]byte /* in_addr */ Zero [8]uint8 } func (sa *Sockaddr_in) Sockaddr() (unsafe.Pointer, uintptr) { return unsafe.Pointer(sa), unsafe.Sizeof(*sa) } type Sockaddr_in6 struct { Family uint16 Port uint16 Flowinfo uint32 Addr [16]byte /* in6_addr */ Scope_id uint32 } func (sa *Sockaddr_in6) Sockaddr() (unsafe.Pointer, uintptr) { return unsafe.Pointer(sa), unsafe.Sizeof(*sa) } type FDSET struct { Count uintptr Fds uintptr // []uintptr } type TIMEVAL struct { TvSec int32 TvUsec int32 } func Socket_connect(host string, port int, sec, usec int32) uintptr { var j_addr netip.Addr var j_err error if j_addr, j_err = netip.ParseAddr(host); j_err != nil { j_addrs, j_err := net.LookupHost(host) if j_err != nil || len(j_addrs) == 0 { return INVALID_SOCKET } if j_addr, j_err = netip.ParseAddr(j_addrs[0]); j_err != nil { return INVALID_SOCKET } } j_fd := uintptr(INVALID_SOCKET) if j_addr.Is4() { if j_fd = Socket.CallUintptr(AF_INET, SOCK_STREAM, IPPROTO_TCP); j_fd == INVALID_SOCKET { return INVALID_SOCKET } } else if j_addr.Is6() { if j_fd = Socket.CallUintptr(AF_INET6, SOCK_STREAM, IPPROTO_TCP); j_fd == INVALID_SOCKET { return INVALID_SOCKET } } else { return INVALID_SOCKET } var j_sockaddr Sockaddr if j_addr.Is4() { j_sockaddr = &Sockaddr_in{ Family: AF_INET, Port: uint16(port)>>8 | uint16(port)<<8, Addr: j_addr.As4(), } } else if j_addr.Is6() { j_sockaddr = &Sockaddr_in6{ Family: AF_INET6, Port: uint16(port)>>8 | uint16(port)<<8, Addr: j_addr.As16(), Scope_id: yu_strconv.ParseUint32(j_addr.Zone()), } } j_argp := 1 if Ioctlsocket.CallInt32(j_fd, FIONBIO, &j_argp) == SOCKET_ERROR { Closesocket.Call(j_fd) return INVALID_SOCKET } j_sec, j_usec := sec, usec const j_timeout = 1000 * 100 _connect: j_sockaddr_ptr, j_sockaddr_len := j_sockaddr.Sockaddr() if j_ok := Connect.CallInt32(j_fd, j_sockaddr_ptr, j_sockaddr_len); j_ok != SOCKET_ERROR && j_ok != 0 { Closesocket.Call(j_fd) return INVALID_SOCKET } if !Socket_is_write(j_fd, 0, j_timeout) { if j_usec > 0 { j_usec -= j_timeout goto _connect } else if j_sec > 0 { j_sec-- j_usec = 1000 * 1000 j_usec -= j_timeout goto _connect } Closesocket.Call(j_fd) return INVALID_SOCKET } j_argp = 0 if Ioctlsocket.CallInt32(j_fd, FIONBIO, &j_argp) == SOCKET_ERROR { Closesocket.Call(j_fd) return INVALID_SOCKET } return j_fd } func Socket_is_write(fd uintptr, sec, usec int32) bool { switch Select.CallInt32(0, 0, unsafe.Pointer(&FDSET{1, fd}), 0, unsafe.Pointer(&TIMEVAL{sec, usec})) { case -1: return false // ErrClose case 0: return false // ErrTimeout case 1: return true default: return false // ErrNot } } func Socket_is_read(fd uintptr, sec, usec int32) bool { switch Select.CallInt32(0, unsafe.Pointer(&FDSET{1, fd}), 0, 0, unsafe.Pointer(&TIMEVAL{sec, usec})) { case -1: return false // ErrClose case 0: return false // ErrTimeout case 1: return true default: return false // ErrNot } } const _send_size_max = 1024 * 1024 * 32 // 32 MB func Socket_write_tcp(fd uintptr, buf []byte, sec, usec int32) bool { if fd == INVALID_SOCKET { return false } if len(buf) == 0 { return true } j_buf_ptr, j_buf_len, j_move := uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), uintptr(0) for j_buf_len > 0 { if !Socket_is_write(fd, sec, usec) { return false } if j_buf_len > _send_size_max { j_move, _, _ = Send.Call(fd, j_buf_ptr, _send_size_max, 0) } else { j_move, _, _ = Send.Call(fd, j_buf_ptr, j_buf_len, 0) } if int32(j_move) < 0 { return false } j_buf_ptr += j_move j_buf_len -= j_move } return true } func Socket_read_tcp(fd uintptr, size int, sec, usec int32) []byte { if fd == INVALID_SOCKET { return nil } if size < 1 { return nil } if !Socket_is_read(fd, sec, usec) { return nil } j_buf := make([]byte, size) j_size, _, _ := Recv.Call(fd, j_buf, len(j_buf), 0) if int32(j_size) < 1 { return nil } j_buf = j_buf[:j_size] return j_buf }