I made some progress.
Code: Select all
#Include AHKsock.ahk ;https://raw.githubusercontent.com/jleb/AHKsock/master/AHKsock.ahk
mySocket := 0 ;global tcp socket handle
;our websocket handshake request:
rawHttpRequestUnicode =
( Join`r`n
GET /?encoding=text HTTP/1.1
Origin: http://websocket.org
Host: echo.websocket.org
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: uRovscZjNol/umbTt5uKmw==
Sec-WebSocket-Version: 13
)
rawHttpRequestSize := StrPut(rawHttpRequestUnicode, "UTF-8") - 1
VarSetCapacity(rawHttpRequest, rawHttpRequestSize)
StrPut(rawHttpRequestUnicode, &rawHttpRequest, rawHttpRequestSize, "UTF-8")
AHKsock_ErrorHandler("ErrorCallback") ;for error handling
If (i := AHKsock_Connect("echo.websocket.org", 80, "TcpCallback")) ;connect to echo.websocket.org on port 80 (http) and set the function TcpCallback as our callback function
MsgBox, % "AHKsock_Connect failed with return value = " i " and error code = " ErrorLevel " at line " A_LineNumber ;error reporting
Loop ;wait for our tcp connection to be established
If (mySocket)
Break
If (i := AHKsock_ForceSend(mySocket, &rawHttpRequest, rawHttpRequestSize)) ;send our formated websocket handshake request
MsgBox, % "AHKsock_ForceSend failed with return value = " i " and error code = " ErrorLevel " at line " A_LineNumber ;error reporting
MsgBox, Let's send a frame now!
text := "Test message! Hello!" ;UTF-8 test data that we try to send as a frame
textLen := StrPut(text, "UTF-8") - 1
frameSize := 6+textLen
VarSetCapacity(textFrame,frameSize,0)
if textLen >= 126
throw "Payload size needs to be encoded differently"
fin := 0x0080 ; 1....... ........
opcode := 0x0001 ; ....0001 ........
masked := 0x8000 ; ........ 1.......
length := textLen<<8 ; ........ .xxxxxxx
NumPut(fin|opcode|masked|length, &textFrame, "ushort")
Random mask1, 0, 0xFFFF ; Had problems with 0,0xFFFFFFFF (acting like -1..0).
Random mask2, 0, 0xFFFF
mask := mask1 | (mask2 << 16)
NumPut(mask, &textFrame+2, "uint")
StrPut(text, &textFrame+6, textLen, "UTF-8")
Loop % textLen ; Apply mask to payload.
NumPut(NumGet(textFrame, 5+A_Index, "uchar") ^ NumGet(textFrame, 2+Mod(A_Index-1,4), "uchar")
, textFrame, 5+A_Index, "uchar")
If (i := AHKsock_ForceSend(mySocket, &textFrame, frameSize)) ;send our frame
MsgBox, % "AHKsock_ForceSend failed with return value = " i " and error code = " ErrorLevel " at line " A_LineNumber ;error reporting
Sleep 1000 ; Send a "close" frame:
NumPut(0xe8030288, textFrame, 0, "uint"), frameSize := 4
AHKsock_ForceSend(mySocket, &textFrame, frameSize)
TcpCallback(sEvent, iSocket = 0, sName = 0, sAddr = 0, sPort = 0, ByRef bData = 0, bDataLength = 0) { ;callback function that gets called when something happens on our tcp connection
Global mySocket
if (bDataLength = 4 && NumGet(bData, "uint") = 0xe8030288) {
MsgBox Connection closing gracefully.
AHKsock_Close(iSocket)
ExitApp
}
MsgBox, % "sEvent: " sEvent "`niSocket: " iSocket "`nsName: " sName "`nsAddr: " sAddr "`nsPort: " sPort "`nbData: " bData "`nbDataLength: " bDataLength
. "`nsData: " StrGet(&bData, bDataLength, "UTF-8")
If (sEvent = "CONNECTED") { ;a tcp connection was successfully established
mySocket := iSocket ;save the connection handle in the global variable mySocket
}
if (sEvent = "DISCONNECTED")
ExitApp
}
ErrorCallback(iError, iSocket) { ;for error handling
MsgBox, % "iError: " iError "`niSocket: " iSocket ;for error handling
} ;for error handling
The frame is sent and echoed back, but the received frame isn't decoded yet.
My (main?) mistake was with bit order: bit 0 in the spec is actually the most significant bit of the first byte (0x80 or 0b10000000).
Also note that numbers larger than a byte within the WebSocket protocol are big-endian, but x86 is little-endian. In my script, the integer
0xe8030288 produces the bytes
88 02 03 E8, which represent a close frame (8: fin, 8: opcode, 02: payload length, 03 E8: payload). The number encoded in the payload is
0x03e8 (1000), not
0xe803.
Your script (and my previous copy) uses the sequence "StrLen, VarSetCapacity, StrPut". This is wrong. StrLen tells you the length in the
current encoding, which can be different to the number of characters which StrPut will write. I've corrected this in the script above.