Advertisement
Guest User

Untitled

a guest
Apr 22nd, 2019
169
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 80.16 KB | None | 0 0
  1. dota-srv.ahk
  2. #NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
  3. ; #Warn ; Enable warnings to assist with detecting common errors.
  4. SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
  5. SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
  6.  
  7. #Persistent
  8. #SingleInstance, force
  9. SetBatchLines, -1
  10.  
  11. paths := {}
  12. paths["/SendConsole"] := Func("SendConsole")
  13. paths["/OpenNetworth"] := Func("OpenNetworth")
  14. paths["404"] := Func("NotFound")
  15. paths["/logo"] := Func("Logo")
  16. paths["/FadeUp"] := Func("FadeUp")
  17. paths["/FadeDown"] := Func("FadeDown")
  18.  
  19. server := new HttpServer()
  20. server.LoadMimes(A_ScriptDir . "/mime.types")
  21. server.SetPaths(paths)
  22. server.Serve(8000)
  23. return
  24.  
  25. Logo(ByRef req, ByRef res, ByRef server) {
  26. server.ServeFile(res, A_ScriptDir . "/logo.png")
  27. res.status := 200
  28. }
  29.  
  30. NotFound(ByRef req, ByRef res) {
  31. res.SetBodyText("Page not found")
  32. }
  33.  
  34. SendConsole(ByRef req, ByRef res) {
  35. ; Go to VConsole
  36. WinActivate, VConsole
  37. ; Command field
  38. Click, 133, 782
  39. ; Type command
  40. Cmd := req.queries["command"]
  41. Send, %Cmd%
  42. ; Hit enter
  43. Send, {Enter}
  44.  
  45. res.SetBodyText("OK")
  46. res.status := 200
  47. }
  48.  
  49. FadeUp(ByRef req, ByRef res) {
  50. Loop 100 {
  51. Sleep 10
  52. SoundSet,+1
  53. }
  54. res.SetBodyText("OK")
  55. res.status := 200
  56. }
  57.  
  58. FadeDown(ByRef req, ByRef res) {
  59. Loop 100 {
  60. Sleep 10
  61. SoundSet,-1
  62. }
  63. res.SetBodyText("OK")
  64. res.status := 200
  65. }
  66.  
  67.  
  68. OpenNetworth(ByRef req, ByRef res) {
  69. WinActivate, Dota 2
  70. Send, y
  71. Click, 1836, 26
  72. MouseMove, 0, -50
  73. res.SetBodyText("OK")
  74. res.status := 200
  75. }
  76.  
  77. #include, %A_ScriptDir%\AHKhttp.ahk
  78. #include %A_ScriptDir%\AHKsock.ahk
  79.  
  80.  
  81.  
  82. AHKhttp.ahk
  83. class Uri
  84. {
  85. Decode(str) {
  86. Loop
  87. If RegExMatch(str, "i)(?<=%)[\da-f]{1,2}", hex)
  88. StringReplace, str, str, `%%hex%, % Chr("0x" . hex), All
  89. Else Break
  90. Return, str
  91. }
  92.  
  93. Encode(str) {
  94. f = %A_FormatInteger%
  95. SetFormat, Integer, Hex
  96. If RegExMatch(str, "^\w+:/{0,2}", pr)
  97. StringTrimLeft, str, str, StrLen(pr)
  98. StringReplace, str, str, `%, `%25, All
  99. Loop
  100. If RegExMatch(str, "i)[^\w\.~%]", char)
  101. StringReplace, str, str, %char%, % "%" . Asc(char), All
  102. Else Break
  103. SetFormat, Integer, %f%
  104. Return, pr . str
  105. }
  106. }
  107.  
  108. class HttpServer
  109. {
  110. static servers := {}
  111.  
  112. LoadMimes(file) {
  113. if (!FileExist(file))
  114. return false
  115.  
  116. FileRead, data, % file
  117. types := StrSplit(data, "`n")
  118. this.mimes := {}
  119. for i, data in types {
  120. info := StrSplit(data, " ")
  121. type := info.Remove(1)
  122. ; Seperates type of content and file types
  123. info := StrSplit(LTrim(SubStr(data, StrLen(type) + 1)), " ")
  124.  
  125. for i, ext in info {
  126. this.mimes[ext] := type
  127. }
  128. }
  129. return true
  130. }
  131.  
  132. GetMimeType(file) {
  133. default := "text/plain"
  134. if (!this.mimes)
  135. return default
  136.  
  137. SplitPath, file,,, ext
  138. type := this.mimes[ext]
  139. if (!type)
  140. return default
  141. return type
  142. }
  143.  
  144. ServeFile(ByRef response, file) {
  145. f := FileOpen(file, "r")
  146. length := f.RawRead(data, f.Length)
  147. f.Close()
  148.  
  149. response.SetBody(data, length)
  150. res.headers["Content-Type"] := this.GetMimeType(file)
  151. }
  152.  
  153. SetPaths(paths) {
  154. this.paths := paths
  155. }
  156.  
  157. Handle(ByRef request) {
  158. response := new HttpResponse()
  159. if (!this.paths[request.path]) {
  160. func := this.paths["404"]
  161. response.status := 404
  162. if (func)
  163. func.(request, response, this)
  164. return response
  165. } else {
  166. this.paths[request.path].(request, response, this)
  167. }
  168. return response
  169. }
  170.  
  171. Serve(port) {
  172. this.port := port
  173. HttpServer.servers[port] := this
  174.  
  175. AHKsock_Listen(port, "HttpHandler")
  176. }
  177. }
  178.  
  179. HttpHandler(sEvent, iSocket = 0, sName = 0, sAddr = 0, sPort = 0, ByRef bData = 0, bDataLength = 0) {
  180. static sockets := {}
  181.  
  182. if (!sockets[iSocket]) {
  183. sockets[iSocket] := new Socket(iSocket)
  184. AHKsock_SockOpt(iSocket, "SO_KEEPALIVE", true)
  185. }
  186. socket := sockets[iSocket]
  187.  
  188. if (sEvent == "DISCONNECTED") {
  189. socket.request := false
  190. sockets[iSocket] := false
  191. } else if (sEvent == "SEND") {
  192. if (socket.TrySend()) {
  193. socket.Close()
  194. }
  195.  
  196. } else if (sEvent == "RECEIVED") {
  197. server := HttpServer.servers[sPort]
  198.  
  199. text := StrGet(&bData, "UTF-8")
  200. request := new HttpRequest(text)
  201.  
  202. ; Multipart request
  203. if (request.IsMultipart()) {
  204. length := request.headers["Content-Length"]
  205. request.bytesLeft := length + 0
  206.  
  207. if (request.body) {
  208. request.bytesLeft -= StrLen(request.body)
  209. }
  210. socket.request := request
  211. } else if (socket.request) {
  212. ; Get data and append it to the request body
  213. socket.request.bytesLeft -= StrLen(text)
  214. socket.request.body := socket.request.body . text
  215. }
  216.  
  217. if (socket.request) {
  218. request := socket.request
  219. if (request.bytesLeft <= 0) {
  220. request.done := true
  221. }
  222. }
  223.  
  224. response := server.Handle(request)
  225. if (response.status) {
  226. socket.SetData(response.Generate())
  227.  
  228. if (socket.TrySend()) {
  229. if (!request.IsMultipart() || (request.IsMultipart() && request.done)) {
  230. socket.Close()
  231. }
  232. }
  233. }
  234. }
  235. }
  236.  
  237. class HttpRequest
  238. {
  239. __New(data = "") {
  240. if (data)
  241. this.Parse(data)
  242. }
  243.  
  244. GetPathInfo(top) {
  245. results := []
  246. while (pos := InStr(top, " ")) {
  247. results.Insert(SubStr(top, 1, pos - 1))
  248. top := SubStr(top, pos + 1)
  249. }
  250. this.method := results[1]
  251. this.path := Uri.Decode(results[2])
  252. this.protocol := top
  253. }
  254.  
  255. GetQuery() {
  256. pos := InStr(this.path, "?")
  257. query := StrSplit(SubStr(this.path, pos + 1), "&")
  258. if (pos)
  259. this.path := SubStr(this.path, 1, pos - 1)
  260.  
  261. this.queries := {}
  262. for i, value in query {
  263. pos := InStr(value, "=")
  264. key := SubStr(value, 1, pos - 1)
  265. val := SubStr(value, pos + 1)
  266. this.queries[key] := val
  267. }
  268. }
  269.  
  270. Parse(data) {
  271. this.raw := data
  272. data := StrSplit(data, "`n`r")
  273. headers := StrSplit(data[1], "`n")
  274. this.body := LTrim(data[2], "`n")
  275.  
  276. this.GetPathInfo(headers.Remove(1))
  277. this.GetQuery()
  278. this.headers := {}
  279.  
  280. for i, line in headers {
  281. pos := InStr(line, ":")
  282. key := SubStr(line, 1, pos - 1)
  283. val := Trim(SubStr(line, pos + 1), "`n`r ")
  284.  
  285. this.headers[key] := val
  286. }
  287. }
  288.  
  289. IsMultipart() {
  290. length := this.headers["Content-Length"]
  291. expect := this.headers["Expect"]
  292.  
  293. if (expect = "100-continue" && length > 0)
  294. return true
  295. return false
  296. }
  297. }
  298.  
  299. class HttpResponse
  300. {
  301. __New() {
  302. this.headers := {}
  303. this.status := 0
  304. this.protocol := "HTTP/1.1"
  305.  
  306. this.SetBodyText("")
  307. }
  308.  
  309. Generate() {
  310. FormatTime, date,, ddd, d MMM yyyy HH:mm:ss
  311. this.headers["Date"] := date
  312.  
  313. headers := this.protocol . " " . this.status . "`n"
  314. for key, value in this.headers {
  315. headers := headers . key . ": " . value . "`n"
  316. }
  317. headers := headers . "`n"
  318. length := this.headers["Content-Length"]
  319.  
  320. buffer := new Buffer((StrLen(headers) * 2) + length)
  321. buffer.WriteStr(headers)
  322.  
  323. buffer.Append(this.body)
  324. buffer.Done()
  325.  
  326. return buffer
  327. }
  328.  
  329. SetBody(ByRef body, length) {
  330. this.body := new Buffer(length)
  331. this.body.Write(&body, length)
  332. this.headers["Content-Length"] := length
  333. }
  334.  
  335. SetBodyText(text) {
  336. this.body := Buffer.FromString(text)
  337. this.headers["Content-Length"] := this.body.length
  338. }
  339.  
  340.  
  341. }
  342.  
  343. class Socket
  344. {
  345. __New(socket) {
  346. this.socket := socket
  347. }
  348.  
  349. Close(timeout = 5000) {
  350. AHKsock_Close(this.socket, timeout)
  351. }
  352.  
  353. SetData(data) {
  354. this.data := data
  355. }
  356.  
  357. TrySend() {
  358. if (!this.data || this.data == "")
  359. return false
  360.  
  361. p := this.data.GetPointer()
  362. length := this.data.length
  363.  
  364. this.dataSent := 0
  365. loop {
  366. if ((i := AHKsock_Send(this.socket, p, length - this.dataSent)) < 0) {
  367. if (i == -2) {
  368. return
  369. } else {
  370. ; Failed to send
  371. return
  372. }
  373. }
  374.  
  375. if (i < length - this.dataSent) {
  376. this.dataSent += i
  377. } else {
  378. break
  379. }
  380. }
  381. this.dataSent := 0
  382. this.data := ""
  383.  
  384. return true
  385. }
  386. }
  387.  
  388. class Buffer
  389. {
  390. __New(len) {
  391. this.SetCapacity("buffer", len)
  392. this.length := 0
  393. }
  394.  
  395. FromString(str, encoding = "UTF-8") {
  396. length := Buffer.GetStrSize(str, encoding)
  397. buffer := new Buffer(length)
  398. buffer.WriteStr(str)
  399. return buffer
  400. }
  401.  
  402. GetStrSize(str, encoding = "UTF-8") {
  403. encodingSize := ((encoding="utf-16" || encoding="cp1200") ? 2 : 1)
  404. ; length of string, minus null char
  405. return StrPut(str, encoding) * encodingSize - encodingSize
  406. }
  407.  
  408. WriteStr(str, encoding = "UTF-8") {
  409. length := this.GetStrSize(str, encoding)
  410. VarSetCapacity(text, length)
  411. StrPut(str, &text, encoding)
  412.  
  413. this.Write(&text, length)
  414. return length
  415. }
  416.  
  417. ; data is a pointer to the data
  418. Write(data, length) {
  419. p := this.GetPointer()
  420. DllCall("RtlMoveMemory", "uint", p + this.length, "uint", data, "uint", length)
  421. this.length += length
  422. }
  423.  
  424. Append(ByRef buffer) {
  425. destP := this.GetPointer()
  426. sourceP := buffer.GetPointer()
  427.  
  428. DllCall("RtlMoveMemory", "uint", destP + this.length, "uint", sourceP, "uint", buffer.length)
  429. this.length += buffer.length
  430. }
  431.  
  432. GetPointer() {
  433. return this.GetAddress("buffer")
  434. }
  435.  
  436. Done() {
  437. this.SetCapacity("buffer", this.length)
  438. }
  439. }
  440.  
  441.  
  442. AHKsock.ahk
  443. /*! TheGood
  444. AHKsock - A simple AHK implementation of Winsock.
  445. http://www.autohotkey.com/forum/viewtopic.php?p=355775
  446. Last updated: January 19, 2011
  447.  
  448. FUNCTION LIST:
  449.  
  450. ________________________________________
  451. AHKsock_Listen(sPort, sFunction = False)
  452.  
  453. Tells AHKsock to listen on the port in sPort, and call the function in sFunction when events occur. If sPort is a port on
  454. which AHKsock is already listening, the action taken depends on sFunction:
  455. - If sFunction is False, AHKsock will stop listening on the port in sPort.
  456. - If sFunction is "()", AHKsock will return the name of the current function AHKsock calls when
  457. a client connects on the port in sPort.
  458. - If sFunction is a valid function, AHKsock will set that function as the new function to call
  459. when a client connects on the port in sPort.
  460.  
  461. Returns blank on success. On failure, it returns one of the following positive integer:
  462. 2: sFunction is not a valid function.
  463. 3: The WSAStartup() call failed. The error is in ErrorLevel.
  464. 4: The Winsock DLL does not support version 2.2.
  465. 5: The getaddrinfo() call failed. The error is in ErrorLevel.
  466. 6: The socket() call failed. The error is in ErrorLevel.
  467. 7: The bind() call failed. The error is in ErrorLevel.
  468. 8: The WSAAsyncSelect() call failed. The error is in ErrorLevel.
  469. 9: The listen() call failed. The error is in ErrorLevel.
  470.  
  471. For the failures which affect ErrorLevel, ErrorLevel will contain either the reason the DllCall itself failed (ie. -1, -2,
  472. An, etc... as laid out in the AHK docs for DllCall) or the Windows Sockets Error Code as defined at:
  473. http://msdn.microsoft.com/en-us/library/ms740668
  474.  
  475. See the section titled "STRUCTURE OF THE EVENT-HANDLING FUNCTION AND MORE INFO ABOUT SOCKETS" for more info about how the
  476. function in sFunction interacts with AHKsock.
  477.  
  478. ________________________________________
  479. AHKsock_Connect(sName, sPort, sFunction)
  480.  
  481. Tells AHKsock to connect to the hostname or IP address in sName on the port in sPort, and call the function in sFunction
  482. when events occur.
  483.  
  484. Although the function will return right away, the connection attempt will still be in progress. Once the connection attempt
  485. is over, successful or not, sFunction will receive the CONNECTED event. Note that it is important that once AHKsock_Connect
  486. returns, the current thread must stay (or soon after must become) interruptible so that sFunction can be called once the
  487. connection attempt is over.
  488.  
  489. AHKsock_Connect can only be called again once the previous connection attempt is over. To check if AHKsock_Connect is ready
  490. to make another connection attempt, you may keep polling it by calling AHKsock_Connect(0,0,0) until it returns False.
  491.  
  492. Returns blank on success. On failure, it returns one of the following positive integer:
  493. 1: AHKsock_Connect is still processing a connection attempt. ErrorLevel contains the name and the port of that
  494. connection attempt, separated by a tab.
  495. 2: sFunction is not a valid function.
  496. 3: The WSAStartup() call failed. The error is in ErrorLevel.
  497. 4: The Winsock DLL does not support version 2.2.
  498. 5: The getaddrinfo() call failed. The error is in ErrorLevel.
  499. 6: The socket() call failed. The error is in ErrorLevel.
  500. 7: The WSAAsyncSelect() call failed. The error is in ErrorLevel.
  501. 8: The connect() call failed. The error is in ErrorLevel.
  502.  
  503. For the failures which affect ErrorLevel, ErrorLevel will contain either the reason the DllCall itself failed (ie. -1, -2,
  504. An, etc... as laid out in the AHK docs for DllCall) or the Windows Sockets Error Code as defined at:
  505. http://msdn.microsoft.com/en-us/library/ms740668
  506.  
  507. See the section titled "STRUCTURE OF THE EVENT-HANDLING FUNCTION AND MORE INFO ABOUT SOCKETS" for more info about how the
  508. function in sFunction interacts with AHKsock.
  509.  
  510. _______________________________________
  511. AHKsock_Send(iSocket, ptrData, iLength)
  512.  
  513. Sends the data of length iLength to which ptrData points to the connected socket in iSocket.
  514.  
  515. Returns the number of bytes sent on success. This can be less than the number requested to be sent in the iLength parameter,
  516. i.e. between 1 and iLength. This would occur if no buffer space is available within the transport system to hold the data to
  517. be transmitted, in which case the number of bytes sent can be between 1 and the requested length, depending on buffer
  518. availability on both the client and server computers. On failure, it returns one of the following negative integer:
  519. -1: WSAStartup hasn't been called yet.
  520. -2: Received WSAEWOULDBLOCK. This means that calling send() would have blocked the thread.
  521. -3: The send() call failed. The error is in ErrorLevel.
  522. -4: The socket specified in iSocket is not a valid socket. This means either that the socket in iSocket hasn't been
  523. created using AHKsock_Connect or AHKsock_Listen, or that the socket has already been destroyed.
  524. -5: The socket specified in iSocket is not cleared for sending. You haven't waited for the SEND event before calling,
  525. either ever, or not since you last received WSAEWOULDBLOCK.
  526.  
  527. You may start sending data to the connected socket in iSocket only after the socket's associated function receives the first
  528. SEND event. Upon receiving the event, you may keep calling AHKsock_Send to send data until you receive the error -2, at
  529. which point you must wait once again until you receive another SEND event before sending more data. Not waiting for the SEND
  530. event results in receiving error -5 when calling AHKsock_Send.
  531.  
  532. For the failures which affect ErrorLevel, ErrorLevel will contain either the reason the DllCall itself failed (ie. -1, -2,
  533. An, etc... as laid out in the AHK docs for DllCall) or the Windows Sockets Error Code as defined at:
  534. http://msdn.microsoft.com/en-us/library/ms740668
  535.  
  536. ____________________________________________
  537. AHKsock_ForceSend(iSocket, ptrData, iLength)
  538.  
  539. This function is exactly the same as AHKsock_Send, but with three differences:
  540. - If only part of the data could be sent, it will automatically keep trying to send the remaining part.
  541. - If it receives WSAEWOULDBLOCK, it will wait for the socket's SEND event and try sending the data again.
  542. - If the data buffer to send is larger than the socket's send buffer size, it will automatically send the data in
  543. smaller chunks in order to avoid a performance hit. See http://support.microsoft.com/kb/823764 for more info.
  544.  
  545. Therefore, AHKsock_ForceSend will return only when all the data has been sent. Because this function relies on waiting for
  546. the socket's SEND event before continuing to send data, it cannot be called in a critical thread. Also, for the same reason,
  547. it cannot be called from a socket's associated function (not specifically iSocket's associated function, but any socket's
  548. associated function).
  549.  
  550. Another limitation to consider when choosing between AHKsock_Send and AHKsock_ForceSend is that AHKsock_ForceSend will not
  551. return until all the data has been sent (unless an error occurs). Although the script will still be responsive (new threads
  552. will still be able to launch), the thread from which it was called will not resume until it returns. Therefore, if sending
  553. a large amount of data, you should either use AHKsock_Send, or use AHKsock_ForceSend by feeding it smaller pieces of the
  554. data, allowing you to update the GUI if necessary (e.g. a progress bar).
  555.  
  556. Returns blank on success, which means that all the data to which ptrData points of length iLength has been sent. On failure,
  557. it returns one of the following negative integer:
  558. -1: WSAStartup hasn't been called yet.
  559. -3: The send() call failed. The error is in ErrorLevel.
  560. -4: The socket specified in iSocket is not a valid socket. This means either that the socket in iSocket hasn't been
  561. created using AHKsock_Connect or AHKsock_Listen, or that the socket has already been destroyed.
  562. -5: The current thread is critical.
  563. -6: The getsockopt() call failed. The error is in ErrorLevel.
  564.  
  565. For the failures which affect ErrorLevel, ErrorLevel will contain either the reason the DllCall itself failed (ie. -1, -2,
  566. An, etc... as laid out in the AHK docs for DllCall) or the Windows Sockets Error Code as defined at:
  567. http://msdn.microsoft.com/en-us/library/ms740668
  568.  
  569. ____________________________________________
  570. AHKsock_Close(iSocket = -1, iTimeout = 5000)
  571.  
  572. Closes the socket in iSocket. If no socket is specified, AHKsock_Close will close all the sockets on record, as well as
  573. terminate use of the Winsock 2 DLL (by calling WSACleanup). If graceful shutdown cannot be attained after the timeout
  574. specified in iTimeout (in milliseconds), it will perform a hard shutdown before calling WSACleanup to free resources. See
  575. the section titled "NOTES ON CLOSING SOCKETS AND AHKsock_Close" for more information.
  576.  
  577. Returns blank on success. On failure, it returns one of the following positive integer:
  578. 1: The shutdown() call failed. The error is in ErrorLevel. AHKsock_Close forcefully closed the socket and freed the
  579. associated resources.
  580.  
  581. Note that when AHKsock_Close is called with no socket specified, it will never return an error.
  582.  
  583. For the failures which affect ErrorLevel, ErrorLevel will contain either the reason the DllCall itself failed (ie. -1, -2,
  584. An, etc... as laid out in the AHK docs for DllCall) or the Windows Sockets Error Code as defined at:
  585. http://msdn.microsoft.com/en-us/library/ms740668
  586.  
  587. ___________________________________________________________
  588. AHKsock_GetAddrInfo(sHostName, ByRef sIPList, bOne = False)
  589.  
  590. Retrieves the list of IP addresses that correspond to the hostname in sHostName. The list is contained in sIPList, delimited
  591. by newline characters. If bOne is True, only one IP (the first one) will be returned.
  592.  
  593. Returns blank on success. On failure, it returns one of the following positive integer:
  594. 1: The WSAStartup() call failed. The error is in ErrorLevel.
  595. 2: The Winsock DLL does not support version 2.2.
  596. 3: Received WSAHOST_NOT_FOUND. No such host is known.
  597. 4: The getaddrinfo() call failed. The error is in ErrorLevel.
  598.  
  599. For the failures which affect ErrorLevel, ErrorLevel will contain either the reason the DllCall itself failed (ie. -1, -2,
  600. An, etc... as laid out in the AHK docs for DllCall) or the Windows Sockets Error Code as defined at:
  601. http://msdn.microsoft.com/en-us/library/ms740668
  602.  
  603. _________________________________________________________________________
  604. AHKsock_GetNameInfo(sIP, ByRef sHostName, sPort = 0, ByRef sService = "")
  605.  
  606. Retrieves the hostname that corresponds to the IP address in sIP. If a port in sPort is supplied, it also retrieves the
  607. service that corresponds to the port in sPort.
  608.  
  609. Returns blank on success. On failure, it returns on of the following positive integer:
  610. 1: The WSAStartup() call failed. The error is in ErrorLevel.
  611. 2: The Winsock DLL does not support version 2.2.
  612. 3: The IP address supplied in sIP is invalid.
  613. 4: The getnameinfo() call failed. The error is in ErrorLevel.
  614.  
  615. For the failures which affect ErrorLevel, ErrorLevel will contain either the reason the DllCall itself failed (ie. -1, -2,
  616. An, etc... as laid out in the AHK docs for DllCall) or the Windows Sockets Error Code as defined at:
  617. http://msdn.microsoft.com/en-us/library/ms740668
  618.  
  619. ______________________________________________
  620. AHKsock_SockOpt(iSocket, sOption, iValue = -1)
  621.  
  622. Retrieves or sets a socket option. Supported options are:
  623. SO_KEEPALIVE: Enable/Disable sending keep-alives. iValue must be True/False to enable/disable. Disabled by default.
  624. SO_SNDBUF: Total buffer space reserved for sends. Set iValue to 0 to completely disable the buffer. Default is 8 KB.
  625. SO_RCVBUF: Total buffer space reserved for receives. Default is 8 KB.
  626. TCP_NODELAY: Enable/Disable the Nagle algorithm for send coalescing. Set iValue to True to disable the Nagle algorithm,
  627. set iValue to False to enable the Nagle algorithm, which is the default.
  628.  
  629. It is usually best to leave these options to their default (especially the Nagle algorithm). Only change them if you
  630. understand the consequences. See MSDN for more information on those options.
  631.  
  632. If iValue is specified, it sets the option to iValue and returns blank on success. If iValue is left as -1, it returns the
  633. value of the option specified. On failure, it returns one of the following negative integer:
  634. -1: The getsockopt() failed. The error is in ErrorLevel.
  635. -2: The setsockopt() failed. The error is in ErrorLevel.
  636.  
  637. For the failures which affect ErrorLevel, ErrorLevel will contain either the reason the DllCall itself failed (ie. -1, -2,
  638. An, etc... as laid out in the AHK docs for DllCall) or the Windows Sockets Error Code as defined at:
  639. http://msdn.microsoft.com/en-us/library/ms740668
  640.  
  641. _______________________________________
  642. AHKsock_Settings(sSetting, sValue = "")
  643.  
  644. Changes the AHKsock setting in sSetting to sValue. If sValue is blank, the current value for that setting is returned. If
  645. sValue is the word "Reset", the setting is restored to its default value. The possible settings are:
  646. Message: Determines the Windows message numbers used to monitor network events. The message number in iMessage and the
  647. next number will be used. Default value is 0x8000. For example, calling AHKsock_Settings("Message", 0x8005)
  648. will cause AHKsock to use 0x8005 and 0x8006 to monitor network events.
  649. Buffer: Determines the size of the buffer (in bytes) used when receiving data. This is thus the maximum size of bData
  650. when the RECEIVED event is raised. If the data received is more than the buffer size, multiple recv() calls
  651. (and thus multiple RECEIVED events) will be needed. Note that you shouldn't use this setting as a means of
  652. delimiting frames. See the "NOTES ON RECEIVING AND SENDING DATA" section for more information about receiving
  653. and sending data. Default value is 64 KB, which is the maximum for TCP.
  654.  
  655. If you do call AHKsock_Settings to change the values from their default ones, it is best to do so at the beginning of the
  656. script. The message number used cannot be changed as long as there are active connections.
  657.  
  658. ______________________________________
  659. AHKsock_ErrorHandler(sFunction = """")
  660.  
  661. Sets the function in sFunction to be the new error handler. If sFunction is left at its default value, it returns the name
  662. of the current error handling function.
  663.  
  664. An error-handling function is optional, but may be useful when troubleshooting applications. The function will be called
  665. anytime there is an error that arises in a thread which wasn't called by the user but by the receival of a Windows message
  666. which was registered using OnMessage.
  667.  
  668. The function in sFunction must be of the following format:
  669. MyErrorHandler(iError, iSocket)
  670.  
  671. The possible values for iError are:
  672. 1: The connect() call failed. The error is in ErrorLevel.
  673. 2: The WSAAsyncSelect() call failed. The error is in ErrorLevel.
  674. 3: The socket() call failed. The error is in ErrorLevel.
  675. 4: The WSAAsyncSelect() call failed. The error is in ErrorLevel.
  676. 5: The connect() call failed. The error is in ErrorLevel.
  677. 6: FD_READ event received with an error. The error is in ErrorLevel. The socket is in iSocket.
  678. 7: The recv() call failed. The error is in ErrorLevel. The socket is in iSocket.
  679. 8: FD_WRITE event received with an error. The error is in ErrorLevel. The socket is in iSocket.
  680. 9: FD_ACCEPT event received with an error. The error is in ErrorLevel. The socket is in iSocket.
  681. 10: The accept() call failed. The error is in ErrorLevel. The listening socket is in iSocket.
  682. 11: The WSAAsyncSelect() call failed. The error is in ErrorLevel. The listening socket is in iSocket.
  683. 12: The listen() call failed. The error is in ErrorLevel. The listening socket is in iSocket.
  684. 13: The shutdown() call failed. The error is in ErrorLevel. The socket is in iSocket.
  685.  
  686. For the failures which affect ErrorLevel, ErrorLevel will contain either the reason the DllCall itself failed (ie. -1, -2,
  687. An, etc... as laid out in the AHK docs for DllCall) or the Windows Sockets Error Code as defined at:
  688. http://msdn.microsoft.com/en-us/library/ms740668
  689.  
  690. __________________________________________________________________
  691. NOTES ON SOCKETS AND THE STRUCTURE OF THE EVENT-HANDLING FUNCTION:
  692.  
  693. The functions used in the sFunction parameter of AHKsock_Listen and AHKsock_Connect must be of the following format:
  694.  
  695. MyFunction(sEvent, iSocket = 0, sName = 0, sAddr = 0, sPort = 0, ByRef bData = 0, bDataLength = 0)
  696.  
  697. The variable sEvent contains the event for which MyFunction was called. The event raised is associated with one and only one
  698. socket; the one in iSocket. The meaning of the possible events that can occur depend on the type of socket involved. AHKsock
  699. deals with three different types of sockets:
  700. - Listening sockets: These sockets are created by a call to AHKsock_Listen. All they do is wait for clients to request
  701. a connection. These sockets will never appear as the iSocket parameter because requests for connections are
  702. immediately accepted, and MyFunction immediately receives the ACCEPTED event with iSocket set to the accepted socket.
  703. - Accepted sockets: These sockets are created once a listening socket receives an incoming connection attempt from a
  704. client and accepts it. They are thus the sockets that servers use to communicate with clients.
  705. - Connected sockets: These sockets are created by a successful call to AHKsock_Connect. These are the sockets that
  706. clients use to communicate with servers.
  707.  
  708. More info about sockets:
  709. - You may have multiple client sockets connecting to the same listening socket (ie. on the same port).
  710. - You may have multiple listening sockets for different ports.
  711. - You cannot have more than one listening socket for the same port (or you will receive a bind() error).
  712. - Every single connection between a client and a server will have its own client socket on the client side, and its own
  713. server (accepted) socket on the server side.
  714.  
  715. For all of the events that the event-handling function receives,
  716. - sEvent contains the event that occurred (as described below),
  717. - iSocket contains the socket on which the event occurred,
  718. - sName contains a value which depends on the type of socket in iSocket:
  719. - If the socket is an accepted socket, sName is empty.
  720. - If the socket is a connected socket, sName is the same value as the sName parameter that was used when
  721. AHKsock_Connect was called to create the socket. Since AHKsock_Connect accepts both hostnames and IP addresses,
  722. sName may contain either.
  723. - sAddr contains the IP address of the socket's endpoint (i.e. the peer's IP address). This means that if the socket in
  724. iSocket is an accepted socket, sAddr contains the IP address of the client. Conversely, if it is a connected socket,
  725. sAddr contains the server's IP.
  726. - sPort contains the server port on which the connection was accepted.
  727.  
  728. Obviously, if your script only calls AHKsock_Listen (acting as a server) or AHKsock_Connect (acting as a client) you don't
  729. need to check if the socket in iSocket is an accepted socket or a connected socket, since it can only be one or the other.
  730. But if you do call both AHKsock_Listen and AHKsock_Connect with both of them using the same function (e.g. MyFunction), then
  731. you will need to check what type of socket iSocket is by checking the sName parameter.
  732.  
  733. Of course, it would be easier to simply have two different functions, for example, MyFunction1 and MyFunction2, with one
  734. handling the server part and the other handling the client part so that you don't need to check what type of socket iSocket
  735. is when each function is called. However, this might not be necessary if both server and client are "symmetrical" (i.e. the
  736. conversation doesn't actually change whether or not we're on the server side or the client side). See Example 3 for an
  737. example of this, where only one function is used for both server and client sockets.
  738.  
  739. The variable sEvent can be one of the following values if iSocket is an accepted socket:
  740. sEvent = Event Description:
  741. ACCEPTED A client connection was accepted (see the "Listening sockets" section above for more details).
  742. CONNECTED <Does not occur on accepted sockets>
  743. DISCONNECTED The client disconnected (see AHKsock_Close for more details).
  744. SEND You may now send data to the client (see AHKsock_Send for more details).
  745. RECEIVED You received data from the client. The data received is in bData and the length is in bDataLength.
  746. SENDLAST The client is disconnecting. This is your last chance to send data to it. Once this function returns,
  747. disconnection will occur. This event only occurs on the side which did not initiate shutdown (see
  748. AHKsock_Close for more details).
  749.  
  750. The variable sEvent can be one of the following values if iSocket is a connected socket:
  751. sEvent = Event Description:
  752. ACCEPTED <Does not occur on connected sockets>
  753. CONNECTED The connection attempt initiated by calling AHKsock_Connect has completed (see AHKsock_Connect for more
  754. details). If it was successful, iSocket will equal the client socket. If it failed, iSocket will equal -1.
  755. To get the error code that the failure returned, set an error handling function with AHKsock_ErrorHandler,
  756. and read ErrorLevel when iError is equal to 1.
  757. DISCONNECTED The server disconnected (see AHKsock_Close for more details).
  758. SEND You may now send data to the server (see AHKsock_Send for more details).
  759. RECEIVED You received data from the server. The data received is in bData and the length is in bDataLength.
  760. SENDLAST The server is disconnecting. This is your last chance to send data to it. Once this function returns,
  761. disconnection will occur. This event only occurs on the side which did not initiate shutdown (see
  762. AHKsock_Close for more details).
  763.  
  764. More information: The event-handling functions described in here are always called with the Critical setting on. This is
  765. necessary in order to ensure proper processing of messages. Note that as long as the event-handling function does not
  766. return, AHKsock cannot process other network messages. Although messages are buffered, smooth operation might suffer when
  767. letting the function run for longer than it should.
  768.  
  769. ___________________________________________
  770. NOTES ON CLOSING SOCKETS AND AHKsock_Close:
  771.  
  772. There are a few things to note about the AHKsock_Close function. The most important one is this: because the OnExit
  773. subroutine cannot be made interruptible if running due to a call to Exit/ExitApp, AHKsock_Close will not be able to execute
  774. a graceful shutdown if it is called from there.
  775.  
  776. A graceful shutdown refers to the proper way of closing a TCP connection. It consists of an exchange of special TCP messages
  777. between the two endpoints to acknowledge that the connection is about to close. It also fires the SENDLAST event in the
  778. socket's associated function to notify that this is the last chance it will have to send data before disconnection. Note
  779. that listening sockets cannot (and therefore do not need to) be gracefully shutdown as it is not an end-to-end connection.
  780. (In practice, you will never have to manually call AHKsock_Close on a listening socket because you do not have access to
  781. them. The socket is closed when you stop listening by calling AHKsock_Listen with no specified value for the second
  782. parameter.)
  783.  
  784. In order to allow the socket(s) connection(s) to gracefully shutdown (which is always preferable), AHKsock_Close must be
  785. called in a thread which is, or can be made, interruptible. If it is called with a specified socket in iSocket, it will
  786. initiate a graceful shutdown for that socket alone. If it is called with no socket specified, it will initiate a graceful
  787. shutdown for all connected/accepted sockets, and once done, deregister itself from the Windows Sockets implementation and
  788. allow the implementation to free any resources allocated for Winsock (by calling WSACleanup). In that case, if any
  789. subsequent AHKsock function is called, Winsock will automatically be restarted.
  790.  
  791. Therefore, before exiting your application, AHKsock_Close must be called at least once with no socket specified in order to
  792. free Winsock resources. This can be done in the OnExit subroutine, either if you do not wish to perform a graceful shutdown
  793. (which is not recommended), or if you have already gracefully shutdown all the sockets individually before calling
  794. Exit/ExitApp. Of course, it doesn't have to be done in the OnExit subroutine and can be done anytime before (which is the
  795. recommended method because AHKsock will automatically gracefully shutdown all the sockets on record).
  796.  
  797. This behaviour has a few repercussions on your application's design. If the only way for the user to terminate your
  798. application is through AHK's default Exit menu item in the tray menu, then upon selecting the Exit menu item, the OnExit sub
  799. will fire, and your application will not have a chance to gracefully shutdown connected sockets. One way around this is to
  800. add your own menu item which will in turn call AHKsock_Close with no socket specified before calling ExitApp to enter the
  801. OnExit sub. See AHKsock Example 1 for an example of this.
  802.  
  803. This is how the graceful shutdown process occurs between two connected peers:
  804. a> Once one of the peers (it may be the server of the client) is done sending all its data, it calls AHKsock_Close to
  805. shutdown the socket. (It is not a good idea to have the last peer receiving data call AHKsock_Close. This will result
  806. in AHKsock_Send errors on the other peer if more data needs to be sent.) In the next steps, we refer to the peer that
  807. first calls AHKsock_Close as the invoker, and the other peer simply as the peer.
  808. b> The peer receives the invoker's intention to close the connection and is given one last chance to send any remaining
  809. data. This is when the peer's socket's associated function receives the SENDLAST event.
  810. c> Once the peer is done sending any remaining data (if any), it also calls AHKsock_Close on that same socket to shut it
  811. down, and then close the socket for good. This happens once the peer's function that received the SENDLAST event
  812. returns from the event. At this point, the peer's socket's associated function receives the DISCONNECTED event.
  813. d> This happens in parallel with c>. After the invoker receives the peer's final data (if any), as well as notice that
  814. the peer has also called AHKsock_Close on the socket, the invoker finally also closes the socket for good. At this
  815. point, the socket's associated function also receives the DISCONNECTED event.
  816.  
  817. When AHKsock_Close is called with no socket specified, this process occurs (in parallel) for every connected socket on
  818. record.
  819.  
  820. ____________________________________
  821. NOTES ON RECEIVING AND SENDING DATA:
  822.  
  823. It's important to understand that AHKsock uses the TCP protocol, which is a stream protocol. This means that the data
  824. received comes as a stream, with no apparent boundaries (i.e. frames or packets). For example, if a peer sends you a string,
  825. it's possible that half the string is received in one RECEIVED event and the other half is received in the next. Of course,
  826. the smaller the string, the less likely this happens. Conversely, the larger the string, the more likely this will occur.
  827.  
  828. Similarly, calling AHKsock_Send will not necessarily send the data right away. If multiple AHKsock_Send calls are issued,
  829. Winsock might, under certain conditions, wait and accumulate data to send before sending it all at once. This process is
  830. called coalescing. For example, if you send two strings to your peer by using two individual AHKsock_Send calls, the peer
  831. will not necessarily receive two consecutive RECEIVED events for each string, but might instead receive both strings through
  832. a single RECEIVED event.
  833.  
  834. One efficient method of receiving data as frames is to use length-prefixing. Length-prefixing means that before sending a
  835. frame of variable length to your peer, you first tell it how many bytes will be in the frame. This way, your peer can
  836. divide the received data into frames that can be individually processed. If it received less than a frame, it can store the
  837. received data and wait for the remaining data to arrive before processing the completed frame with the length specified.
  838. This technique is used in in AHKsock Example 3, where peers send each other strings by first declaring how long the string
  839. will be (see the StreamProcessor function of Example 3).
  840.  
  841. ____________________________________
  842. NOTES ON TESTING A STREAM PROCESSOR:
  843.  
  844. As you write applications that use length-prefixing as described above, you might find it hard to test their ability to
  845. properly cut up and/or put together the data into frames when testing them on the same machine or on a LAN (because the
  846. latency is too low and it is thus harder to stress the connection).
  847.  
  848. In this case, what you can do to properly test them is to uncomment the comment block in AHKsock_Send, which will sometimes
  849. purposely fail to send part of the data requested. This will allow you to simulate what could happen on a connection going
  850. through the Internet. You may change the probability of failure by changing the number in the If statement.
  851.  
  852. If your application can still work after uncommenting the block, then it is a sign that it is properly handling frames split
  853. across multiple RECEIVED events. This would also demonstrate your application's ability to cope with partially sent data.
  854. */
  855.  
  856. /****************\
  857. Main functions |
  858. */
  859.  
  860. AHKsock_Listen(sPort, sFunction = False) {
  861.  
  862. ;Check if there is already a socket listening on this port
  863. If (sktListen := AHKsock_Sockets("GetSocketFromNamePort", A_Space, sPort)) {
  864.  
  865. ;Check if we're stopping the listening
  866. If Not sFunction {
  867. AHKsock_Close(sktListen) ;Close the socket
  868.  
  869. ;Check if we're retrieving the current function
  870. } Else If (sFunction = "()") {
  871. Return AHKsock_Sockets("GetFunction", sktListen)
  872.  
  873. ;Check if it's a different function
  874. } Else If (sFunction <> AHKsock_Sockets("GetFunction", sktListen))
  875. AHKsock_Sockets("SetFunction", sktListen, sFunction) ;Update it
  876.  
  877. Return ;We're done
  878. }
  879.  
  880. ;Make sure we even have a function
  881. If Not IsFunc(sFunction)
  882. Return 2 ;sFunction is not a valid function.
  883.  
  884. ;Make sure Winsock has been started up
  885. If (i := AHKsock_Startup())
  886. Return (i = 1) ? 3 ;The WSAStartup() call failed. The error is in ErrorLevel.
  887. : 4 ;The Winsock DLL does not support version 2.2.
  888.  
  889. ;Resolve the local address and port to be used by the server
  890. VarSetCapacity(aiHints, 16 + 4 * A_PtrSize, 0)
  891. NumPut(1, aiHints, 0, "Int") ;ai_flags = AI_PASSIVE
  892. NumPut(2, aiHints, 4, "Int") ;ai_family = AF_INET
  893. NumPut(1, aiHints, 8, "Int") ;ai_socktype = SOCK_STREAM
  894. NumPut(6, aiHints, 12, "Int") ;ai_protocol = IPPROTO_TCP
  895. iResult := DllCall("Ws2_32\GetAddrInfo", "Ptr", 0, "Ptr", &sPort, "Ptr", &aiHints, "Ptr*", aiResult)
  896. If (iResult != 0) Or ErrorLevel { ;Check for error
  897. ErrorLevel := ErrorLevel ? ErrorLevel : iResult
  898. Return 5 ;The getaddrinfo() call failed. The error is in ErrorLevel.
  899. }
  900.  
  901. sktListen := -1 ;INVALID_SOCKET
  902. sktListen := DllCall("Ws2_32\socket", "Int", NumGet(aiResult+0, 04, "Int")
  903. , "Int", NumGet(aiResult+0, 08, "Int")
  904. , "Int", NumGet(aiResult+0, 12, "Int"), "Ptr")
  905. If (sktListen = -1) Or ErrorLevel { ;Check for INVALID_SOCKET
  906. sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  907. DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult)
  908. ErrorLevel := sErrorLevel
  909. Return 6 ;The socket() call failed. The error is in ErrorLevel.
  910. }
  911.  
  912. ;Setup the TCP listening socket
  913. iResult := DllCall("Ws2_32\bind", "Ptr", sktListen, "Ptr", NumGet(aiResult+0, 16 + 2 * A_PtrSize), "Int", NumGet(aiResult+0, 16, "Ptr"))
  914. If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR
  915. sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  916. DllCall("Ws2_32\closesocket", "Ptr", sktListen)
  917. DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult)
  918. ErrorLevel := sErrorLevel
  919. Return 7 ;The bind() call failed. The error is in ErrorLevel.
  920. }
  921.  
  922. DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult)
  923.  
  924. ;Add socket to array with A_Space for Name and IP to indicate that it's a listening socket
  925. AHKsock_Sockets("Add", sktListen, A_Space, A_Space, sPort, sFunction)
  926.  
  927. ;We must now actually register the socket
  928. If AHKsock_RegisterAsyncSelect(sktListen) {
  929. sErrorLevel := ErrorLevel
  930. DllCall("Ws2_32\closesocket", "Ptr", sktListen)
  931. AHKsock_Sockets("Delete", sktListen) ;Remove from array
  932. ErrorLevel := sErrorLevel
  933. Return 8 ;The WSAAsyncSelect() call failed. The error is in ErrorLevel.
  934. }
  935.  
  936. ;Start listening for incoming connections
  937. iResult := DllCall("Ws2_32\listen", "Ptr", sktListen, "Int", 0x7FFFFFFF) ;SOMAXCONN
  938. If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR
  939. sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  940. DllCall("Ws2_32\closesocket", "Ptr", sktListen)
  941. AHKsock_Sockets("Delete", sktListen) ;Remove from array
  942. ErrorLevel := sErrorLevel
  943. Return 9 ;The listen() call failed. The error is in ErrorLevel.
  944. }
  945. }
  946.  
  947. AHKsock_Connect(sName, sPort, sFunction) {
  948. Static aiResult, iPointer, bProcessing, iMessage
  949. Static sCurName, sCurPort, sCurFunction, sktConnect
  950.  
  951. ;Check if it's just to inquire whether or not a call is possible
  952. If (Not sName And Not sPort And Not sFunction)
  953. Return bProcessing
  954.  
  955. ;Check if we're busy
  956. If bProcessing And (sFunction != iMessage) {
  957. ErrorLevel := sCurName A_Tab sCurPort
  958. Return 1 ;AHKsock_Connect is still processing a connection attempt. ErrorLevel contains the name and the port,
  959. ;delimited by a tab.
  960. } Else If bProcessing { ;sFunction = iMessage. The connect operation has finished.
  961.  
  962. ;Check if it was successful
  963. If (i := sPort >> 16) {
  964.  
  965. ;Close the socket that failed
  966. DllCall("Ws2_32\closesocket", "Ptr", sktConnect)
  967.  
  968. ;Get the next pointer. ai_next
  969. iPointer := NumGet(iPointer+0, 16 + 3 * A_PtrSize)
  970.  
  971. ;Check if we reached the end of the linked structs
  972. If (iPointer = 0) {
  973.  
  974. ;We can now free the chain of addrinfo structs
  975. DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult)
  976.  
  977. ;This is to ensure that the user can call AHKsock_Connect() right away upon receiving the message.
  978. bProcessing := False
  979.  
  980. ;Raise an error (can't use Return 1 because we were called asynchronously)
  981. ErrorLevel := i
  982. AHKsock_RaiseError(1) ;The connect() call failed. The error is in ErrorLevel.
  983.  
  984. ;Call the function to signal that connection failed
  985. If IsFunc(sCurFunction)
  986. %sCurFunction%("CONNECTED", -1, sCurName, 0, sCurPort)
  987.  
  988. Return
  989. }
  990.  
  991. } Else { ;Successful connection!
  992.  
  993. ;Get the IP we successfully connected to
  994. sIP := DllCall("Ws2_32\inet_ntoa", "UInt", NumGet(NumGet(iPointer+0, 16 + 2 * A_PtrSize)+4, 0, "UInt"), "AStr")
  995.  
  996. ;We can now free the chain of ADDRINFO structs
  997. DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult)
  998.  
  999. ;Add socket to array
  1000. AHKsock_Sockets("Add", sktConnect, sCurName, sIP, sCurPort, sCurFunction)
  1001.  
  1002. ;This is to ensure that the user can call AHKsock_Connect() right away upon receiving the message.
  1003. bProcessing := False
  1004.  
  1005. ;Do this small bit in Critical so that AHKsock_AsyncSelect doesn't receive
  1006. ;any FD messages before we call the user function
  1007. Critical
  1008.  
  1009. ;We must now actually register the socket
  1010. If AHKsock_RegisterAsyncSelect(sktConnect) {
  1011. sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  1012. DllCall("Ws2_32\closesocket", "Ptr", sktConnect)
  1013. AHKsock_Sockets("Delete", sktConnect) ;Remove from array
  1014. ErrorLevel := sErrorLevel
  1015. AHKsock_RaiseError(2) ;The WSAAsyncSelect() call failed. The error is in ErrorLevel.
  1016.  
  1017. If IsFunc(sCurFunction) ;Call the function to signal that connection failed
  1018. %sCurFunction%("CONNECTED", -1, sCurName, 0, sCurPort)
  1019.  
  1020. } Else If IsFunc(sCurFunction) ;Call the function to signal that connection was successful
  1021. %sCurFunction%("CONNECTED", sktConnect, sCurName, sIP, sCurPort)
  1022.  
  1023. Return
  1024. }
  1025.  
  1026. } Else { ;We were called
  1027.  
  1028. ;Make sure we even have a function
  1029. If Not IsFunc(sFunction)
  1030. Return 2 ;sFunction is not a valid function.
  1031.  
  1032. bProcessing := True ;Block future calls to AHKsock_Connect() until we're done
  1033.  
  1034. ;Keep the values
  1035. sCurName := sName
  1036. sCurPort := sPort
  1037. sCurFunction := sFunction
  1038.  
  1039. ;Make sure Winsock has been started up
  1040. If (i := AHKsock_Startup()) {
  1041. bProcessing := False
  1042. Return (i = 1) ? 3 ;The WSAStartup() call failed. The error is in ErrorLevel.
  1043. : 4 ;The Winsock DLL does not support version 2.2.
  1044. }
  1045.  
  1046. ;Resolve the server address and port
  1047. VarSetCapacity(aiHints, 16 + 4 * A_PtrSize, 0)
  1048. NumPut(2, aiHints, 4, "Int") ;ai_family = AF_INET
  1049. NumPut(1, aiHints, 8, "Int") ;ai_socktype = SOCK_STREAM
  1050. NumPut(6, aiHints, 12, "Int") ;ai_protocol = IPPROTO_TCP
  1051. iResult := DllCall("Ws2_32\GetAddrInfo", "Ptr", &sName, "Ptr", &sPort, "Ptr", &aiHints, "Ptr*", aiResult)
  1052. If (iResult != 0) Or ErrorLevel { ;Check for error
  1053. ErrorLevel := ErrorLevel ? ErrorLevel : iResult
  1054. bProcessing := False
  1055. Return 5 ;The getaddrinfo() call failed. The error is in ErrorLevel.
  1056. }
  1057.  
  1058. ;Start with the first struct
  1059. iPointer := aiResult
  1060. }
  1061.  
  1062. ;Create a SOCKET for connecting to server
  1063. sktConnect := DllCall("Ws2_32\socket", "Int", NumGet(iPointer+0, 04, "Int")
  1064. , "Int", NumGet(iPointer+0, 08, "Int")
  1065. , "Int", NumGet(iPointer+0, 12, "Int"), "Ptr")
  1066. If (sktConnect = 0xFFFFFFFF) Or ErrorLevel { ;Check for INVALID_SOCKET
  1067. sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  1068. DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult)
  1069. bProcessing := False
  1070. ErrorLevel := sErrorLevel
  1071. If (sFunction = iMessage) { ;Check if we were called asynchronously
  1072. AHKsock_RaiseError(3) ;The socket() call failed. The error is in ErrorLevel.
  1073.  
  1074. ;Call the function to signal that connection failed
  1075. If IsFunc(sCurFunction)
  1076. %sCurFunction%("CONNECTED", -1)
  1077. }
  1078. Return 6 ;The socket() call failed. The error is in ErrorLevel.
  1079. }
  1080.  
  1081. ;Register the socket to know when the connect() function is done. FD_CONNECT = 16
  1082. iMessage := AHKsock_Settings("Message") + 1
  1083. If AHKsock_RegisterAsyncSelect(sktConnect, 16, "AHKsock_Connect", iMessage) {
  1084. sErrorLevel := ErrorLevel
  1085. DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult)
  1086. DllCall("Ws2_32\closesocket", "Ptr", sktConnect)
  1087. bProcessing := False
  1088. ErrorLevel := sErrorLevel
  1089. If (sFunction = iMessage) { ;Check if we were called asynchronously
  1090. AHKsock_RaiseError(4) ;The WSAAsyncSelect() call failed. The error is in ErrorLevel.
  1091.  
  1092. ;Call the function to signal that connection failed
  1093. If IsFunc(sCurFunction)
  1094. %sCurFunction%("CONNECTED", -1)
  1095. }
  1096. Return 7 ;The WSAAsyncSelect() call failed. The error is in ErrorLevel.
  1097. }
  1098.  
  1099. ;Connect to server (the connect() call also implicitly binds the socket to any host address and any port)
  1100. iResult := DllCall("Ws2_32\connect", "Ptr", sktConnect, "Ptr", NumGet(iPointer+0, 16 + 2 * A_PtrSize), "Int", NumGet(iPointer+0, 16))
  1101. If ErrorLevel Or ((iResult = -1) And (AHKsock_LastError() != 10035)) { ;Check for any error other than WSAEWOULDBLOCK
  1102. sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  1103. DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult)
  1104. DllCall("Ws2_32\closesocket", "Ptr", sktConnect)
  1105. bProcessing := False
  1106. ErrorLevel := sErrorLevel
  1107. If (sFunction = iMessage) { ;Check if we were called asynchronously
  1108. AHKsock_RaiseError(5) ;The connect() call failed. The error is in ErrorLevel.
  1109.  
  1110. ;Call the function to signal that connection failed
  1111. If IsFunc(sCurFunction)
  1112. %sCurFunction%("CONNECTED", -1)
  1113. }
  1114. Return 8 ;The connect() call failed. The error is in ErrorLevel.
  1115. }
  1116. }
  1117.  
  1118. AHKsock_Send(iSocket, ptrData = 0, iLength = 0) {
  1119.  
  1120. ;Make sure the socket is on record. Fail-safe
  1121. If Not AHKsock_Sockets("Index", iSocket)
  1122. Return -4 ;The socket specified in iSocket is not a recognized socket.
  1123.  
  1124. ;Make sure Winsock has been started up
  1125. If Not AHKsock_Startup(1)
  1126. Return -1 ;WSAStartup hasn't been called yet.
  1127.  
  1128. ;Make sure the socket is cleared for sending
  1129. If Not AHKsock_Sockets("GetSend", iSocket)
  1130. Return -5 ;The socket specified in iSocket is not cleared for sending.
  1131.  
  1132. /*! Uncomment this block to simulate the possibility of an incomplete send()
  1133. Random, iRand, 1, 100
  1134. If (iRand <= 30) { ;Probability of failure of 30%
  1135. Random, iRand, 1, iLength - 1 ;Randomize how much of the data will not be sent
  1136. iLength -= iRand
  1137. }
  1138. */
  1139.  
  1140. iSendResult := DllCall("Ws2_32\send", "Ptr", iSocket, "Ptr", ptrData, "Int", iLength, "Int", 0)
  1141. If (iSendResult = -1) And ((iErr := AHKsock_LastError()) = 10035) { ;Check specifically for WSAEWOULDBLOCK
  1142. AHKsock_Sockets("SetSend", iSocket, False) ;Update socket's send status
  1143. Return -2 ;Calling send() would have blocked the thread. Try again once you get the proper update.
  1144. } Else If (iSendResult = -1) Or ErrorLevel {
  1145. ErrorLevel := ErrorLevel ? ErrorLevel : iErr
  1146. Return -3 ;The send() call failed. The error is in ErrorLevel.
  1147. } Else Return iSendResult ;The send() operation was successful
  1148. }
  1149.  
  1150. AHKsock_ForceSend(iSocket, ptrData, iLength) {
  1151.  
  1152. ;Make sure Winsock has been started up
  1153. If Not AHKsock_Startup(1)
  1154. Return -1 ;WSAStartup hasn't been called yet
  1155.  
  1156. ;Make sure the socket is on record. Fail-safe
  1157. If Not AHKsock_Sockets("Index", iSocket)
  1158. Return -4
  1159.  
  1160. ;Make sure that we're not in Critical, or we won't be able to wait for FD_WRITE messages
  1161. If A_IsCritical
  1162. Return -5
  1163.  
  1164. ;Extra precaution to make sure FD_WRITE messages can make it
  1165. Thread, Priority, 0
  1166.  
  1167. ;We need to make sure not to fill up the send buffer in one call, or we'll get a performance hit.
  1168. ;http://support.microsoft.com/kb/823764
  1169.  
  1170. ;Get the socket's send buffer size
  1171. If ((iMaxChunk := AHKsock_SockOpt(iSocket, "SO_SNDBUF")) = -1)
  1172. Return -6
  1173.  
  1174. ;Check if we'll be sending in chunks or not
  1175. If (iMaxChunk <= 1) {
  1176.  
  1177. ;We'll be sending as much as possible everytime!
  1178.  
  1179. Loop { ;Keep sending the data until we're done or until an error occurs
  1180.  
  1181. ;Wait until we can send data (ie. when FD_WRITE arrives)
  1182. While Not AHKsock_Sockets("GetSend", iSocket)
  1183. Sleep -1
  1184.  
  1185. Loop { ;Keep sending the data until we get WSAEWOULDBLOCK or until an error occurs
  1186. If ((iSendResult := AHKsock_Send(iSocket, ptrData, iLength)) < 0) {
  1187. If (iSendResult = -2) ;Check specifically for WSAEWOULDBLOCK
  1188. Break ;Calling send() would have blocked the thread. Break the loop and we'll try again after we
  1189. ;receive FD_WRITE
  1190. Else Return iSendResult ;Something bad happened with AHKsock_Send. Return the same value we got.
  1191. } Else {
  1192.  
  1193. ;AHKsock_Send was able to send bytes. Let's check if it sent only part of what we requested
  1194. If (iSendResult < iLength) ;Move the offset up by what we were able to send
  1195. ptrData += iSendResult, iLength -= iSendResult
  1196. Else Return ;We're done sending all the data
  1197. }
  1198. }
  1199. }
  1200. } Else {
  1201.  
  1202. ;We'll be sending in chunks of just under the send buffer size to avoid the performance hit
  1203.  
  1204. iMaxChunk -= 1 ;Reduce by 1 to be smaller than the send buffer
  1205. Loop { ;Keep sending the data until we're done or until an error occurs
  1206.  
  1207. ;Wait until we can send data (ie. when FD_WRITE arrives)
  1208. While Not AHKsock_Sockets("GetSend", iSocket)
  1209. Sleep -1
  1210.  
  1211. ;Check if we have less than the max chunk to send
  1212. If (iLength < iMaxChunk) {
  1213.  
  1214. Loop { ;Keep sending the data until we get WSAEWOULDBLOCK or until an error occurs
  1215. ;Send using the traditional offset method
  1216. If ((iSendResult := AHKsock_Send(iSocket, ptrData, iLength)) < 0) {
  1217. If (iSendResult = -2) ;Check specifically for WSAEWOULDBLOCK
  1218. Break ;Calling send() would have blocked the thread. Break the loop and we'll try again after we
  1219. ;receive FD_WRITE
  1220. Else Return iSendResult ;Something bad happened with AHKsock_Send. Return the same value we got.
  1221. } Else {
  1222.  
  1223. ;AHKsock_Send was able to send bytes. Let's check if it sent only part of what we requested
  1224. If (iSendResult < iLength) ;Move the offset up by what we were able to send
  1225. ptrData += iSendResult, iLength -= iSendResult
  1226. Else Return ;We're done sending all the data
  1227. }
  1228. }
  1229. } Else {
  1230.  
  1231. ;Send up to max chunk
  1232. If ((iSendResult := AHKsock_Send(iSocket, ptrData, iMaxChunk)) < 0) {
  1233. If (iSendResult = -2) ;Check specifically for WSAEWOULDBLOCK
  1234. Continue ;Calling send() would have blocked the thread. Continue the loop and we'll try again after
  1235. ;we receive FD_WRITE
  1236. Else Return iSendResult ;Something bad happened with AHKsock_Send. Return the same value we got.
  1237. } Else ptrData += iSendResult, iLength -= iSendResult ;Move up offset by updating the pointer and length
  1238. }
  1239. }
  1240. }
  1241. }
  1242.  
  1243. AHKsock_Close(iSocket = -1, iTimeout = 5000) {
  1244.  
  1245. ;Make sure Winsock has been started up
  1246. If Not AHKsock_Startup(1)
  1247. Return ;There's nothing to close
  1248.  
  1249. If (iSocket = -1) { ;We need to close all the sockets
  1250.  
  1251. ;Check if we even have sockets to close
  1252. If Not AHKsock_Sockets() {
  1253. DllCall("Ws2_32\WSACleanup")
  1254. AHKsock_Startup(2) ;Reset the value to show that we've turned off Winsock
  1255. Return ;We're done!
  1256. }
  1257.  
  1258. ;Take the current time (needed for time-outing)
  1259. iStartClose := A_TickCount
  1260.  
  1261. Loop % AHKsock_Sockets() ;Close all sockets and cleanup
  1262. AHKsock_ShutdownSocket(AHKsock_Sockets("GetSocketFromIndex", A_Index))
  1263.  
  1264. ;Check if we're in the OnExit subroutine
  1265. If Not A_ExitReason {
  1266.  
  1267. A_IsCriticalOld := A_IsCritical
  1268.  
  1269. ;Make sure we can still receive FD_CLOSE msgs
  1270. Critical, Off
  1271. Thread, Priority, 0
  1272.  
  1273. ;We can try a graceful shutdown or wait for a timeout
  1274. While (AHKsock_Sockets()) And (A_TickCount - iStartClose < iTimeout)
  1275. Sleep, -1
  1276.  
  1277. ;Restore previous Critical
  1278. Critical, %A_IsCriticalOld%
  1279. }
  1280.  
  1281. /*! Used for debugging purposes only
  1282. If (i := AHKsock_Sockets()) {
  1283. If (i = 1)
  1284. OutputDebug, % "Cleaning up now, with the socket " AHKsock_Sockets("GetSocketFromIndex", 1) " remaining..."
  1285. Else {
  1286. OutputDebug, % "Cleaning up now, with the following sockets remaining:"
  1287. Loop % AHKsock_Sockets() {
  1288. OutputDebug, % AHKsock_Sockets("GetSocketFromIndex", A_Index)
  1289. }
  1290. }
  1291. }
  1292. */
  1293.  
  1294. DllCall("Ws2_32\WSACleanup")
  1295. AHKsock_Startup(2) ;Reset the value to show that we've turned off Winsock
  1296.  
  1297. ;Close only one socket
  1298. } Else If AHKsock_ShutdownSocket(iSocket) ;Error-checking
  1299. Return 1 ;The shutdown() call failed. The error is in ErrorLevel.
  1300. }
  1301.  
  1302. AHKsock_GetAddrInfo(sHostName, ByRef sIPList, bOne = False) {
  1303.  
  1304. ;Make sure Winsock has been started up
  1305. If (i := AHKsock_Startup())
  1306. Return i ;Return the same error (error 1 and 2)
  1307.  
  1308. ;Resolve the address and port
  1309. VarSetCapacity(aiHints, 16 + 4 * A_PtrSize, 0)
  1310. NumPut(2, aiHints, 4, "Int") ;ai_family = AF_INET
  1311. NumPut(1, aiHints, 8, "Int") ;ai_socktype = SOCK_STREAM
  1312. NumPut(6, aiHints, 12, "Int") ;ai_protocol = IPPROTO_TCP
  1313. iResult := DllCall("Ws2_32\GetAddrInfo", "Ptr", &sHostName, "Ptr", 0, "Ptr", &aiHints, "Ptr*", aiResult)
  1314. If (iResult = 11001) ;Check specifically for WSAHOST_NOT_FOUND since it's the most common error
  1315. Return 3 ;Received WSAHOST_NOT_FOUND. No such host is known.
  1316. Else If (iResult != 0) Or ErrorLevel { ;Check for any other error
  1317. ErrorLevel := ErrorLevel ? ErrorLevel : iResult
  1318. Return 4 ;The getaddrinfo() call failed. The error is in ErrorLevel.
  1319. }
  1320.  
  1321. If bOne
  1322. sIPList := DllCall("Ws2_32\inet_ntoa", "UInt", NumGet(NumGet(aiResult+0, 16 + 2 * A_PtrSize)+4, 0, "UInt"), "AStr")
  1323. Else {
  1324.  
  1325. ;Start with the first addrinfo struct
  1326. iPointer := aiResult, sIPList := ""
  1327. While iPointer {
  1328. s := DllCall("Ws2_32\inet_ntoa", "UInt", NumGet(NumGet(iPointer+0, 16 + 2 * A_PtrSize)+4, 0, "UInt"), "AStr")
  1329. iPointer := NumGet(iPointer+0, 16 + 3 * A_PtrSize) ;Go to the next addrinfo struct
  1330. sIPList .= s (iPointer ? "`n" : "") ;Add newline only if it's not the last one
  1331. }
  1332. }
  1333.  
  1334. ;We're done
  1335. DllCall("Ws2_32\FreeAddrInfo", "Ptr", aiResult)
  1336. }
  1337.  
  1338. AHKsock_GetNameInfo(sIP, ByRef sHostName, sPort = 0, ByRef sService = "") {
  1339.  
  1340. ;Make sure Winsock has been started up
  1341. If (i := AHKsock_Startup())
  1342. Return i ;Return the same error (error 1 and 2)
  1343.  
  1344. ;Translate to IN_ADDR
  1345. iIP := DllCall("Ws2_32\inet_addr", "AStr", sIP, "UInt")
  1346. If (iIP = 0 Or iIP = 0xFFFFFFFF) ;Check for INADDR_NONE or INADDR_ANY
  1347. Return 3 ;The IP address supplied in sIP is invalid.
  1348.  
  1349. ;Construct a sockaddr struct
  1350. VarSetCapacity(tSockAddr, 16, 0)
  1351. NumPut(2, tSockAddr, 0, "Short") ;ai_family = AF_INET
  1352. NumPut(iIP, tSockAddr, 4, "UInt") ;Put in the IN_ADDR
  1353.  
  1354. ;Fill in the port field if we're also looking up the service name
  1355. If sPort ;Translate to network byte order
  1356. NumPut(DllCall("Ws2_32\htons", "UShort", sPort, "UShort"), tSockAddr, 2, "UShort")
  1357.  
  1358. ;Prep vars
  1359. VarSetCapacity(sHostName, 1025 * 2, 0) ;NI_MAXHOST
  1360. If sPort
  1361. VarSetCapacity(sService, 32 * 2, 0) ;NI_MAXSERV
  1362.  
  1363. iResult := DllCall("Ws2_32\GetNameInfoW", "Ptr", &tSockAddr, "Int", 16, "Str", sHostName, "UInt", 1025 * 2
  1364. , sPort ? "Str" : "UInt", sPort ? sService : 0, "UInt", 32 * 2, "Int", 0)
  1365. If (iResult != 0) Or ErrorLevel {
  1366. ErrorLevel := ErrorLevel ? ErrorLevel : DllCall("Ws2_32\WSAGetLastError")
  1367. Return 4 ;The getnameinfo() call failed. The error is in ErrorLevel.
  1368. }
  1369. }
  1370.  
  1371. AHKsock_SockOpt(iSocket, sOption, iValue = -1) {
  1372.  
  1373. ;Prep variable
  1374. VarSetCapacity(iOptVal, iOptValLength := 4, 0)
  1375. If (iValue <> -1)
  1376. NumPut(iValue, iOptVal, 0, "UInt")
  1377.  
  1378. If (sOption = "SO_KEEPALIVE") {
  1379. intLevel := 0xFFFF ;SOL_SOCKET
  1380. intOptName := 0x0008 ;SO_KEEPALIVE
  1381. } Else If (sOption = "SO_SNDBUF") {
  1382. intLevel := 0xFFFF ;SOL_SOCKET
  1383. intOptName := 0x1001 ;SO_SNDBUF
  1384. } Else If (sOption = "SO_RCVBUF") {
  1385. intLevel := 0xFFFF ;SOL_SOCKET
  1386. intOptName := 0x1002 ;SO_SNDBUF
  1387. } Else If (sOption = "TCP_NODELAY") {
  1388. intLevel := 6 ;IPPROTO_TCP
  1389. intOptName := 0x0001 ;TCP_NODELAY
  1390. }
  1391.  
  1392. ;Check if we're getting or setting
  1393. If (iValue = -1) {
  1394. iResult := DllCall("Ws2_32\getsockopt", "Ptr", iSocket, "Int", intLevel, "Int", intOptName
  1395. , "UInt*", iOptVal, "Int*", iOptValLength)
  1396. If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR
  1397. ErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  1398. Return -1
  1399. } Else Return iOptVal
  1400. } Else {
  1401. iResult := DllCall("Ws2_32\setsockopt", "Ptr", iSocket, "Int", intLevel, "Int", intOptName
  1402. , "Ptr", &iOptVal, "Int", iOptValLength)
  1403. If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR
  1404. ErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  1405. Return -2
  1406. }
  1407. }
  1408. }
  1409.  
  1410. /*******************\
  1411. Support functions |
  1412. */
  1413.  
  1414. AHKsock_Startup(iMode = 0) {
  1415. Static bAlreadyStarted
  1416.  
  1417. /*
  1418. iMode = 0 ;Turns on WSAStartup()
  1419. iMode = 1 ;Returns whether or not WSAStartup has been called
  1420. iMode = 2 ;Resets the static variable to force another call next time iMode = 0
  1421. */
  1422.  
  1423. If (iMode = 2)
  1424. bAlreadyStarted := False
  1425. Else If (iMode = 1)
  1426. Return bAlreadyStarted
  1427. Else If Not bAlreadyStarted { ;iMode = 0. Call the function only if it hasn't already been called.
  1428.  
  1429. ;Start it up - request version 2.2
  1430. VarSetCapacity(wsaData, A_PtrSize = 4 ? 400 : 408, 0)
  1431. iResult := DllCall("Ws2_32\WSAStartup", "UShort", 0x0202, "Ptr", &wsaData)
  1432. If (iResult != 0) Or ErrorLevel {
  1433. ErrorLevel := ErrorLevel ? ErrorLevel : iResult
  1434. Return 1
  1435. }
  1436.  
  1437. ;Make sure the Winsock DLL supports at least version 2.2
  1438. If (NumGet(wsaData, 2, "UShort") < 0x0202) {
  1439. DllCall("Ws2_32\WSACleanup") ;Abort
  1440. ErrorLevel := "The Winsock DLL does not support version 2.2."
  1441. Return 2
  1442. }
  1443.  
  1444. bAlreadyStarted := True
  1445. }
  1446. }
  1447.  
  1448. AHKsock_ShutdownSocket(iSocket) {
  1449.  
  1450. ;Check if it's a listening socket
  1451. sName := AHKsock_Sockets("GetName", iSocket)
  1452. If (sName != A_Space) { ;It's not a listening socket. Shutdown send operations.
  1453. iResult := DllCall("Ws2_32\shutdown", "Ptr", iSocket, "Int", 1) ;SD_SEND
  1454. If (iResult = -1) Or ErrorLevel {
  1455. sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  1456. DllCall("Ws2_32\closesocket", "Ptr", iSocket)
  1457. AHKsock_Sockets("Delete", iSocket)
  1458. ErrorLevel := sErrorLevel
  1459. Return 1
  1460. }
  1461.  
  1462. ;Mark it
  1463. AHKsock_Sockets("SetShutdown", iSocket)
  1464.  
  1465. } Else {
  1466. DllCall("Ws2_32\closesocket", "Ptr", iSocket) ;It's only a listening socket
  1467. AHKsock_Sockets("Delete", iSocket) ;Remove it from the array
  1468. }
  1469. }
  1470.  
  1471. /***********************\
  1472. AsyncSelect functions |
  1473. */
  1474. ;FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE
  1475. AHKsock_RegisterAsyncSelect(iSocket, fFlags = 43, sFunction = "AHKsock_AsyncSelect", iMsg = 0) {
  1476. Static hwnd := False
  1477.  
  1478. If Not hwnd { ;Use the main AHK window
  1479. A_DetectHiddenWindowsOld := A_DetectHiddenWindows
  1480. DetectHiddenWindows, On
  1481. WinGet, hwnd, ID, % "ahk_pid " DllCall("GetCurrentProcessId") " ahk_class AutoHotkey"
  1482. DetectHiddenWindows, %A_DetectHiddenWindowsOld%
  1483. }
  1484.  
  1485. iMsg := iMsg ? iMsg : AHKsock_Settings("Message")
  1486. If (OnMessage(iMsg) <> sFunction)
  1487. OnMessage(iMsg, sFunction)
  1488.  
  1489. iResult := DllCall("Ws2_32\WSAAsyncSelect", "Ptr", iSocket, "Ptr", hwnd, "UInt", iMsg, "Int", fFlags)
  1490. If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR
  1491. ErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  1492. Return 1
  1493. }
  1494. }
  1495.  
  1496. AHKsock_AsyncSelect(wParam, lParam) {
  1497. Critical ;So that messages are buffered
  1498.  
  1499. ;wParam parameter identifies the socket on which a network event has occurred
  1500. ;The low word of lParam specifies the network event that has occurred.
  1501. ;The high word of lParam contains any error code
  1502.  
  1503. ;Make sure the socket is on record. Fail-safe
  1504. If Not AHKsock_Sockets("Index", wParam)
  1505. Return
  1506.  
  1507. iEvent := lParam & 0xFFFF, iErrorCode := lParam >> 16
  1508.  
  1509. /*! Used for debugging purposes
  1510. OutputDebug, % "AsyncSelect - A network event " iEvent " has occurred on socket " wParam
  1511. If iErrorCode
  1512. OutputDebug, % "AsyncSelect - Error code = " iErrorCode
  1513. */
  1514.  
  1515. If (iEvent = 1) { ;FD_READ
  1516.  
  1517. ;Check for error
  1518. If iErrorCode { ;WSAENETDOWN is the only possible
  1519. ErrorLevel := iErrorCode
  1520. ;FD_READ event received with an error. The error is in ErrorLevel. The socket is in iSocket.
  1521. AHKsock_RaiseError(6, wParam)
  1522. Return
  1523. }
  1524.  
  1525. VarSetCapacity(bufReceived, bufReceivedLength := AHKsock_Settings("Buffer"), 0)
  1526. iResult := DllCall("Ws2_32\recv", "UInt", wParam, "Ptr", &bufReceived, "Int", bufReceivedLength, "Int", 0)
  1527. If (iResult > 0) { ;We received data!
  1528. VarSetCapacity(bufReceived, -1) ;Update the internal length
  1529.  
  1530. ;Get associated function and call it
  1531. If IsFunc(sFunc := AHKsock_Sockets("GetFunction", wParam))
  1532. %sFunc%("RECEIVED", wParam, AHKsock_Sockets("GetName", wParam)
  1533. , AHKsock_Sockets("GetAddr", wParam)
  1534. , AHKsock_Sockets("GetPort", wParam), bufReceived, iResult)
  1535.  
  1536. ;Check for error other than WSAEWOULDBLOCK
  1537. } Else If ErrorLevel Or ((iResult = -1) And Not ((iErrorCode := AHKsock_LastError()) = 10035)) {
  1538. ErrorLevel := ErrorLevel ? ErrorLevel : iErrorCode
  1539. AHKsock_RaiseError(7, wParam) ;The recv() call failed. The error is in ErrorLevel. The socket is in iSocket.
  1540. iResult = -1 ;So that if it's a spoofed call from FD_CLOSE, we exit the loop and close the socket
  1541. }
  1542.  
  1543. ;Here, we bother with returning a value in case it's a spoofed call from FD_CLOSE
  1544. Return iResult
  1545.  
  1546. } Else If (iEvent = 2) { ;FD_WRITE
  1547.  
  1548. ;Check for error
  1549. If iErrorCode { ;WSAENETDOWN is the only possible
  1550. ErrorLevel := iErrorCode
  1551. ;FD_WRITE event received with an error. The error is in ErrorLevel. The socket is in iSocket.
  1552. AHKsock_RaiseError(8, wParam)
  1553. Return
  1554. }
  1555.  
  1556. ;Update socket's setting
  1557. AHKsock_Sockets("SetSend", wParam, True)
  1558.  
  1559. ;Make sure the socket isn't already shut down
  1560. If Not AHKsock_Sockets("GetShutdown", wParam)
  1561. If IsFunc(sFunc := AHKsock_Sockets("GetFunction", wParam))
  1562. %sFunc%("SEND", wParam, AHKsock_Sockets("GetName", wParam)
  1563. , AHKsock_Sockets("GetAddr", wParam)
  1564. , AHKsock_Sockets("GetPort", wParam))
  1565.  
  1566. } Else If (iEvent = 8) { ;FD_ACCEPT
  1567.  
  1568. ;Check for error
  1569. If iErrorCode { ;WSAENETDOWN is the only possible
  1570. ErrorLevel := iErrorCode
  1571. ;FD_ACCEPT event received with an error. The error is in ErrorLevel. The socket is in iSocket.
  1572. AHKsock_RaiseError(9, wParam)
  1573. Return
  1574. }
  1575.  
  1576. ;We need to accept the connection
  1577. VarSetCapacity(tSockAddr, tSockAddrLength := 16, 0)
  1578. sktClient := DllCall("Ws2_32\accept", "Ptr", wParam, "Ptr", &tSockAddr, "Int*", tSockAddrLength)
  1579. If (sktClient = -1) And ((iErrorCode := AHKsock_LastError()) = 10035) ;Check specifically for WSAEWOULDBLOCK
  1580. Return ;We'll be called again next time we can retry accept()
  1581. Else If (sktClient = -1) Or ErrorLevel { ;Check for INVALID_SOCKET
  1582. ErrorLevel := ErrorLevel ? ErrorLevel : iErrorCode
  1583. ;The accept() call failed. The error is in ErrorLevel. The listening socket is in iSocket.
  1584. AHKsock_RaiseError(10, wParam)
  1585. Return
  1586. }
  1587.  
  1588. ;Add to array
  1589. sName := ""
  1590. sAddr := DllCall("Ws2_32\inet_ntoa", "UInt", NumGet(tSockAddr, 4, "UInt"), "AStr")
  1591. sPort := AHKsock_Sockets("GetPort", wParam)
  1592. sFunc := AHKsock_Sockets("GetFunction", wParam)
  1593. AHKsock_Sockets("Add", sktClient, sName, sAddr, sPort, sFunc)
  1594.  
  1595. ;Go back to listening
  1596. iResult := DllCall("Ws2_32\listen", "Ptr", wParam, "Int", 0x7FFFFFFF) ;SOMAXCONN
  1597. If (iResult = -1) Or ErrorLevel { ;Check for SOCKET_ERROR
  1598. sErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
  1599. DllCall("Ws2_32\closesocket", "Ptr", wParam)
  1600. AHKsock_Sockets("Delete", wParam) ;Remove from array
  1601. ErrorLevel := sErrorLevel
  1602. ;The listen() call failed. The error is in ErrorLevel. The listening socket is in iSocket.
  1603. AHKsock_RaiseError(12, wParam)
  1604. Return
  1605. }
  1606.  
  1607. ;Get associated function and call it
  1608. If IsFunc(sFunc)
  1609. %sFunc%("ACCEPTED", sktClient, sName, sAddr, sPort)
  1610.  
  1611. } Else If (iEvent = 32) { ;FD_CLOSE
  1612.  
  1613. ;Keep receiving data before closing the socket by spoofing an FD_READ event to call recv()
  1614. While (AHKsock_AsyncSelect(wParam, 1) > 0)
  1615. Sleep, -1
  1616.  
  1617. ;Check if we initiated it
  1618. If Not AHKsock_Sockets("GetShutdown", wParam) {
  1619.  
  1620. ;Last chance to send data. Get associated function and call it.
  1621. If IsFunc(sFunc := AHKsock_Sockets("GetFunction", wParam))
  1622. %sFunc%("SENDLAST", wParam, AHKsock_Sockets("GetName", wParam)
  1623. , AHKsock_Sockets("GetAddr", wParam)
  1624. , AHKsock_Sockets("GetPort", wParam))
  1625.  
  1626. ;Shutdown the socket. This is to attempt a graceful shutdown
  1627. If AHKsock_ShutdownSocket(wParam) {
  1628. ;The shutdown() call failed. The error is in ErrorLevel. The socket is in iSocket.
  1629. AHKsock_RaiseError(13, wParam)
  1630. Return
  1631. }
  1632. }
  1633.  
  1634. ;We just have to close the socket then
  1635. DllCall("Ws2_32\closesocket", "Ptr", wParam)
  1636.  
  1637. ;Get associated data before deleting
  1638. sFunc := AHKsock_Sockets("GetFunction", wParam)
  1639. sName := AHKsock_Sockets("GetName", wParam)
  1640. sAddr := AHKsock_Sockets("GetAddr", wParam)
  1641. sPort := AHKsock_Sockets("GetPort", wParam)
  1642.  
  1643. ;We can remove it from the array
  1644. AHKsock_Sockets("Delete", wParam)
  1645.  
  1646. If IsFunc(sFunc)
  1647. %sFunc%("DISCONNECTED", wParam, sName, sAddr, sPort)
  1648. }
  1649. }
  1650.  
  1651. /******************\
  1652. Array controller |
  1653. */
  1654.  
  1655. AHKsock_Sockets(sAction = "Count", iSocket = "", sName = "", sAddr = "", sPort = "", sFunction = "") {
  1656. Static
  1657. Static aSockets0 := 0
  1658. Static iLastSocket := 0xFFFFFFFF ;Cache to lessen index lookups on the same socket
  1659. Local i, ret, A_IsCriticalOld
  1660.  
  1661. A_IsCriticalOld := A_IsCritical
  1662. Critical
  1663.  
  1664. If (sAction = "Count") {
  1665. ret := aSockets0
  1666.  
  1667. } Else If (sAction = "Add") {
  1668. aSockets0 += 1 ;Expand array
  1669. aSockets%aSockets0%_Sock := iSocket
  1670. aSockets%aSockets0%_Name := sName
  1671. aSockets%aSockets0%_Addr := sAddr
  1672. aSockets%aSockets0%_Port := sPort
  1673. aSockets%aSockets0%_Func := sFunction
  1674. aSockets%aSockets0%_Shutdown := False
  1675. aSockets%aSockets0%_Send := False
  1676.  
  1677. } Else If (sAction = "Delete") {
  1678.  
  1679. ;First we need the index
  1680. i := (iSocket = iLastSocket) ;Check cache
  1681. ? iLastSocketIndex
  1682. : AHKsock_Sockets("Index", iSocket)
  1683.  
  1684. If i {
  1685. iLastSocket := 0xFFFF ;Clear cache
  1686. If (i < aSockets0) { ;Let the last item overwrite this one
  1687. aSockets%i%_Sock := aSockets%aSockets0%_Sock
  1688. aSockets%i%_Name := aSockets%aSockets0%_Name
  1689. aSockets%i%_Addr := aSockets%aSockets0%_Addr
  1690. aSockets%i%_Port := aSockets%aSockets0%_Port
  1691. aSockets%i%_Func := aSockets%aSockets0%_Func
  1692. aSockets%i%_Shutdown := aSockets%aSockets0%_Shutdown
  1693. aSockets%i%_Send := aSockets%aSockets0%_Send
  1694.  
  1695. }
  1696. aSockets0 -= 1 ;Remove element
  1697. }
  1698.  
  1699. } Else If (sAction = "GetName") {
  1700. i := (iSocket = iLastSocket) ;Check cache
  1701. ? iLastSocketIndex
  1702. : AHKsock_Sockets("Index", iSocket)
  1703. ret := aSockets%i%_Name
  1704.  
  1705. } Else If (sAction = "GetAddr") {
  1706. i := (iSocket = iLastSocket) ;Check cache
  1707. ? iLastSocketIndex
  1708. : AHKsock_Sockets("Index", iSocket)
  1709. ret := aSockets%i%_Addr
  1710.  
  1711. } Else If (sAction = "GetPort") {
  1712. i := (iSocket = iLastSocket) ;Check cache
  1713. ? iLastSocketIndex
  1714. : AHKsock_Sockets("Index", iSocket)
  1715. ret := aSockets%i%_Port
  1716.  
  1717. } Else If (sAction = "GetFunction") {
  1718. i := (iSocket = iLastSocket) ;Check cache
  1719. ? iLastSocketIndex
  1720. : AHKsock_Sockets("Index", iSocket)
  1721. ret := aSockets%i%_Func
  1722.  
  1723. } Else If (sAction = "SetFunction") {
  1724. i := (iSocket = iLastSocket) ;Check cache
  1725. ? iLastSocketIndex
  1726. : AHKsock_Sockets("Index", iSocket)
  1727. aSockets%i%_Func := sName
  1728.  
  1729. } Else If (sAction = "GetSend") {
  1730. i := (iSocket = iLastSocket) ;Check cache
  1731. ? iLastSocketIndex
  1732. : AHKsock_Sockets("Index", iSocket)
  1733. ret := aSockets%i%_Send
  1734.  
  1735. } Else If (sAction = "SetSend") {
  1736. i := (iSocket = iLastSocket) ;Check cache
  1737. ? iLastSocketIndex
  1738. : AHKsock_Sockets("Index", iSocket)
  1739. aSockets%i%_Send := sName
  1740.  
  1741. } Else If (sAction = "GetShutdown") {
  1742. i := (iSocket = iLastSocket) ;Check cache
  1743. ? iLastSocketIndex
  1744. : AHKsock_Sockets("Index", iSocket)
  1745. ret := aSockets%i%_Shutdown
  1746.  
  1747. } Else If (sAction = "SetShutdown") {
  1748. i := (iSocket = iLastSocket) ;Check cache
  1749. ? iLastSocketIndex
  1750. : AHKsock_Sockets("Index", iSocket)
  1751. aSockets%i%_Shutdown := True
  1752.  
  1753. } Else If (sAction = "GetSocketFromNamePort") {
  1754. Loop % aSockets0 {
  1755. If (aSockets%A_Index%_Name = iSocket)
  1756. And (aSockets%A_Index%_Port = sName) {
  1757. ret := aSockets%A_Index%_Sock
  1758. Break
  1759. }
  1760. }
  1761.  
  1762. } Else If (sAction = "GetSocketFromIndex") {
  1763. ret := aSockets%iSocket%_Sock
  1764.  
  1765. } Else If (sAction = "Index") {
  1766. Loop % aSockets0 {
  1767. If (aSockets%A_Index%_Sock = iSocket) {
  1768. iLastSocketIndex := A_Index, iLastSocket := iSocket
  1769. ret := A_Index
  1770. Break
  1771. }
  1772. }
  1773. }
  1774.  
  1775. ;Restore old Critical setting
  1776. Critical %A_IsCriticalOld%
  1777. Return ret
  1778. }
  1779.  
  1780. /*****************\
  1781. Error Functions |
  1782. */
  1783.  
  1784. AHKsock_LastError() {
  1785. Return DllCall("Ws2_32\WSAGetLastError")
  1786. }
  1787.  
  1788. AHKsock_ErrorHandler(sFunction = """") {
  1789. Static sCurrentFunction
  1790. If (sFunction = """")
  1791. Return sCurrentFunction
  1792. Else sCurrentFunction := sFunction
  1793. }
  1794.  
  1795. AHKsock_RaiseError(iError, iSocket = -1) {
  1796. If IsFunc(sFunc := AHKsock_ErrorHandler())
  1797. %sFunc%(iError, iSocket)
  1798. }
  1799.  
  1800. /*******************\
  1801. Settings Function |
  1802. */
  1803.  
  1804. AHKsock_Settings(sSetting, sValue = "") {
  1805. Static iMessage := 0x8000
  1806. Static iBuffer := 65536
  1807.  
  1808. If (sSetting = "Message") {
  1809. If Not sValue
  1810. Return iMessage
  1811. Else iMessage := (sValue = "Reset") ? 0x8000 : sValue
  1812. } Else If (sSetting = "Buffer") {
  1813. If Not sValue
  1814. Return iBuffer
  1815. Else iBuffer := (sValue = "Reset") ? 65536 : sValue
  1816. }
  1817. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement