├── README.md ├── _socket_example.ahk └── _socket.ahk /README.md: -------------------------------------------------------------------------------- 1 | # Socket_Ahkv2 2 | 3 | Thanks to GeekDude for his work [here](https://github.com/G33kDude/Socket.ahk) and [here](https://www.autohotkey.com/boards/viewtopic.php?f=6&t=35120). And Bentschi for his work [here](https://autohotkey.com/board/topic/94376-socket-class-%C3%BCberarbeitet/). 4 | 5 | Please read the comments in both files detailed help. 6 | 7 | Example is designed to work on the same machine, but IP addr:port can be modified as desired. 8 | 9 | ## To-DO 10 | * Add Multicast functions. 11 | -------------------------------------------------------------------------------- /_socket_example.ahk: -------------------------------------------------------------------------------- 1 | #Include _socket.ahk 2 | 3 | #Requires AutoHotkey v2.0-beta ; 32-bit 4 | 5 | 6 | g := Gui() 7 | g.OnEvent("close",gui_close) 8 | g.OnEvent("escape",gui_close) 9 | g.Add("Edit","vMyEdit Multi w500 h500 ReadOnly","") 10 | g.Show() 11 | 12 | 13 | gui_close(*) { 14 | ExitApp 15 | } 16 | 17 | print_gui(str) { 18 | global g 19 | AppendText(g["MyEdit"].hwnd,str) 20 | } 21 | 22 | client_data := "" 23 | 24 | F1::test_google() 25 | F2::test_server() 26 | F3::test_client() 27 | F4::{ ; clear log 28 | client_data := "" 29 | g["MyEdit"].Value := "" 30 | } 31 | F5::test_error() 32 | 33 | F6::test_send() 34 | 35 | F7::test_google_blocking() 36 | 37 | test_error() { 38 | sock := winsock("client-err",cb,"IPV4") 39 | 40 | ; sock.Connect("www.google.com",80) ; 127.0.0.1",27015 ; www.google.com",80 41 | result := sock.Connect("localhost",5678) ; error is returned in callback 42 | } 43 | 44 | test_google() { 45 | sock := winsock("client",cb,"IPV6") 46 | 47 | sock.Connect("www.google.com",80) ; 127.0.0.1",27015 ; www.google.com",80 48 | ; sock.Connect("www.autohotkey.com",80) ; www.autohotkey.com/download/2.0/ 49 | 50 | print_gui("Client connecting...`r`n`r`n") 51 | } 52 | 53 | test_server() { ; server - uses async to accept sockets 54 | sock := winsock("server",cb,"IPV4") 55 | sock.Bind("0.0.0.0",27015) ; "0.0.0.0",27015 56 | 57 | sock.Listen() 58 | 59 | print_gui("Server listening...`r`n`r`n") 60 | } 61 | 62 | test_client() { ; client 63 | sock := winsock("client",cb,"IPV4") 64 | 65 | sock.Connect("127.0.0.1",27015) ; 127.0.0.1",27015 ; www.google.com",80 66 | 67 | print_gui("Client connecting...`r`n`r`n") 68 | } 69 | 70 | test_google_blocking() { ; this uses no async at all 71 | sock := winsock("client",cb,"IPV6") 72 | 73 | domain := "www.autohotkey.com" ; www.google.com / www.autohotkey.com 74 | port := 443 ; 443 / 80 75 | 76 | If !(r1 := sock.Connect(domain,port,true)) { ; www.google.com 77 | msgbox "Could not connect: " sock.err 78 | return 79 | } 80 | 81 | get_req := "GET / HTTP/1.1`r`n" 82 | . "Host: " domain "`r`n`r`n" 83 | 84 | strbuf := Buffer(StrPut(get_req,"UTF-8"),0) 85 | StrPut(get_req,strbuf,"UTF-8") 86 | 87 | If !(r2 := sock.Send(strbuf)) { 88 | Msgbox "Send failed." 89 | return 90 | } 91 | 92 | While !(buf:=sock.Recv()).size ; wait for data 93 | Sleep 10 94 | 95 | result := "" 96 | while buf.size { 97 | result .= StrGet(buf,"UTF-8") ; collect data 98 | buf:=sock.Recv() 99 | } 100 | 101 | sock.Close() 102 | 103 | A_Clipboard := result 104 | msgbox "Done. Check clipboard." 105 | } 106 | 107 | test_send() { ; the connecting client doesn't use async 108 | sock := winsock("client", cb, "IPV4") 109 | If !(r := sock.Connect("127.0.0.1", 27015, true)) { ; connect as blocking, setting param #3 to TRUE 110 | msgbox "Could not connect." 111 | return ; handle a failed connect properly 112 | } 113 | 114 | msg := "abc" 115 | strbuf := Buffer(StrLen(msg) + 1) ; for UTF-8, take strLen() + 1 as the buffer size 116 | StrPut(msg, strbuf, "UTF-8") 117 | 118 | r := sock.Send(strbuf) ; check send result if necessary 119 | 120 | sock.Close() 121 | } 122 | 123 | cb(sock, event, err) { 124 | global client_data 125 | 126 | dbg("sock.name: " sock.name " / event: " event " / err: " err " ===========================") 127 | 128 | if (sock.name = "client") { 129 | 130 | if (event = "close") { 131 | msgbox "Address: " sock.addr "`n" "Port: " sock.port "`n`n" client_data 132 | A_Clipboard := client_data 133 | client_data := "" 134 | sock.close() 135 | 136 | } else if (event = "connect") { ; connection complete, if (err = 0) then it is a success 137 | print_gui("Client...`r`nConnect Addr: " sock.addr "`r`nConnect Port: " sock.port "`r`n`r`n") ; this does not check for failure 138 | 139 | } else if (event = "write") { ; client ready to send/write 140 | get_req := "GET / HTTP/1.1`r`n" 141 | . "Host: www.google.com`r`n`r`n" 142 | 143 | strbuf := Buffer(StrPut(get_req,"UTF-8"),0) 144 | StrPut(get_req,strbuf,"UTF-8") 145 | 146 | sock.Send(strbuf) 147 | 148 | print_gui("Client sends to server:`r`n" 149 | . "======================`r`n" 150 | . Trim(get_req,"`r`n") "`r`n" 151 | . "======================`r`n`r`n") 152 | 153 | } else if (event = "read") { ; there is data to be read 154 | buf := sock.Recv() 155 | client_data .= StrGet(buf,"UTF-8") 156 | } 157 | 158 | 159 | 160 | } else if (sock.name = "server") || instr(sock.name,"serving-") { 161 | 162 | if (event = "accept") { 163 | sock.Accept(&addr,&newsock) ; pass &addr param to extract addr of connected machine 164 | 165 | print_gui("======================`r`n" 166 | . "Server processes client connection:`r`n" 167 | . "address: " newsock.addr "`r`n" 168 | . "======================`r`n`r`n" ) 169 | 170 | } else if (event = "close") { 171 | 172 | 173 | } else if (event = "read") { 174 | 175 | If !(buf := sock.Recv()).size ; get buffer, check size, return on zero-size buffer 176 | return 177 | 178 | dbg("buf size: " buf.size) 179 | 180 | print_gui("======================`r`n" 181 | . "Server recieved from client:`r`n" 182 | . Trim(strget(buf,"UTF-8"),"`r`n") "`r`n" 183 | . "======================`r`n`r`n") 184 | } 185 | 186 | } else if (sock.name = "client-err") { ; this is how you catch an error with async / non-blocking 187 | 188 | if (event = "connect") && err 189 | msgbox sock.name ": " event ": err: " err 190 | 191 | } 192 | 193 | ; dbg(sock.name ": " event ": err: " err) ; to make it easier to see all the events 194 | } 195 | 196 | 197 | 198 | ; ========================================================== 199 | ; support funcs 200 | ; ========================================================== 201 | 202 | AppendText(EditHwnd, sInput, loc := "bottom") { ; Posted by TheGood: https://autohotkey.com/board/topic/52441-append-text-to-an-edit-control/#entry328342 203 | insertPos := (loc="bottom") ? SendMessage(0x000E, 0, 0,, "ahk_id " EditHwnd) : 0 ; WM_GETTEXTLENGTH 204 | r1 := SendMessage(0x00B1, insertPos, insertPos,, "ahk_id " EditHwnd) ; EM_SETSEL - place cursor for insert 205 | r2 := SendMessage(0x00C2, False, StrPtr(sInput),, "ahk_id " EditHwnd) ; EM_REPLACESEL - insert text at cursor 206 | } 207 | 208 | dbg(_in) { ; AHK v2 209 | Loop Parse _in, "`n", "`r" 210 | OutputDebug "AHK: " A_LoopField 211 | } -------------------------------------------------------------------------------- /_socket.ahk: -------------------------------------------------------------------------------- 1 | ; Socket class 2 | ; Big thanks to GeekDude for remaking this. 3 | ; Original script by GeekDude: https://github.com/G33kDude/Socket.ahk/blob/master/Socket.ahk 4 | ; AHK Forum Post by GeekDude: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=35120 5 | ; 6 | ; And thanks to Bentschi's work which GeekDude expanded on. 7 | ; https://autohotkey.com/board/topic/94376-socket-class-%C3%BCberarbeitet/ 8 | ; 9 | ; Helpful info: 10 | ; Event Objects vs ComplitionIO ports 11 | ; https://stackoverflow.com/questions/44664511/windows-event-based-overlapped-io-vs-io-completion-ports-real-world-performanc 12 | ; 13 | ; Thanks to winsockdotnetworkprogramming.com 14 | ; https://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancediomethod5g.html 15 | ; - this gave a workaround for using overlapped I/O 16 | ; 17 | ; MS Help Docs: 18 | ; 19 | ; ===================================================================================================== 20 | ; 21 | ; 22 | 23 | class winsock { 24 | Static sockets := Map(), wm_msg := 0x9987 25 | 26 | Static __New() { ; init WS2 lib on script start 27 | invert("family","family_2") ; for looking up family name by number 28 | 29 | this.hModule := DllCall("LoadLibrary", "Str", "Ws2_32", "UPtr") 30 | this.WSAData := Buffer(394+A_PtrSize) 31 | if (Err := DllCall("Ws2_32\WSAStartup", "UShort", 0x0202, "UPtr", this.WSAData.ptr)) 32 | throw Error("Error starting Winsock",, Err) 33 | if (NumGet(this.WSAData, 2, "UShort") != 0x0202) 34 | throw Error("Winsock version 2.2 not available") 35 | 36 | this.socketMonitor := ObjBindMethod(this,"WM_SOCKET") 37 | OnMessage(this.wm_msg,this.socketMonitor) ; 0x9987 38 | 39 | this.sockaddr.Prototype.DefineProp("insert" ,{Call:(o,buf,dest)=>insert(buf,dest)}) 40 | this.sockaddr.Prototype.DefineProp("extract",{Call:(o,src,len)=>extract(src,len)}) 41 | 42 | insert(buf,dest) => DllCall("RtlCopyMemory","UPtr",dest,"UPtr",buf.ptr,"UPtr",buf.size) 43 | extract(src,len) { 44 | buf := Buffer(len,0) 45 | DllCall("RtlCopyMemory","UPtr",buf.ptr,"UPtr",src,"UPtr",len) 46 | return buf 47 | } 48 | invert(name,name_2) { 49 | this.%name_2% := {} 50 | For name, value in this.%name%.OwnProps() 51 | this.%name_2%.%value% := name 52 | } 53 | } 54 | 55 | 56 | Static flags := { Passive:0x1 , NumericHost:0x4 , Secure:0x8000 , NonAuthoritative:0x4000 57 | , CanonName:0x2 , AddrConfig:0x400 , FQDN:0x20000, ReturnPreferredNames:0x10000 58 | , V4Mapped:0x800, FileServer:0x40000, All:0x100} 59 | 60 | , family := {Unspec:0, IPV4:2, NetBIOS:17, IPv6:23, IRDA:26, BTH:32} 61 | , fam_size := {Unspec:-1, IPv4:16,NetBIOS:-1, IPv6:28,IRDA:-1, BTH:-1} 62 | , fam_len := {Unspec:-1, IPv4:4, NetBIOS:-1, IPv6:8, IRDA:-1, BTH:-1} 63 | , fam_addr_off := {Unspec:-1, IPv4:4, NetBIOS:-1, IPv6:8, IRDA:-1, BTH:-1} 64 | , socktype := {Stream:1, DGram:2, RAW:3, RDM:4, SeqPacket:5} 65 | , protocol := {TCP:6, UDP:17, RM:113} 66 | 67 | , events := {1:"Read",2:"Write",4:"OOB",8:"Accept",16:"Connect",32:"Close",64:"QOS" 68 | ,128:"GroupQOS",256:"RoutingInterfaceChange",512:"AddressListChange"} 69 | 70 | , errors := {6:"WSA_INVALID_HANDLE", 8:"WSA_NOT_ENOUGH_MEMORY", 87:"WSA_INVALID_PARAMETER", 995:"WSA_OPERATION_ABORTED" 71 | , 996:"WSA_IO_INCOMPLETE", 997:"WSA_IO_PENDING", 10004:"WSAEINTR", 10009:"WSAEBADF", 10013:"WSAEACCES" 72 | , 10014:"WSAEFAULT", 10022:"WSAEINVAL", 10024:"WSAEMFILE", 10035:"WSAEWOULDBLOCK", 10036:"WSAEINPROGRESS" 73 | , 10037:"WSAEALREADY", 10038:"WSAENOTSOCK", 10039:"WSAEDESTADDRREQ", 10040:"WSAEMSGSIZE", 10041:"WSAEPROTOTYPE" 74 | , 10042:"WSAENOPROTOOPT", 10043:"WSAEPROTONOSUPPORT", 10044:"WSAESOCKTNOSUPPORT", 10045:"WSAEOPNOTSUPP" 75 | , 10046:"WSAEPFNOSUPPORT", 10047:"WSAEAFNOSUPPORT", 10048:"WSAEADDRINUSE", 10049:"WSAEADDRNOTAVAIL", 10050:"WSAENETDOWN" 76 | , 10051:"WSAENETUNREACH", 10052:"WSAENETRESET", 10053:"WSAECONNABORTED", 10054:"WSAECONNRESET", 10055:"WSAENOBUFS" 77 | , 10056:"WSAEISCONN", 10057:"WSAENOTCONN", 10058:"WSAESHUTDOWN", 10059:"WSAETOOMANYREFS", 10060:"WSAETIMEDOUT" 78 | , 10061:"WSAECONNREFUSED", 10062:"WSAELOOP", 10063:"WSAENAMETOOLONG", 10064:"WSAEHOSTDOWN", 10065:"WSAEHOSTUNREACH" 79 | , 10066:"WSAENOTEMPTY", 10067:"WSAEPROCLIM", 10068:"WSAEUSERS", 10069:"WSAEDQUOT", 10070:"WSAESTALE", 10071:"WSAEREMOTE" 80 | , 10091:"WSASYSNOTREADY", 10092:"WSAVERNOTSUPPORTED", 10093:"WSANOTINITIALISED", 10101:"WSAEDISCON", 10102:"WSAENOMORE" 81 | , 10103:"WSAECANCELLED", 10104:"WSAEINVALIDPROCTABLE", 10105:"WSAEINVALIDPROVIDER", 10106:"WSAEPROVIDERFAILEDINIT" 82 | , 10107:"WSASYSCALLFAILURE", 10108:"WSASERVICE_NOT_FOUND", 10109:"WSATYPE_NOT_FOUND", 10110:"WSA_E_NO_MORE" 83 | , 10111:"WSA_E_CANCELLED", 10112:"WSAEREFUSED", 11001:"WSAHOST_NOT_FOUND", 11002:"WSATRY_AGAIN", 11003:"WSANO_RECOVERY" 84 | , 11004:"WSANO_DATA", 11005:"WSA_QOS_RECEIVERS", 11006:"WSA_QOS_SENDERS", 11007:"WSA_QOS_NO_SENDERS", 11008:"WSA_QOS_NO_RECEIVERS" 85 | , 11009:"WSA_QOS_REQUEST_CONFIRMED", 11010:"WSA_QOS_ADMISSION_FAILURE", 11011:"WSA_QOS_POLICY_FAILURE", 11012:"WSA_QOS_BAD_STYLE" 86 | , 11013:"WSA_QOS_BAD_OBJECT", 11014:"WSA_QOS_TRAFFIC_CTRL_ERROR", 11015:"WSA_QOS_GENERIC_ERROR", 11016:"WSA_QOS_ESERVICETYPE" 87 | , 11017:"WSA_QOS_EFLOWSPEC", 11018:"WSA_QOS_EPROVSPECBUF", 11019:"WSA_QOS_EFILTERSTYLE", 11020:"WSA_QOS_EFILTERTYPE" 88 | , 11021:"WSA_QOS_EFILTERCOUNT", 11022:"WSA_QOS_EOBJLENGTH", 11023:"WSA_QOS_EFLOWCOUNT", 11024:"WSA_QOS_EUNKOWNPSOBJ" 89 | , 11025:"WSA_QOS_EPOLICYOBJ", 11026:"WSA_QOS_EFLOWDESC", 11027:"WSA_QOS_EPSFLOWSPEC", 11028:"WSA_QOS_EPSFILTERSPEC" 90 | , 11029:"WSA_QOS_ESDMODEOBJ", 11030:"WSA_QOS_ESHAPERATEOBJ", 11031:"WSA_QOS_RESERVED_PETYPE"} 91 | 92 | Static WM_SOCKET(sockDesc, lParam, msg, hwnd) { ; socket monitor 93 | event_cd := lParam & 0xFFFF 94 | errCode := (lParam >> 16) & 0xFFFF 95 | event := (winsock.events.HasProp(event_cd) ? winsock.events.%event_cd% : event_cd) 96 | socket := winsock.sockets[String(sockDesc)] 97 | cb := socket.cb 98 | 99 | If (event="Connect") { 100 | socket.statusCode := errCode 101 | If !(errCode) 102 | socket.ConnectFinish(), socket.status := "Connected" 103 | Else If socket.ConAddrStruct.next 104 | socket.ConnectNext() ; try next addr from GetAddrInfo 105 | Else If !socket.ConAddrStruct.next { 106 | socket.status := "Failed", socket.ConnectFinish() ; all addresses tried, and failed 107 | socket.addr := 0, socket.port := 0, socket.ConAddrIndex := 0 108 | } 109 | } 110 | 111 | if (cb) 112 | cb(socket,event,errCode) 113 | } 114 | 115 | family := 0, protocol := 0, socktype := 0 116 | callback := 0, desc := -1, name := "" 117 | err := "", errnum := 0, LastOp := "" 118 | 119 | host := "", port := 0, addr := "", status := "", statusCode := 0 120 | ConAddrStruct := 0, ConAddrIndex := 0, ConAddrRoot := 0 121 | 122 | block := false ; internal, don't use directly, use this.blocking 123 | 124 | ; sock := {name:"client", family:"Unspec", protocol:"TCP", type:"Stream", block:false, callback:fnc} ; init obj?? 125 | 126 | __New(name, callback:=0, family:="Unspec", protocol:="TCP", socktype:="Stream", desc:=-1) { 127 | 128 | this.name := name, this.desc := desc, this.cb := callback 129 | 130 | If !winsock.family.HasProp(family) ; IPV4:2, NetBIOS:17, IPV6:23, IRDA:26, BTH:32 131 | throw Error("Invalid socket family specified.",-1,"Valid values are:`n`nIPV4,IPV6,NetBIOS,IRDA,BTH") 132 | 133 | this.family := winsock.family.%family% , this.familyName := family 134 | this.socktype := winsock.socktype.%socktype%, this.socktypeName := socktype 135 | this.protocol := winsock.protocol.%protocol%, this.protocolName := protocol 136 | 137 | if !this.CreateSocket(this.desc) 138 | throw Error("Error creating socket.",-1,"Error Code: " this.errnum " / " this.err "`nLast Op: " this.LastOp) 139 | else 140 | winsock.sockets[String(this.desc)] := this 141 | } 142 | 143 | Accept(&addr:=0,&newsock:=0,block:=false) { ; optional VarRef to capture the sockaddr struct of new connection 144 | retCode := 1 145 | sockaddr := winsock.sockaddr(,this.familyName) ; new approach ... 146 | 147 | If ((newdesc := DllCall("Ws2_32\accept","UInt",this.desc,"UPtr",sockaddr.ptr,"Int*",sockaddr.size)) = -1) { 148 | this.WsaLastErr("accept"), retCode := 0, this.Close() 149 | return 0 150 | } 151 | 152 | newsock := winsock("serving-" newdesc, this.cb, this.familyName, this.protocolName, this.socktypeName, newdesc) 153 | (!block) ? newsock.RegisterEvents() : "" ; enable non-blocking mode (default) 154 | 155 | strAddr := newsock.AddrToStr(sockaddr), cSep := InStr(strAddr,":",,,-1) 156 | newsock.addr := RegExReplace(SubStr(strAddr,1,cSep-1),"[\[\]]","") ; record connecting address 157 | newsock.port := SubStr(strAddr,cSep+1) ; record connecting port 158 | winsock.sockets[String(newsock.desc)] := newsock ; catalog new socket 159 | 160 | return retcode 161 | } 162 | 163 | AddrToStr(sock_addr, protocol_info:=0) { 164 | DllCall("Ws2_32\WSAAddressToString","UPtr",sock_addr.ptr ,"UInt",sock_addr.size 165 | ,"UPtr",protocol_info ,"UPtr",0 ,"UInt*",&strSize:=0) 166 | 167 | strbuf := Buffer(strSize * (StrLen(0xFFFF)?2:1),0) 168 | If (DllCall("Ws2_32\WSAAddressToString","UPtr",sock_addr.ptr ,"UInt",sock_addr.size 169 | ,"UPtr",protocol_info ,"UPtr",strbuf.ptr ,"Int*",strSize) = -1) { 170 | this.WsaLastErr() 171 | return "" 172 | } 173 | return StrGet(strbuf) 174 | } 175 | 176 | RegisterEvents(lEvent:=0x3FF) { ; FD_ALL_EVENTS = 0x3FF 177 | this.block := !lEvent ? false : true 178 | 179 | If (result:=this._WSAAsyncSelect(this.desc, A_ScriptHwnd, winsock.wm_msg, lEvent) = -1) 180 | throw Error("WSAASyncSelect failed.", -1) 181 | 182 | ; If !lEvent 183 | ; result := this._ioctlsocket(this.desc, 0x8004667E, &r:=0) ; FIONBIO := 0x8004667E (non-blocking mode) 184 | 185 | return !result 186 | } 187 | 188 | _WSAAsyncSelect(sock_desc, hwnd, msg, lEvent) => 189 | DllCall("Ws2_32\WSAAsyncSelect", "UInt", sock_desc, "UPtr", hwnd, "UInt", msg, "UInt", lEvent) 190 | 191 | Bind(host:=0,port:=0) { 192 | Static AI_PASSIVE:=0x1 ; required flag for calling Bind() 193 | 194 | result := this.GetAddrInfo(host,port,AI_PASSIVE) ; addrinfo struct 195 | retCode := 1 196 | If (DllCall("Ws2_32\bind", "UInt", this.desc, "UPtr", result.addr, "UInt", result.addrlen) = -1) 197 | this.WsaLastErr("bind"), retCode := 0, this.Close() ; close socket 198 | DllCall("Ws2_32\FreeAddrInfo", "UPtr", result.ptr) ; free memory of addrinfo chain 199 | return retCode 200 | } 201 | 202 | Close() { 203 | result := 1 204 | if (DllCall("Ws2_32\closesocket","Int",this.desc) = -1) 205 | this.WsaLastErr("closesocket"), result := 0 206 | Else 207 | winsock.sockets.Delete(String(this.desc)), this.status := "Closed" 208 | return result 209 | } 210 | 211 | Connect(host:=0,port:=0,block:=false) { ; init connect process 212 | (!block) ? (this.RegisterEvents()) : "" 213 | 214 | If (this.ConAddrStruct := this.GetAddrInfo(this.host:=host,port)) { 215 | this.ConAddrRoot := this.ConAddrStruct.ptr 216 | 217 | ; dbg("root: " this.ConAddrRoot " / next: " this.ConAddrStruct.next) 218 | 219 | If !block { 220 | result := this.ConnectNext() 221 | } Else { 222 | While (result := this.ConnectNext()) { ; initiate connect process 223 | ; dbg("still looping?") 224 | Sleep(10) 225 | } 226 | this.ConnectFinish() ; cleanup 227 | } 228 | } 229 | 230 | If (result=-1) && this.WsaLastErr("connect") && (this.err = "WSAEWOULDBLOCK") 231 | result := !result 232 | 233 | return !result 234 | } 235 | 236 | ConnectFinish() { ; free memory of addrinfo chain 237 | DllCall("Ws2_32\FreeAddrInfo", "UPtr", this.ConAddrRoot) 238 | this.ConAddrRoot := 0, this.ConAddrStruct := "" 239 | } 240 | 241 | ConnectNext() { ; ConAddrIndex is initiated at 0. The first call of ConnectNext() sets ConAddrIndex to 1. 242 | this.status := "Connecting" 243 | 244 | this.ConAddrIndex++ ; When ConAddrIndex is > 1, the next line prepares the next address to try, if any. 245 | (this.ConAddrIndex>1) ? (this.ConAddrStruct := winsock.addrinfo(this.ConAddrStruct.next)) : "" ; this.ConAddrStruct.next checked in WM_SOCKET 246 | 247 | ; dbg("ConAddrIndex: " this.ConAddrIndex) 248 | 249 | sockaddr := winsock.sockaddr(this.ConAddrStruct.addr,this.ConAddrStruct.addrlen) ; get sockaddr struct 250 | strAddr := this.AddrToStr(sockaddr), cSep := InStr(strAddr,":",,,-1) 251 | this.addr := RegExReplace(SubStr(strAddr,1,cSep-1), "[\[\]]", "") ; record connecting address 252 | this.port := SubStr(strAddr,cSep+1) ; record connecting port 253 | 254 | If (r := DllCall("Ws2_32\connect","UInt",this.desc,"UPtr",this.ConAddrStruct.addr,"UInt",this.ConAddrStruct.addrlen) = -1) 255 | this.WsaLastErr("connect") 256 | 257 | ; dbg("connect result: " r " / err: " this.err) 258 | 259 | return r 260 | } 261 | 262 | CreateSocket(desc:=-1) { ; make new socket, or take given socket, then call WSAAsyncSelect 263 | result := 1 264 | If ((desc = -1) ; try to open the socket 265 | && (this.desc := DllCall("Ws2_32\socket","Int",this.family,"Int",this.socktype,"Int",this.protocol)) = -1) 266 | this.WsaLastErr("socket"), result := 0 267 | return result 268 | } 269 | 270 | GetAddrInfo(host,port:=0,flags:=0) { 271 | hints := winsock.addrinfo() 272 | hints.family := this.family , hints.protocol := this.protocol 273 | hints.socktype := this.socktype, hints.flags := flags 274 | 275 | if (err := DllCall("Ws2_32\GetAddrInfo",(host?"Str":"UPtr"),host 276 | ,(port?"Str":"UPtr"),(port?String(port):0) 277 | ,"UPtr",hints.ptr 278 | ,"UPtr*",&result:=0)) { 279 | this.WsaLastErr("GetAddrInfo") 280 | return winsock.addrinfo(0) ; return NULL ptr 281 | } 282 | 283 | return winsock.addrinfo(result) 284 | } 285 | 286 | Listen(backlog:=5,block:=false) { ; SOMAXCONN = 5 287 | (!block) ? this.RegisterEvents() : "" ; enable non-blocking mode (default) 288 | retCode := 1 289 | If (DllCall("Ws2_32\listen","UInt",this.desc,"Int",backlog) = -1) 290 | this.WsaLastErr("bind"), retCode := 0, this.Close() ; close socket 291 | return retCode 292 | } 293 | 294 | ; QueueData(buf) => this.OutQueue.Push(buf) ; ??? 295 | 296 | Recv(flags:=0) { 297 | If (this._ioctlsocket(this.desc, 0x4004667F, &bytes) = -1) { ; FIONREAD := 0x4004667F 298 | this.WsaLastErr("ioctlsocket") 299 | If (this.err != "WSAEWOULDBLOCK") 300 | return Buffer(0) 301 | } 302 | 303 | buf := Buffer(bytes,0) 304 | If (DllCall("Ws2_32\recv","UInt",this.desc,"UPtr",buf.ptr,"Int",buf.size,"Int",flags) = -1) { 305 | this.WsaLastErr("recv") 306 | if (this.err != "WSAEWOULDBLOCK") 307 | return Buffer(0) 308 | } 309 | 310 | return buf 311 | } 312 | 313 | _ioctlsocket(socket, cmd, &agrp:=0) => DllCall("Ws2_32\ioctlsocket", "UInt", socket, "UInt", cmd, "UInt*", &agrp:=0) 314 | 315 | Send(buf,flags:=0) { 316 | If (DllCall("Ws2_32\send","UInt",this.desc,"UPtr",buf.ptr,"UInt",buf.size,"UInt",flags) = -1) { 317 | this.WsaLastErr("send") 318 | return 0 319 | } Else return 1 320 | } 321 | 322 | WsaLastErr(LastOp:="") { 323 | If (result := DllCall("Ws2_32\WSAGetLastError")) 324 | this.errnum := result, this.LastOp := LastOp, this.err := winsock.errors.%result% 325 | Else 326 | this.errnum := 0 , this.LastOp := "" , this.err := "" 327 | return result 328 | } 329 | 330 | __Delete() => this.Close() ; close socket if open 331 | 332 | class addrinfo { 333 | Static __New() { 334 | off := {flags: {off:0, type:"Int"} ,addrlen: {off:16, type:"UPtr"} 335 | ,family: {off:4, type:"Int"} ,cannonname:{off:16+(p:=A_PtrSize),type:"UPtr"} 336 | ,socktype: {off:8, type:"Int"} ,addr: {off:16+(p*2),type:"UPtr"} 337 | ,protocol: {off:12, type:"Int"} ,next: {off:16+(p*3),type:"UPtr"}} 338 | this.Prototype.DefineProp("s",{Value:off}) 339 | } 340 | 341 | __New(buf := 0) { 342 | this.DefineProp("_struct",{Value:(!buf) ? Buffer(16 + (A_PtrSize*4),0) : {ptr:buf}}) 343 | this.DefineProp("Ptr",{Get:(o)=>this._struct.ptr}) 344 | } 345 | 346 | __Get(name,p) => NumGet(this.ptr, this.s.%name%.off, this.s.%name%.type) 347 | __Set(name,p,value) => NumPut(this.s.%name%.type, value, this.ptr, this.s.%name%.off) 348 | } 349 | 350 | class sockaddr { ; family := {Unspec:0, IPV4:2, NetBIOS:17, IPV6:23, IRDA:26, BTH:32} 351 | __New(buf := 0, fam := "IPv4") { ; buf=0 && fam=family_text OR buf=ptr && fam=size 352 | Static _fs := winsock.fam_size, _fo := winsock.fam_addr_off, _fL := winsock.fam_len 353 | , _f := winsock.family, _f2 := winsock.family_2 354 | 355 | If !buf && !winsock.family.HasOwnProp(fam) 356 | throw Error("Invalid family for sockaddr creation: " fam, -1) 357 | 358 | this.DefineProp("size",{Value:size := (Type(fam)="String")?(_fs.%fam%):fam}) 359 | this.DefineProp("_struct",{Value:(!buf) ? Buffer(size,0) : {ptr:buf}}) 360 | this.DefineProp("Ptr",{Get:(o)=>this._struct.ptr}) 361 | 362 | (buf) ? (fam := _f2.%NumGet(buf,0,"UShort")%) : NumPut("UShort",_f.%fam%,this.Ptr) ; init family, or get fam name 363 | 364 | ; off := {family: {off:0,type:"UShort"},address: {off:_fo.%family% , len:_fL.%family%} 365 | ; ,port: {off:2,type:"UShort"},flowinfo:{off:4,type:"UInt"} 366 | ; ,scopeid: {off:24,type:"UInt"}} 367 | 368 | off := {family: {off:0,type:"UShort"},flowinfo:{off:4 ,type:"UInt"}, address:{off:_fo.%fam% , len:_fL.%fam%} 369 | , port: {off:2,type:"UShort"}, scopeid:{off:24,type:"UInt"}} 370 | 371 | this.DefineProp("s",{Value:off}) 372 | } 373 | 374 | __Get(name,p) => (this.s.%name%.HasOwnProp("len")) ? this.extract(this.ptr + this.s.%name%.off,this.s.%name%.len) 375 | : NumGet(this.ptr,this.s.%name%.off,this.s.%name%.type) 376 | 377 | __Set(name,p,value) => (this.s.%name%.HasOwnProp("len")) ? this.insert(value,this.ptr + this.s.%name%.off) 378 | : NumPut(this.s.%name%.type,value,this.ptr,this.s.%name%.off) 379 | } 380 | 381 | ; class ProtoInfo { 382 | ; Static __New() { 383 | ; off := 384 | ; } 385 | 386 | ; __New(buf:=0) { 387 | ; Static u := StrLen(Chr(0xFFFF)) 388 | ; this._struct := (!buf) ? Buffer(u?:,0) : {ptr:buf} 389 | 390 | ; this.DefineProp("Ptr",{Get:(o)=>this._struct.ptr}) 391 | ; } 392 | ; } 393 | 394 | } 395 | 396 | 397 | ; typedef struct _WSAPROTOCOL_INFOA { 398 | ; DWORD dwServiceFlags1; 399 | ; DWORD dwServiceFlags2; 400 | ; DWORD dwServiceFlags3; 401 | ; DWORD dwServiceFlags4; 402 | ; DWORD dwProviderFlags; 403 | ; GUID ProviderId; 404 | ; DWORD dwCatalogEntryId; 405 | ; WSAPROTOCOLCHAIN ProtocolChain; 406 | ; int iVersion; 407 | ; int iAddressFamily; 408 | ; int iMaxSockAddr; 409 | ; int iMinSockAddr; 410 | ; int iSocketType; 411 | ; int iProtocol; 412 | ; int iProtocolMaxOffset; 413 | ; int iNetworkByteOrder; 414 | ; int iSecurityScheme; 415 | ; DWORD dwMessageSize; 416 | ; DWORD dwProviderReserved; 417 | ; CHAR szProtocol[WSAPROTOCOL_LEN + 1]; 418 | ; } WSAPROTOCOL_INFOA, *LPWSAPROTOCOL_INFOA; 419 | 420 | 421 | ; ======================================================================== 422 | ; Old methods 423 | ; ======================================================================== 424 | 425 | ; MsgSize() { 426 | ; static FIONREAD := 0x4004667F 427 | ; if (DllCall("Ws2_32\ioctlsocket", "UInt", this.Socket, "UInt", FIONREAD, "UInt*", &argp:=0) == -1) 428 | ; throw Error("Error calling ioctlsocket",, this.GetLastError()) 429 | ; return argp 430 | ; } 431 | 432 | 433 | 434 | ; MsgMonitor(wParam, lParam, Msg, hWnd) { 435 | ; dbg("msg: " Format("0x{:X}",msg) " / hwnd: " hWnd " / wParam: " wParam " / lParam: " lParam) 436 | 437 | ; if (Msg != Socket.WM_SOCKET || wParam != this.Socket) 438 | ; return 439 | 440 | ; dbg("it worked? / " Type(this.recvCB) " / ID: " this.SockID) 441 | 442 | ; If (RecvCB := this.recvCB) { ; data, date, EventType, socket 443 | ; if (lParam & Socket.FD_READ) { 444 | ; dbg("reading... / ID: " this.SockID) 445 | 446 | 447 | ; RecvCB("", "read", this) ; this.Recv() 448 | ; } else if (lParam & Socket.FD_ACCEPT) { 449 | ; dbg("accepting... / ID: " this.SockID) 450 | ; RecvCB("", "accept", this) ; this.Accept() 451 | 452 | ; } else if (lParam & Socket.FD_CLOSE) { 453 | ; dbg("closing... / ID: " this.SockID) 454 | ; this.EventProcUnregister(), this.Disconnect(), RecvCB("", "close", this) 455 | ; } 456 | ; } 457 | ; } 458 | 459 | ; EventProcRegister(lEvent) { 460 | ; this.AsyncSelect(lEvent) 461 | ; if !this.Bound { 462 | ; this.Bound := ObjBindMethod(this,"MsgMonitor") 463 | ; OnMessage Socket.WM_SOCKET, this.Bound ; register event function 464 | ; } 465 | ; } 466 | 467 | ; EventProcUnregister() { 468 | ; this.AsyncSelect(0) 469 | ; if this.Bound { 470 | ; OnMessage Socket.WM_SOCKET, this.Bound, 0 ; unregister event function 471 | ; this.Bound := False 472 | ; } 473 | ; } 474 | 475 | ; AsyncSelect(lEvent) { 476 | ; if (r := DllCall("Ws2_32\WSAAsyncSelect" 477 | ; , "UInt", this.Socket ; s 478 | ; , "Ptr", A_ScriptHwnd ; hWnd 479 | ; , "UInt", Socket.WM_SOCKET ; wMsg 480 | ; , "UInt", lEvent) == -1) ; lEvent 481 | ; throw Error("Error calling WSAAsyncSelect ---> SockID: " this.SockID " / " this.Socket,, this.GetLastError()) 482 | ; } 483 | 484 | ; GetLastError() { 485 | ; return DllCall("Ws2_32\WSAGetLastError") 486 | ; } 487 | 488 | ; SetBroadcast(Enable) { ; for UDP sockets only -- don't know what this does yet 489 | ; static SOL_SOCKET := 0xFFFF, SO_BROADCAST := 0x20 490 | ; if (DllCall("Ws2_32\setsockopt" 491 | ; , "UInt", this.Socket ; SOCKET s 492 | ; , "Int", SOL_SOCKET ; int level 493 | ; , "Int", SO_BROADCAST ; int optname 494 | ; , "UInt", !!Enable ; *char optval 495 | ; , "Int", 4) == -1) ; int optlen 496 | ; throw Error("Error calling setsockopt",, this.GetLastError()) 497 | ; } --------------------------------------------------------------------------------