AHK v1 做本机进程通信、局域网工具或自定义 TCP 协议时,最麻烦的通常不是发送一段文本,而是 Winsock 初始化、地址解析、连接、收发、异步消息和资源释放这些细节。Socket_v1 把这些基础工作封装成客户端、服务端和缓冲区对象。

这个库源自 thqby 的 Socket 实现,由 dbgba 移植到 AHK v1,底层使用 Windows 自带的 Winsock 2.2,不需要单独安装网络组件。

 

主要能力

  • 创建 TCP 客户端和监听服务端,并接受连接。
  • 同步发送、接收 Buffer,或直接使用 UTF-8 等编码收发文本。
  • 通过 onAcceptonReadonConnectonClose 等回调处理异步事件。
  • 封装地址解析、Socket 错误信息、消息长度查询和资源清理。
  • 提供 StartTLS() 接口与外部 TLS 包装对象衔接。

 

客户端、服务端和 Buffer

Socket.Client 负责主动连接,Socket.Server 负责监听端口,服务端收到连接后通过 AcceptAsClient() 得到可收发数据的客户端对象。文本协议可以先从 SendText()RecvText() 入门;二进制协议则使用 Socket.BufferSend()Recv()

库同时支持同步和 Windows 消息驱动的异步模式。同步模式更容易理解,适合短连接和一次性工具;GUI、长连接或需要同时处理多个连接时,再使用异步回调。

 

示例做了什么

示例全部使用 127.0.0.1,先在 19241 端口演示同步收发,再在 19242 端口演示 onAcceptonRead 回调。服务端收到文本后加上 echo: 返回,适合用来理解一条 TCP 消息在两端如何流动。

 

示例代码:Socket_v1_示例.ahk

 

库源码:Socket_v1.ahk

; @description simple implementation of a socket Server and Client.
; @author thqby (Original v2) / dbgba (Ported to v1)
; @date 2025/05/29
; @version 1.0.8
; @note Converted for AutoHotkey v1 compatibility by dbgba.
;       Original source: https://github.com/thqby/ahk2_lib

Socket_v1_OnMessage(wParam, lParam, msg, hwnd) {
	table := Socket._SocketTable()
	if (!table.HasKey(wParam))
		return 0
	sock := table[wParam]
	if (!IsObject(sock))
		return 0
	ev := lParam & 0xFFFF
	err := (lParam >> 16) & 0xFFFF
	if (ev = 1) {
		sock._AsyncSelect(1, -1)
		try sock.OnRead(err)
		catch e
			Socket._ReportCallbackError(sock, "OnRead", e)
		try sock._AsyncSelect(1)
		catch e
			Socket._ReportCallbackError(sock, "OnRead monitor", e)
	} else if (ev = 2) {
		try sock.OnWrite(err)
		catch e
			Socket._ReportCallbackError(sock, "OnWrite", e)
	} else if (ev = 4) {
		try sock.OnOOB(err)
		catch e
			Socket._ReportCallbackError(sock, "OnOOB", e)
	} else if (ev = 8) {
		try sock.OnAccept(err)
		catch e
			Socket._ReportCallbackError(sock, "OnAccept", e)
	} else if (ev = 16) {
		try sock.OnConnect(err)
		catch e
			Socket._ReportCallbackError(sock, "OnConnect", e)
	} else if (ev = 32) {
		try sock.OnClose(err)
		catch e
			Socket._ReportCallbackError(sock, "OnClose", e)
	} else if (ev = 64) {
		try sock.OnQOS(err)
		catch e
			Socket._ReportCallbackError(sock, "OnQOS", e)
	}
	return 0
}

class Socket {
	static TYPE := {STREAM: 1, DGRAM: 2, RAW: 3, RDM: 4, SEQPACKET: 5}
	static AF := {UNSPEC: 0, UNIX: 1, INET: 2, IPX: 6, APPLETALK: 16, NETBIOS: 17, INET6: 23, IRDA: 26, BTH: 32}
	static IPPROTO := {ICMP: 1, IGMP: 2, RFCOMM: 3, TCP: 6, UDP: 17, ICMPV6: 58, RM: 113}
	static MSG := {OOB: 1, PEEK: 2, DONTROUTE: 4, WAITALL: 8, INTERRUPT: 0x10, PUSH_IMMEDIATE: 0x20, PARTIAL: 0x8000}

	_State() {
		static state
		if (!IsObject(state)) {
			state := Object()
			state.WmSocket := 0
			state.WsaStarted := 0
			state.MonitorCount := 0
			state.SocketTable := Object()
		}
		return state
	}

	Init() {
		state := Socket._State()
		if (state.WsaStarted)
			return
		h := DllCall("LoadLibrary", "Str", "ws2_32.dll", "Ptr")
		VarSetCapacity(WSAData, 408, 0)
		err := DllCall("ws2_32\WSAStartup", "UShort", 0x0202, "Ptr", &WSAData, "Int")
		if (err)
			throw Socket._Error(err, -1)
		if (NumGet(WSAData, 2, "UShort") != 0x0202)
			throw Exception("Winsock version 2.2 not available", -1)
		state.WsaStarted := 1
	}

	Cleanup() {
		state := Socket._State()
		if (state.WsaStarted) {
			DllCall("ws2_32\WSACleanup")
			state.WsaStarted := 0
		}
	}

	GetLastError() {
		return DllCall("ws2_32\WSAGetLastError", "Int")
	}

	_Error(err := "", what := -1) {
		if (err = "")
			err := Socket.GetLastError()
		msg := Socket._FormatError(err)
		if (msg = "")
			msg := "Winsock error " err
		return Exception(msg, what, err)
	}

	_ReportCallbackError(sock, stage, e) {
		local msg
		msg := Socket._ErrorMessage(e)
		OutputDebug, % "socket " stage ": " msg
	}

	_ErrorMessage(e) {
		local msg
		if (!IsObject(e))
			return e
		try msg := e.Message
		catch ex
			msg := ""
		if (msg = "")
			msg := "<no message>"
		return msg
	}

	_FormatError(err) {
		pstr := 0
		if (DllCall("FormatMessage", "UInt", 0x1300, "Ptr", 0, "UInt", err, "UInt", 0, "Ptr*", pstr, "UInt", 0, "Ptr", 0) && pstr) {
			msg := StrGet(pstr)
			DllCall("LocalFree", "Ptr", pstr)
			return RTrim(msg, "`r`n ")
		}
		return ""
	}

	_WM_SOCKET() {
		state := Socket._State()
		if (!state.WmSocket)
			state.WmSocket := DllCall("RegisterWindowMessage", "Str", "WM_AHK_SOCKET", "UInt")
		return state.WmSocket
	}

	_SocketTable() {
		return Socket._State().SocketTable
	}

	_EventNameToFlag(name) {
		StringLower, name, name
		if (name = "onread")
			return 1
		if (name = "onoob")
			return 4
		if (name = "onaccept")
			return 8
		if (name = "onconnect")
			return 16
		if (name = "onclose")
			return 32
		if (name = "onqos")
			return 64
		return 0
	}

	_HasHandler(obj, eventName) {
		name := SubStr(eventName, 1, 2) = "On" ? "on" SubStr(eventName, 3) : eventName
		if (obj.HasKey(name)) {
			handler := obj[name]
			if (IsObject(handler))
				return 1
			if (handler != "" && IsFunc(handler))
				return 1
		}
		try className := obj.__Class
		catch e
			className := ""
		if (className != "" && className != "Socket.base" && className != "Socket.Client" && className != "Socket.Server" && className != "Socket.AcceptedClient") {
			if (IsFunc(className "." name) || IsFunc(className "." eventName))
				return 1
		}
		return 0
	}

	_CallHandler(obj, name, args*) {
		local handler, className, bound
		try handler := obj[name]
		catch e
			handler := ""
		if (IsObject(handler))
			return handler.Call(obj, args*)
		if (handler != "" && IsFunc(handler))
			return %handler%(obj, args*)
		try className := obj.__Class
		catch e
			className := ""
		if (className != "" && className != "Socket.base" && className != "Socket.Client" && className != "Socket.Server" && className != "Socket.AcceptedClient" && IsFunc(className "." name)) {
			bound := ObjBindMethod(obj, name)
			return bound.Call(args*)
		}
	}

	_Ptr(value) {
		if IsObject(value) {
			try return value.Ptr + 0
			try return value.ptr + 0
		}
		return value + 0
	}

	_Size(value) {
		if IsObject(value) {
			try return value.Size + 0
			try return value.size + 0
		}
		return 0
	}

	_WSAAddressToString(addr, addrlen) {
		VarSetCapacity(buf, 4096, 0)
		s := 2048
		if (!DllCall("ws2_32\WSAAddressToStringW", "Ptr", addr, "UInt", addrlen, "Ptr", 0, "Ptr", &buf, "UInt*", s, "Int"))
			return StrGet(&buf)
		return ""
	}

	_AddrInfoFromPtr(ptr) {
		if (!ptr)
			return 0
		ai := new Socket.AddrInfo()
		ai.ptr := ptr
		ai._owns := false
		return ai
	}

	class Buffer {
		__New(size := 0, fill := "") {
			if (size < 0)
				size := 0
			capacity := size ? size : 1
			this.SetCapacity("__buf", capacity)
			ObjRawSet(this, "__capacity", capacity)
			ObjRawSet(this, "__size", size)
			if (size && fill != "")
				DllCall("RtlFillMemory", "Ptr", this.Ptr, "UPtr", size, "UChar", fill)
		}

		Ptr {
			get {
				return this.GetAddress("__buf")
			}
		}

		Size {
			get {
				return ObjRawGet(this, "__size")
			}
			set {
				if (value < 0)
					value := 0
				capacity := ObjRawGet(this, "__capacity")
				if (value > capacity) {
					this.SetCapacity("__buf", value)
					ObjRawSet(this, "__capacity", value)
				}
				ObjRawSet(this, "__size", value)
				return value
			}
		}
	}

	class AddrInfo {
		__New(host := "", port := 0, family := 0) {
			Socket.Init()
			this.ptr := 0
			this._owns := true
			this._buf := ""
			if (host = "" && port = "") {
			} else if (family != 1) {
				hintsPtr := 0
				if (family) {
					VarSetCapacity(hints, 48, 0)
					NumPut(family, hints, 4, "Int")
					hintsPtr := &hints
				}
				addr := 0
				err := DllCall("ws2_32\GetAddrInfoW", "WStr", host, "WStr", "" port, "Ptr", hintsPtr, "Ptr*", addr, "Int")
				if (err)
					throw Socket._Error(err, -1)
				this.ptr := addr
			} else {
				VarSetCapacity(buf, 158, 0)
				NumPut(1, buf, 48, "UShort")
				StrPut(host, &buf + 50, "CP0")
				NumPut(0, buf, 0, "Int")
				NumPut(1, buf, 4, "Int")
				NumPut(0, buf, 8, "Int")
				NumPut(0, buf, 12, "Int")
				NumPut(110, buf, 16, "UPtr")
				NumPut(0, buf, 16 + A_PtrSize, "Ptr")
				NumPut(&buf + 48, buf, 16 + 2 * A_PtrSize, "Ptr")
				this._buf := buf
				this.ptr := this.GetAddress("_buf")
			}
		}

		__Delete() {
			if (this._owns && this.ptr && this.family != 1)
				DllCall("ws2_32\FreeAddrInfoW", "Ptr", this.ptr)
		}

		flags {
			get {
				return NumGet(this.ptr + 0, 0, "Int")
			}
		}

		family {
			get {
				return NumGet(this.ptr + 0, 4, "Int")
			}
		}

		socktype {
			get {
				return NumGet(this.ptr + 0, 8, "Int")
			}
		}

		protocol {
			get {
				return NumGet(this.ptr + 0, 12, "Int")
			}
		}

		addrlen {
			get {
				return NumGet(this.ptr + 0, 16, "UPtr")
			}
		}

		canonname {
			get {
				p := NumGet(this.ptr + 0, 16 + A_PtrSize, "Ptr")
				return p ? StrGet(p) : ""
			}
		}

		addr {
			get {
				return NumGet(this.ptr + 0, 16 + 2 * A_PtrSize, "Ptr")
			}
		}

		next {
			get {
				p := NumGet(this.ptr + 0, 16 + 3 * A_PtrSize, "Ptr")
				return Socket._AddrInfoFromPtr(p)
			}
		}

		addrstr {
			get {
				if (this.family = 1)
					return StrGet(this.addr + 2, "CP0")
				return Socket._WSAAddressToString(this.addr, this.addrlen)
			}
		}
	}

	class base {
		__New(family := "", socktype := "", protocol := "") {
			Socket.Init()
			this.Ptr := -1
			this.addr := ""
			this._monitorFlags := 0
			if (family != "")
				this._CreateSocket(family, socktype, protocol)
		}

		__Delete() {
			this.Close()
		}

		__Set(name, value) {
			flag := Socket._EventNameToFlag(name)
			if (flag) {
				if (!IsObject(value) && value != "" && IsFunc(value))
					value := Func(value)
				ObjRawSet(this, name, value)
				this.UpdateMonitoring()
				return value
			}
			ObjRawSet(this, name, value)
			return value
		}

		_CreateSocket(family, socktype, protocol) {
			if (this.Ptr != -1)
				this.Close()
			this.Ptr := DllCall("ws2_32\socket", "Int", family, "Int", socktype, "Int", protocol, "Ptr")
			if (this.Ptr = -1)
				throw Socket._Error(Socket.GetLastError())
		}

		Close() {
			if (this.Ptr = -1 || this.Ptr = "")
				return
			this.UpdateMonitoring(-1)
			DllCall("ws2_32\closesocket", "Ptr", this.Ptr, "Int")
			this.Ptr := -1
		}

		Send(addrInfo, buf, size := "") {
			sz := this._send(addrInfo, buf, size)
			if (sz < 0)
				throw Socket._Error(Socket.GetLastError())
			return sz
		}

		_send(addrInfo, buf, size := "") {
			ptr := Socket._Ptr(buf)
			if (size = "")
				size := Socket._Size(buf)
			return DllCall("ws2_32\sendto", "Ptr", this.Ptr, "Ptr", ptr, "Int", size, "Int", 0, "Ptr", addrInfo.addr, "Int", addrInfo.addrlen, "Int")
		}

		_recv(buf, size := "", flags := 0, from := 0, ByRef len := 0) {
			ptr := Socket._Ptr(buf)
			if (size = "")
				size := Socket._Size(buf)
			if (from)
				sz := DllCall("ws2_32\recvfrom", "Ptr", this.Ptr, "Ptr", ptr, "Int", size, "Int", flags, "Ptr", from, "Int*", len, "Int")
			else
				sz := DllCall("ws2_32\recvfrom", "Ptr", this.Ptr, "Ptr", ptr, "Int", size, "Int", flags, "Ptr", 0, "Ptr", 0, "Int")
			if (sz < 0)
				throw Socket._Error(Socket.GetLastError())
			return sz
		}

		GetLastError() {
			return Socket.GetLastError()
		}

		MsgSize(timeout := 0) {
			FIONBIO := 0x8004667E
			FIONREAD := 0x4004667F
			wasAsync := this.HasKey("_async_select")
			if (!wasAsync) {
				one := 1
				if (timeout || timeout := 0xFFFFFFFF)
					DllCall("ws2_32\ioctlsocket", "Ptr", this.Ptr, "UInt", FIONBIO, "UInt*", one, "Int")
			}
			endTick := A_TickCount + timeout
			size := 0
			Loop {
				DllCall("ws2_32\ioctlsocket", "Ptr", this.Ptr, "UInt", FIONREAD, "UInt*", size, "Int")
				if (size)
					break
				if (A_TickCount >= endTick)
					break
				if (this._IsClosed())
					break
				Sleep, 10
			}
			if (!wasAsync) {
				zero := 0
				DllCall("ws2_32\ioctlsocket", "Ptr", this.Ptr, "UInt", FIONBIO, "UInt*", zero, "Int")
			}
			return size
		}

		_IsClosed() {
			VarSetCapacity(peek, 1, 0)
			r := DllCall("ws2_32\recv", "Ptr", this.Ptr, "Ptr", &peek, "Int", 1, "Int", 2, "Int")
			if (r = 0)
				return true
			if (r < 0) {
				err := Socket.GetLastError()
				return (err != 10035)
			}
			return false
		}

		UpdateMonitoring(start := true) {
			state := Socket._State()
			flags := 0
			if (start > 0) {
				if (Socket._HasHandler(this, "onRead"))
					flags |= 1
				if (Socket._HasHandler(this, "onOOB"))
					flags |= 4
				if (Socket._HasHandler(this, "onAccept"))
					flags |= 8
				if (Socket._HasHandler(this, "onConnect"))
					flags |= 16
				if (Socket._HasHandler(this, "onClose"))
					flags |= 32
				if (Socket._HasHandler(this, "onQOS"))
					flags |= 64
			}
			if (flags) {
				table := Socket._SocketTable()
				if (!state.MonitorCount)
					OnMessage(Socket._WM_SOCKET(), "Socket_v1_OnMessage", 255)
				if (!table.HasKey(this.Ptr)) {
					state.MonitorCount += 1
				}
				table[this.Ptr] := this
				ObjRawSet(this, "_async_select", true)
				this._DefineAsyncMethods()
				this._AsyncSelect(flags, 0)
				return
			}
			if (this.HasKey("_async_select"))
				this.Delete("_async_select")
			this._AsyncSelect(0, 0)
			table := Socket._SocketTable()
			if (table.HasKey(this.Ptr)) {
				table.Delete(this.Ptr)
				state.MonitorCount -= 1
				if (state.MonitorCount <= 0) {
					state.MonitorCount := 0
					OnMessage(Socket._WM_SOCKET(), "")
				}
			}
			if (start > -1) {
				zero := 0
				DllCall("ws2_32\ioctlsocket", "Ptr", this.Ptr, "UInt", 0x8004667E, "UInt*", zero, "Int")
			}
		}

		_AsyncSelect(newFlags, mode := 1) {
			flags := this.HasKey("_monitorFlags") ? this._monitorFlags : 0
			if (mode > 0)
				newFlags := flags | newFlags
			else if (mode < 0)
				newFlags := flags & ~newFlags
			if (mode && flags = newFlags)
				return
			if (DllCall("ws2_32\WSAAsyncSelect", "Ptr", this.Ptr, "Ptr", A_ScriptHwnd, "UInt", Socket._WM_SOCKET(), "UInt", newFlags, "Int"))
				throw Socket._Error(Socket.GetLastError())
			this._monitorFlags := newFlags
		}

		OnRead(err) {
			return Socket._CallHandler(this, "onRead", err)
		}

		OnOOB(err) {
			return Socket._CallHandler(this, "onOOB", err)
		}

		OnAccept(err) {
			return Socket._CallHandler(this, "onAccept", err)
		}

		OnConnect(err) {
			return Socket._CallHandler(this, "onConnect", err)
		}

		OnClose(err) {
			return Socket._CallHandler(this, "onClose", err)
		}

		OnQOS(err) {
			return Socket._CallHandler(this, "onQOS", err)
		}

		OnWrite(err) {
			if (!this.HasKey("_queue"))
				return
			queue := this._queue
			index := queue.index
			max := queue.MaxIndex()
			while (++index <= max) {
				params := queue[index]
				queue.Delete(index)
				sz := this._send(params[1], params[2])
				if (sz < 0) {
					queue[index] := params
					err := Socket.GetLastError()
					break
				}
			}
			if (--index && index >= (queue.MaxIndex() >> 1)) {
				queue.RemoveAt(1, index)
				queue.index := 0
			} else {
				queue.index := index
			}
			if (err && err != 10035)
				throw Socket._Error(err)
		}

		_DefineAsyncMethods() {
			return 0
		}
	}

	class Client extends Socket.base {
		__New(host, port := "", socktype := 1, protocol := 0) {
			Socket.Init()
			this.Ptr := -1
			this.addr := ""
			this._monitorFlags := 0
			this.isConnected := 1
			if IsObject(host)
				this.addrinfo := host
			else
				this.addrinfo := new Socket.AddrInfo(host, port, port = "" ? 1 : 0)
			this._socktype := socktype
			this._protocol := protocol
			err := this.ReConnect(false)
			if (err && err != 10035)
				throw Socket._Error(err, -1)
		}

		ReConnect(next := false) {
			this.isConnected := 0
			if (!next) {
				this._ai := this.addrinfo
				this._last_family := -1
			} else {
				this._ai := IsObject(this._ai) ? this._ai.next : 0
			}
			Loop {
				ai := this._ai
				if (!IsObject(ai))
					return 10061
				if (this._last_family != ai.family && this.Ptr != -1)
					this.Close()
				if (this.Ptr = -1) {
					this.Ptr := DllCall("ws2_32\socket", "Int", ai.family, "Int", this._socktype, "Int", this._protocol, "Ptr")
					if (this.Ptr = -1)
						return Socket.GetLastError()
					this._last_family := ai.family
				}
				this.addr := ai.addrstr
				if (Socket._HasHandler(this, "onConnect"))
					this.UpdateMonitoring()
				if (!DllCall("ws2_32\connect", "Ptr", this.Ptr, "Ptr", ai.addr, "UInt", ai.addrlen, "Int")) {
					this.UpdateMonitoring()
					this.isConnected := 1
					return 0
				}
				err := Socket.GetLastError()
				if (err = 10035)
					return err
				this._ai := ai.next
				if (this.Ptr != -1)
					this.Close()
			}
		}

		OnConnect(err) {
			if (!err) {
				this.isConnected := 1
				return Socket._CallHandler(this, "onConnect", err)
			}
			if (err = 10061) {
				err := this.ReConnect(true)
				if (err = 10035)
					return
			}
			throw Socket._Error(err)
		}

		_DefineAsyncMethods() {
			if (this.HasKey("_queue"))
				return
			queue := []
			queue.index := 0
			this._queue := queue
		}

		_send(buf, size := "") {
			ptr := Socket._Ptr(buf)
			if (size = "")
				size := Socket._Size(buf)
			return DllCall("ws2_32\send", "Ptr", this.Ptr, "Ptr", ptr, "Int", size, "Int", 0, "Int")
		}

		Send(buf, size := "") {
			if (this.HasKey("_queue")) {
				queue := this._queue
				if (!queue.MaxIndex()) {
					sz := this._send(buf, size)
					if (sz < 0) {
						err := Socket.GetLastError()
						if (err != 10035)
							throw Socket._Error(err)
						this._AsyncSelect(2)
					} else {
						return sz
					}
				}
				queue.Push([buf, size])
				return 0
			}
			sz := this._send(buf, size)
			if (sz < 0)
				throw Socket._Error(Socket.GetLastError())
			return sz
		}

		SendText(text, encoding := "UTF-8") {
			StringUpper, encKey, encoding
			isUtf16 := (encKey = "UTF-16" || encKey = "CP1200")
			need := StrPut(text, encoding)
			size := isUtf16 ? (need - 1) * 2 : need - 1
			capacity := isUtf16 ? need * 2 : need
			buf := new Socket.Buffer(capacity)
			buf.Size := size
			StrPut(text, buf.Ptr, need, encoding)
			return this.Send(buf, size)
		}

		_recv(buf, size := "", flags := 0) {
			ptr := Socket._Ptr(buf)
			if (size = "")
				size := Socket._Size(buf)
			sz := DllCall("ws2_32\recv", "Ptr", this.Ptr, "Ptr", ptr, "Int", size, "Int", flags, "Int")
			if (sz < 0)
				throw Socket._Error(Socket.GetLastError())
			return sz
		}

		Recv(timeout := 0, flags := 0) {
			size := this.MsgSize(timeout)
			if (!size)
				return 0
			buf := new Socket.Buffer(size)
			this._recv(buf, size, flags)
			return buf
		}

		RecvText(encoding := "UTF-8", timeout := 0, flags := 0) {
			buf := this.Recv(timeout, flags)
			if IsObject(buf)
				return StrGet(buf.Ptr, buf.Size, encoding)
			return ""
		}

		StartTLS(tls) {
			return tls.wrapSocket(this)
		}
	}

	class Server extends Socket.base {
		__New(port := "", host := "0.0.0.0", socktype := 1, protocol := 0, backlog := 4) {
			Socket.Init()
			this.Ptr := -1
			this.addr := ""
			this._monitorFlags := 0
			ai := new Socket.AddrInfo(host, port, port = "" ? 1 : 0)
			this.addrinfo := ai
			last_family := -1
			ptr := -1
			Loop {
				if (!IsObject(ai))
					break
				if (last_family != ai.family) {
					if (ptr != -1) {
						DllCall("ws2_32\closesocket", "Ptr", ptr, "Int")
						this.Ptr := -1
					}
					ptr := DllCall("ws2_32\socket", "Int", ai.family, "Int", socktype, "Int", protocol, "Ptr")
					if (ptr = -1) {
						ai := ai.next
						continue
					}
					last_family := ai.family
					this.Ptr := ptr
				}
				reuse := 1
				DllCall("ws2_32\setsockopt", "Ptr", ptr, "Int", 0xFFFF, "Int", 4, "Int*", reuse, "Int", 4, "Int")
				if (!DllCall("ws2_32\bind", "Ptr", ptr, "Ptr", ai.addr, "UInt", ai.addrlen, "Int")
					&& !DllCall("ws2_32\listen", "Ptr", ptr, "Int", backlog, "Int")) {
					this.addr := ai.addrstr
					this.UpdateMonitoring()
					this._listenOK := true
					break
				}
				ai := ai.next
			}
			if (this._listenOK)
				return this
			throw Socket._Error(Socket.GetLastError(), -1)
		}

		_accept(ByRef addr := "") {
			addrlen := 128
			VarSetCapacity(addrbuf, addrlen, 0)
			ptr := DllCall("ws2_32\accept", "Ptr", this.Ptr, "Ptr", &addrbuf, "Int*", addrlen, "Ptr")
			if (ptr = -1)
				throw Socket._Error(Socket.GetLastError())
			if (NumGet(addrbuf, 0, "UShort") != 1)
				addr := Socket._WSAAddressToString(&addrbuf, addrlen)
			else
				addr := this.addr
			return ptr
		}

		AcceptAsClient(clientType := "") {
			ptr := this._accept(addr)
			sock := new Socket.AcceptedClient(ptr, addr)
			sock.UpdateMonitoring()
			return sock
		}
	}

	class AcceptedClient extends Socket.Client {
		__New(ptr, addr := "") {
			Socket.Init()
			this.Ptr := ptr
			this.addr := addr
			this._monitorFlags := 0
			this.isConnected := 1
			this._socktype := 1
			this._protocol := 0
		}
	}
}

 

需要先说清楚的边界

这是 Socket 传输层封装,不会自动提供 HTTP、WebSocket、JSON 消息边界或 TLS 证书验证逻辑。TCP 也是字节流,正式协议必须自行设计长度、分隔符或消息头,不能假设一次 Send 永远对应一次 Recv。

StartTLS() 需要传入外部 TLS 包装对象,库本身不包含完整 TLS 实现。把服务端开放到局域网或公网前,也要自行处理鉴权、输入长度、异常连接和防火墙规则。

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。