Socket通信库增强版

网上的Socket库用着一堆坑和毛病,于是就是自改了一个Socket库。支持UDP单端口双向通信、异步TCP收发,发送和接收16进制信息,收发文件等等。

Socket通信示例包

提取码:6666复制
解压码:无

异步TCP服务端【Server端】

; Server
SetBatchLines -1
#Include <Socket>
#SingleInstance Force

myTcp := New SocketTCP()
myTcp.onAccept := Func("OnTCPAccept")
myTcp.bind(["0.0.0.0", 22345])
myTcp.listen()

Loop {
    ToolTip 持续运算阻塞演示-%A_Index%
    Sleep 50
}
Return

; 被客户端连接时触发
OnTCPAccept() {
    Global
    newTcp := myTcp.accept()
    newTcp.AsyncRecv("标签回调测试")
}

标签回调测试:
MsgBox % newTcp.AsyncText()
newTcp.AsyncRecv("标签回调测试")  ; 接收消息后,再次监听达到循环接收
; newTcp.AsyncEmpty()  ; 清空异步缓存信息
Return

; F1向对方发送信息
F1::newTcp.SendText("接收端 → 向 → 发送端的回复" SubStr(A_TickCount, 4, 4))

; F2查看收到的16进制信息
F2::MsgBox % "16进制信息:" newTcp.AsyncBuf() "`n`n信息长度:" newTcp.AsyncLength()

 

异步TCP客户端【Client端】

; Client
SetBatchLines -1
#Include <Socket>
#SingleInstance Force

; 免费动态域名解析申请:http://www.pubyun.com/
myTcp := New SocketTCP()
While !(myTcp.Connect(["127.0.0.1", 22345]))  ; 支持动态域名解析,比如:myname.f3322.net
    Sleep 100  ; While循环判断是否连接上主机,若未能连上则延时100毫秒尝试重连
myTcp.AsyncRecv("标签回调测试")
Return

; 公网IP主机+动态域名+路由DDNS端口转发可以实现简单的字符串反向代理【转发分配】
; 测试公网连接时,不能服务端和客户端在同一台电脑上运行。【会连接不上】

标签回调测试:
MsgBox % myTcp.AsyncText()
myTcp.AsyncRecv("标签回调测试")  ; 接收消息后,再次监听达到循环接收
; myTcp.AsyncEmpty()  ; 清空异步缓存信息
Return

; F1向对方发送信息
~F1::myTcp.SendText("向接收端 →→ 发送文字" SubStr(A_TickCount, 4, 4))

; F2查看收到的16进制信息
~F2::MsgBox % "16进制信息:" myTcp.AsyncBuf() "`n`n信息长度:" myTcp.AsyncLength()

 

Socket.ahk

; Modified from: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=35120
class Socket {
    static WM_SOCKET := 0x9987, MSG_PEEK := 2
    static FD_READ := 1, FD_ACCEPT := 8, FD_CLOSE := 32
    static Blocking := True, BlockSleep := 20, AsyncBlockSleep := 40

    __New(Socket:=-1) {
        static Init
        if !Init {
            DllCall("LoadLibrary", "Str", "Ws2_32", "Ptr")
            , VarSetCapacity(WSAData, 394+A_PtrSize)
            if (Error := DllCall("Ws2_32\WSAStartup", "UShort", 0x0202, "Ptr", &WSAData))
                throw Exception("Error starting Winsock",, Error)
            if (NumGet(WSAData, 2, "UShort") != 0x0202)
                throw Exception("Winsock version 2.2 not available")
            Init := True
        }
        this.Socket := Socket
    }

    __Delete() {
        if (this.Socket != -1)
            this.Disconnect()
    }

    Connect(Address) {
        if (this.Socket != -1)
            return -1
        Next := pAddrInfo := this.GetAddrInfo(Address)
        While Next {
            ai_addrlen := NumGet(Next+0, 16, "UPtr")
            , ai_addr := NumGet(Next+0, 16+(2*A_PtrSize), "Ptr")
            if ((this.Socket := DllCall("Ws2_32\socket", "int", NumGet(Next+0, 4, "int"), "int", this.SocketType, "int", this.ProtocolId, "Uint")) != -1) {
                if (DllCall("Ws2_32\WSAConnect", "Uint", this.Socket, "Ptr", ai_addr, "Uint", ai_addrlen, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr", 0, "int") == 0) {
                    DllCall("Ws2_32\freeaddrinfo", "Ptr", pAddrInfo)  ; TODO: Error Handling
                    return this.EventProcRegister(this.FD_READ | this.FD_CLOSE)
                }
                this.Disconnect()
            }
            Next := NumGet(Next+0, 16+(3*A_PtrSize), "Ptr")
        }
        return 0
    }

    Bind(Address) {
        if (this.Socket != -1)
            return 0
        Next := pAddrInfo := this.GetAddrInfo(Address)
        While Next {
            ai_addrlen := NumGet(Next+0, 16, "UPtr")
            , ai_addr := NumGet(Next+0, 16+(2*A_PtrSize), "Ptr")
            if ((this.Socket := DllCall("Ws2_32\socket", "int", NumGet(Next+0, 4, "int"), "int", this.SocketType, "int", this.ProtocolId, "Uint")) != -1) {
                if (DllCall("Ws2_32\bind", "Uint", this.Socket, "Ptr", ai_addr, "Uint", ai_addrlen, "int") == 0) {
                    DllCall("Ws2_32\freeaddrinfo", "Ptr", pAddrInfo)  ; TODO: ERROR HANDLING
                    return this.EventProcRegister(this.FD_READ | this.FD_ACCEPT | this.FD_CLOSE)
                }
                this.Disconnect()
            }
            Next := NumGet(Next+0, 16+(3*A_PtrSize), "Ptr")
        }
        throw Exception("Error binding")
    }

    Listen(backlog=32) {
        return DllCall("Ws2_32\listen", "Uint", this.Socket, "int", backlog) == 0
    }

    Accept() {
        if ((s := DllCall("Ws2_32\accept", "Uint", this.Socket, "Ptr", 0, "Ptr", 0, "Ptr")) == -1)
            throw Exception("Error calling accept",, this.GetLastError())
        Sock := new Socket(s)
        , Sock.ProtocolId := this.ProtocolId
        , Sock.SocketType := this.SocketType
        , Sock.EventProcRegister(this.FD_READ | this.FD_CLOSE)
        return Sock
    }

    Disconnect() {
        ; Return 0 if not connected
        if (this.Socket == -1)
            return 0

        ; Unregister the socket event handler and close the socket
        this.EventProcUnregister()
        if (DllCall("Ws2_32\closesocket", "Uint", this.Socket, "int") == -1)
            throw Exception("Error closing socket",, this.GetLastError())
        this.Socket := -1
        return 1
    }

    MsgSize() {
        static FIONREAD := 0x4004667F
        if (DllCall("Ws2_32\ioctlsocket", "Uint", this.Socket, "Uint", FIONREAD, "UInt*", argp) == -1)
            throw Exception("Error calling ioctlsocket",, this.GetLastError())
        return argp
    }

    Send(pBuffer, BufSize, Flags:=0) {
        if ((r := DllCall("Ws2_32\send", "Uint", this.Socket, "Ptr", pBuffer, "int", BufSize, "int", Flags)) == -1)
            return 0
        return r
    }

    SendText(Text, Encoding:="UTF-8") {
        VarSetCapacity(Buffer, StrPut(Text, Encoding) * ((Encoding="UTF-16"||Encoding="CP1200") ? 2 : 1))
        return this.Send(&Buffer, StrPut(Text, &Buffer, Encoding) - 1)
    }

    SendHex(Hex*) {
        VarSetCapacity(binary, Hex.Length(), 0)
        Loop % Hex.Length()
            NumPut(Hex[A_Index], binary, A_Index-1, "UChar")
        return this.Send(&binary, Hex.Length())
    }

    Recv(ByRef Buffer, BufSize:=0, Flags:=0, AsyncLabel:="") {
        if (AsyncLabel="") {
            While (!(Length := this.MsgSize()) && this.Blocking)
                Sleep, this.BlockSleep
         } else {
            __AsyncEmpty := ObjBindMethod(this, "Recv", Buffer, BufSize, Flags, AsyncLabel)
            if (!(Length := this.MsgSize()) && this.Blocking) {
                this.AsyncWait := 1
                SetTimer %__AsyncEmpty%, % "-" this.AsyncBlockSleep
                Return
             } else {
                SetTimer %__AsyncEmpty%, Delete
                this.AsyncWait := 0
            }
        }

        if !Length
            return 0

        if !BufSize
            BufSize := Length
        VarSetCapacity(Buffer, BufSize)
        , VarSetCapacity(from, 16, 0)
        , r := DllCall("ws2_32\recvfrom", "Ptr", this.Socket, "Ptr", &Buffer, "int", BufSize, "int", 0, "Ptr", &from, "Int*", 16)
        , this.Port := DllCall("ws2_32\htons", "UShort", NumGet(from, 2, "UShort"), "UShort")
        , this.IPfrom := DllCall("ws2_32\inet_ntoa", "Uint", NumGet(from, 4, "Uint"), "AStr")

        if (r <= 0)
            return 0

        this.AsyncRecvBuf := Buffer

        if IsLabel(AsyncLabel)
            SetTimer %AsyncLabel%, -1

        return this.AsyncRecvLength := r
    }

    GetRecvIPfrom() {
        return this.IPfrom
    }

    GetRecvPort() {
        return this.Port
    }

    AsyncRecv(AsyncLabel:="") {
        if !this.AsyncWait
            this.Recv(Buffer, 0, 0, AsyncLabel)
    }

    AsyncText(Encoding:="UTF-8") {
        Buffer := this.AsyncRecvBuf
        return StrGet(&Buffer, this.AsyncRecvLength, Encoding)
    }

    AsyncBuf() {
        return this.AsyncRecvBuf
    }

    AsyncLength() {
        return this.AsyncRecvLength
    }

    AsyncEmpty() {
        this.AsyncRecvLength := this.AsyncRecvBuf := ""
    }

    RecvText(BufSize:=0, Flags:=0, Encoding:="UTF-8") {
        if (Length := this.Recv(Buffer, BufSize, flags))
            return StrGet(&Buffer, Length, Encoding)
        return
    }

    RecvHex() {
        if (bytes := this.Recv(addr)) {
            DllCall("crypt32\CryptBinaryToStringW", "Ptr", &addr, "Uint", bytes, "Uint", 0x40000004, "Ptr", 0, "Uint*", chars)
            , VarSetCapacity(hex, chars * 2)
            , DllCall("crypt32\CryptBinaryToStringW", "Ptr", &addr, "Uint", bytes, "Uint", 0x40000004, "Str", hex, "Uint*", chars)
            return Format("{:U}", hex)
        }
    }

    ; https://www.autohotkey.com/boards/viewtopic.php?p=28453
    ; "package_size"可增加到 4096KB。尽管 8192KB 理论上是可能的,但随后会发生错误。
    SendFilePackages(filepath, package_size := 8192) {
        file := FileOpen(filepath, "r")
        , VarSetCapacity(buf, file.length)
        , file.RawRead(&buf, file.length)
        Stringsplit, filepath, filepath, \
        filename := filepath%filepath0%  ; Dateiname Rausfiltern
        , len := file.length
        , pos := &buf
        , this.SendText(filename ":" len ":" package_size)  ; Header
        , this.RecvText() 

        While (len > 0)
            current_len := (len>=package_size) ? package_size : len
            , this.Send(pos, current_len)
            , len -= current_len  
            , pos += current_len
            , this.RecvText()

        file.close()
    }

    RecvFilePackages(filepath:="") {
        Header := this.RecvText()
        , Header := StrSplit(Header, ":")
        , num_packages := Ceil(Header[2] / Header[3])  ; Anzahl der Packete aufgerundet 

        if (filepath="") {
            if FileExist(A_ScriptDir "\" Header[1])
                filepath := A_ScriptDir "\Bak-" Header[1]
             else
                filepath := A_ScriptDir "\" Header[1]
        } else
            filepath := RTrim(filepath, "\") "\" Header[1]

        file := FileOpen(filepath, "w") 
        , this.SendText("#")

        Loop %num_packages% {
            len := this.Recv(Buffer)
            , file.RawWrite(&Buffer, len)
            if (A_Index = num_packages)
                file.close()
            this.SendText("#")
        }
    }

    RecvLine(BufSize:=0, Flags:=0, Encoding:="UTF-8", KeepEnd:=False) {
        While !(i := InStr(this.RecvText(BufSize, Flags|this.MSG_PEEK, Encoding), "`n")) {
            if !this.Blocking
                return
            Sleep, this.BlockSleep
        }
        if KeepEnd
            return this.RecvText(i, Flags, Encoding)
         else
            return RTrim(this.RecvText(i, Flags, Encoding), "rn") } GetAddrInfo(Address) { __ := ["127.0.0.1", "0.0.0.0", "255.255.255.255", "::1", "::", "FF00::"] conv := {localhost:__[1], addr_loopback:__[1], inaddr_loopback:__[1], addr_any:__[2], inaddr_any:__[2], addr_broadcast:__[3] , inaddr_broadcast:__[3], addr_none:__[3], inaddr_none:__[3], localhost6:__[4], addr_loopback6:__[4], inaddr_loopback6:__[4] , addr_any6:__[5], inaddr_any:__[5], addr_broadcast6:__[6], inaddr_broadcast6:__[6], addr_none6:__[6], inaddr_none6:__[6]} ; TODO: Use GetAddrInfoW Host := Address[1], Port := Address[2] if (conv[host]) host := conv[host] VarSetCapacity(Hints, 16+(4*A_PtrSize), 0) , NumPut(this.SocketType, Hints, 8, "int") , NumPut(this.ProtocolId, Hints, 12, "int") if (Error := DllCall("Ws2_32\getaddrinfo", "AStr", Host, "AStr", Port, "Ptr", &Hints, "Ptr*", Result)) throw Exception("Error calling GetAddrInfo",, Error) return Result } OnMessage(wParam, lParam, Msg, hWnd) { Critical if (Msg != this.WM_SOCKET || wParam != this.Socket) return if (lParam & this.FD_READ) this.onRecv() else if (lParam & this.FD_ACCEPT) this.onAccept() else if (lParam & this.FD_CLOSE) this.EventProcUnregister(), this.OnDisconnect() } EventProcRegister(lEvent) { Rtn := this.AsyncSelect(lEvent) if !this.Bound this.Bound := this.OnMessage.Bind(this) , OnMessage(this.WM_SOCKET, this.Bound) Return Rtn } EventProcUnregister() { this.AsyncSelect(0) if this.Bound OnMessage(this.WM_SOCKET, this.Bound, 0) , this.Bound := False } AsyncSelect(lEvent) { if (DllCall("Ws2_32\WSAAsyncSelect", "Uint", this.Socket, "Ptr", A_ScriptHwnd, "Uint", this.WM_SOCKET, "Uint", lEvent) == -1) throw Exception("Error calling WSAAsyncSelect",, this.GetLastError()) Return 1 } GetLastError() { return DllCall("Ws2_32\WSAGetLastError") } } class SocketTCP extends Socket { static ProtocolId := 6 ; IPPROTO_TCP static SocketType := 1 ; SOCK_STREAM } class SocketUDP extends Socket { static ProtocolId := 17 ; IPPROTO_UDP static SocketType := 2 ; SOCK_DGRAM EnableBroadcast() { VarSetCapacity(optval, 4, 0) && NumPut(1, optval, 0, "Uint") if (DllCall("ws2_32\setsockopt", "Ptr", this.Socket, "int", 0xFFFF, "int", 0x0020, "Ptr", &optval, "int", 4) = 0) return 1 return 0 } DisableBroadcast() { VarSetCapacity(optval, 4, 0) if (DllCall("ws2_32\setsockopt", "Ptr", this.Socket, "int", 0xFFFF, "int", 0x0020, "Ptr", &optval, "int", 4) = 0) return 1 return 0 } }

给TA捐赠
共{{data.count}}人
人已捐赠
其他函数

二维码生成器——QRCode库

2022-9-5 11:53:03

其他

AHK自动安装工具

2022-9-6 8:40:30

3 条回复 A文章作者 M管理员
  1. AHK中文社区
    1河许人给您捐赠了¥5
  2. 蜜獾哥
    蜜獾哥给您捐赠了¥5
  3. 蜜獾哥

    db出品,必属精品!

个人中心
购物车
优惠劵
有新私信 私信列表
搜索