Websocket how to send and receive data?

The client is a standard web-browser with the JavaScript websocket implementation, this is my code:

//File: Test.js
//This file contains all the javascript functions required by the client.
//History:
//2024/11/20 Created by Simon Platten
const cintCheckWebSocketInterval        = 1000;
const cintReconnectTime                 = 5;
const cintRetryInitInterval             = 2000;
const cintServiceNoResponses            = 5;
const cintServiceRequestInterval        = 1000;
const cintServiceRequestAltInterval     = 10000;
const cstrServerIP                      = "{SRVR_ADR}";
const cstrServerSocketIP                = "{SRVR_SCKT_ADR}";
const cstrRequestPrefix                 = "/?";
const cstrRequestType                   = "GET";
const cstrSendMsg                       = "msg=*US";
const cstrServiceInfoTagRequests        = "requests";
const cstrServiceInfoTagResponses       = "responses";
const cobjWebSocketErrors = {1000:"Normal Closure"
                            ,1001:"Going Away"
                            ,1002:"Protocol Error"
                            ,1003:"Unsupported Data"
                            ,1004:"---Reserved---"
                            ,1005:"No Status Rcvd"
                            ,1006:"Abnormal Closure"
                            ,1007:"Invalid frame payload data"
                            ,1008:"Policy Violation"
                            ,1009:"Message Too Big"
                            ,1010:"Mandatory Ext."
                            ,1011:"Internal Server Error"
                            ,1015:"TLS handshake"};
var blnSocketClosed = false, intNotConnected, intServiceInterval = 0
  , objServiceInfo = {}, tmrCheckWebSocket, tmrInit, tmrService
  , webSocket = null;
//Initialisation function
function TestInit() {    
    if (cstrServerSocketIP[0] == '{') {
    //Server hasn't replace this macro yet....install a timer to retry this
        tmrInit = setInterval(TestInit, cintRetryInitInterval);
        return;
    }
    if (tmrInit != null && typeof tmrInit !== "undefined") {
        clearInterval(tmrInit);
        tmrInit = null;
    }
    if (typeof tmrService !== "undefined") {
        return;
    }
    console.log("Attempting connect to: " + cstrServerSocketIP);
    //Create web-socket and set it up
    CreateWebSocket();
    //Initialise service info object
    objServiceInfo[cstrServiceInfoTagRequests] = 0;
    objServiceInfo[cstrServiceInfoTagResponses] = 0;
}
function Connected() {
    return (typeof webSocket == "object"
         && typeof webSocket.readyState == "number"
                && webSocket.readyState == WebSocket.OPEN);
}
function CreateWebSocket() {
    try {        
        intNotConnected = 0;
        console.log(cstrServerSocketIP);
        webSocket = new WebSocket(cstrServerSocketIP);
        webSocket.binaryType = "arraybuffer";
        if (tmrCheckWebSocket != undefined) {
            clearInterval(tmrCheckWebSocket);
        }
    //Setup timeout timer to timeout if webSocket does not connect
        tmrCheckWebSocket = setInterval(function() {
            var strMsg = "Checking (" + cstrServerSocketIP + "), ";
            if (typeof webSocket == "object"
             && typeof webSocket.readyState == "number") {
                if (webSocket.readyState == WebSocket.CONNECTING) {
                    strMsg += "connecting";
                } else if (webSocket.readyState == WebSocket.OPEN) {
                    strMsg += "open (connected)";
                } else if (webSocket.readyState == WebSocket.CLOSING) {
                    strMsg += "closing";
                } else if (webSocket.readyState == WebSocket.CLOSED) {
                    strMsg += "closed";
                }
                if (WebSocket.readyState != WebSocket.OPEN) {
                    intNotConnected++;    
                } else {
                    intNotConnected = 0;
                }
            } else {
                strMsg += "not a valid connection!";
            }            
            DebugMsg(strMsg);
            if (webSocket.readyState != WebSocket.OPEN 
             && intNotConnected >= cintReconnectTime) {
                CreateWebSocket();
            }                                
        }, cintCheckWebSocketInterval);
        webSocket.onclose = (event) => {
            DebugMsg("onclose");
            if (typeof event == "object") {
                var strReason = "";    
                if (typeof event.code == "number") {
                    strReason += "code[" + event.code + "]";
                    var strError = cobjWebSocketErrors[event.code];
                    if (typeof strError == "string") {
                        strReason += ":" + strError;
                    }
                }
                if (typeof event.reason == "string" && event.reason.length > 0) {
                    if (strReason.length > 0) {
                        strReason += ", ";    
                    }
                    strReason += "Reason:\"" + event.reason + "\"";
                }
                DebugMsg(strReason);
            }
        //Try again...
            CreateWebSocket();
        };    
        webSocket.onerror = (event) => {
            DebugMsg("onerror" 
                + ((typeof event == "object"
                    && typeof event.data != "undefined") ? ":" + String(event.data) : ""));
           };
        webSocket.onmessage = (event) => { 
            DebugMsg("onmessage");
            webSocket.close();
        };
        webSocket.onopen = () => {
            DebugMsg("onopen");
            sendMsg();
        };
        //Set-up timer to send requests for data updates
        InstallServiceTimer(cintServiceRequestInterval);
    } catch (e) {
        DebugMsg(e);
    }
}
function DebugMsg(msg) {
    var msgdump = document.getElementById("msgdump");
    if (msgdump == null || typeof msgdump === "undefined") {
        return;
    }
    var strContent;    
    if (typeof msg == "string" && msg.length > 0) {
        strContent = msg;
    }
    if (typeof msg == "object" && typeof msg.message == "string") {
        strContent = msg.message;
    }
    if (typeof strContent == "string" && strContent.length > 0) {
        strContent = (new Date()).toUTCString() + ", " + strContent; 
        msgdump.innerHTML = strContent + "\r\n" + msgdump.innerHTML;
    }
}
function InstallServiceTimer(intInterval) {
    if (intServiceInterval != intInterval) {
        tmrService = setInterval(ServiceRequests, intInterval);
        if (tmrService != null && typeof tmrService == "number") {       
            intServiceInterval = intInterval;
        }
    }
    if (!(tmrService == null || typeof tmrService === "undefined")) {
        DebugMsg("Set-up, interval: " + intInterval + "ms");
    }
}
function sendMsg() {
    if (Connected() != true) {
        return;
    }
    webSocket.send("Hello World");
}
//Function called when service request timer expires, no arguments
function ServiceRequests() { 
    if (Connected() != true) {
        return;
    }
    objServiceInfo[cstrServiceInfoTagRequests]++;
    var intServiceReqRespDiff = objServiceInfo[cstrServiceInfoTagRequests] 
                                - objServiceInfo[cstrServiceInfoTagResponses];
    if (intServiceReqRespDiff >= cintServiceNoResponses
        && intServiceInterval != cintServiceRequestAltInterval) {
    //Stop the existing timer
        clearInterval(tmrService);
    //Reset request and response counters
        objServiceInfo[cstrServiceInfoTagRequests] = 
        objServiceInfo[cstrServiceInfoTagResponses] = 0;
    //Create a new timer with the slower interval
        InstallServiceTimer(cintServiceRequestAltInterval);
        return;
    }
    DebugMsg("Sending: \"" + cstrSendMsg + "\" to " + cstrServerSocketIP);
    webSocket.send(cstrSendMsg);
}

I have developed a C# application which has an HTTP web-server implementation and also a web-socket server. The web-socket server is based on the TcpClient and TcpListener classes and listens for traffic on port 8080:

                try {
                    mWebSocketServer = new TcpListener(ipWebSocketAddr, mcintWebSocketPort);
                    mWebSocketServer.Start();
                    mWebSocketThread = new Thread(() => WebSocketServer());
                    mWebSocketThread.Start();
                } catch (Exception ex) {
                    FrmMain.UpdateTaskbar(ex.ToString());
                }

WebSocketHandshake and WebSocketServer:

        /// <summary>
        /// Function <c>WebSocketHandshake</c>
        /// </summary>
        /// <param name="arybytRX"></param>
        /// <param name="stream"></param>
        private void WebSocketHandshake(byte[] arybytRX, NetworkStream stream) {
            int intRxLength = arybytRX.Length, intTxLength = 1;
            byte[] arybytPacket = null;
            for (int intPass = 1; intPass <= 2; intPass++) {
                if (intPass == 2) {
                    arybytPacket = new byte[intTxLength + intRxLength];
                    arybytPacket[0] = mcintWebSocketDataID0;
                }
                if (intRxLength <= mcintWebSocketLength1) {
                    if (intPass == 1) {
                        intTxLength++;
                    } else {
                        arybytPacket[1] = (byte)intRxLength;
                    }
                } else if (intRxLength >= 0x7e && intRxLength < 0xffff) {
                    if (intPass == 1) {
                        intTxLength += 3;
                    } else {
                        arybytPacket[1] = mcintWebSocketLength2;
                        arybytPacket[2] = (byte)((intRxLength >> 0x08) & 0xff);//Hi-byte
                        arybytPacket[3] = (byte)(intRxLength & 0xff);          //Lo-byte
                    }
                } else {
                    if (intPass == 1) {
                        intTxLength += 9;
                    } else {
                        arybytPacket[1] = mcintWebSocketLength3;
                        arybytPacket[2] = (byte)((intRxLength >> 0x38) & 0xff);//56
                        arybytPacket[3] = (byte)((intRxLength >> 0x30) & 0xff);//48
                        arybytPacket[4] = (byte)((intRxLength >> 0x28) & 0xff);//40
                        arybytPacket[5] = (byte)((intRxLength >> 0x20) & 0xff);//32
                        arybytPacket[6] = (byte)((intRxLength >> 0x18) & 0xff);//24
                        arybytPacket[7] = (byte)((intRxLength >> 0x10) & 0xff);//16
                        arybytPacket[8] = (byte)((intRxLength >> 0x08) & 0xff);//8
                        arybytPacket[9] = (byte)(intRxLength & 0xff);          //0
                    }
                }
                if (intPass == 2) {
                    for (int i = 0; i < intRxLength; i++) {
                        arybytPacket[intTxLength++] = arybytRX[i];
                    }
                }
            }
            if (arybytPacket[0] != mcintWebSocketDataID0) {
                Console.WriteLine(FrmMain.strTimeStampMsg("Cannot interpret data, mal-formed packet!"));
                return;
            }
            int intDataLength, intDataOffset;
            if (arybytPacket[1] == mcintWebSocketLength2) {
                //Data length is between 126 and 65535 bytes
                intDataLength = (int)arybytPacket[2] << 0x08;       //Hi-byte
                intDataLength += (int)arybytPacket[3];              //Lo-byte
                intDataOffset = 4;
            } else if (arybytPacket[1] == mcintWebSocketLength3) {
                //Data length is > 65535 bytes
                intDataLength = (int)arybytPacket[2] << 0x38;
                intDataLength += (int)arybytPacket[3] << 0x30;
                intDataLength += (int)arybytPacket[4] << 0x28;
                intDataLength += (int)arybytPacket[5] << 0x20;
                intDataLength += (int)arybytPacket[6] << 0x18;
                intDataLength += (int)arybytPacket[7] << 0x10;
                intDataLength += (int)arybytPacket[8] << 0x08;
                intDataLength += (int)arybytPacket[9];
                intDataOffset = 10;
            } else {
                //Data length must be <= 125 bytes
                intDataLength = (int)arybytPacket[1];
                intDataOffset = 2;
            }
            string strRX = Encoding.ASCII.GetString(arybytPacket, intDataOffset
                                                         , intTxLength - intDataOffset);
            string[] arystrRX = strRX.Split('\n');
            Console.WriteLine(string.Format("Received: [{0}]", strRX));//Just log to console whats received
            string strWebSocketAccept = null;
            bool blnWebsocket = false;
            foreach (string strLine in arystrRX) {
                int intColon = strLine.IndexOf(":");
                if (intColon == -1) {
                    //Shouldn't get here!
                    continue;
                }
                string strValue = strLine.Substring(intColon + 1).Trim();
                if (strValue.Length == 0) {
                    continue;
                }
                string strParam = strLine.Substring(0, intColon).Trim();
                if (strParam.CompareTo(mcstrHdrUpgrade) == 0) {
                    if (strValue.CompareTo(mcstrWebsocket) == 0) {
                        blnWebsocket = true;
                    }
                } else if (strParam.CompareTo(mcstrHdrWebSocketKey) == 0) {
                    string strKey = strValue + mcstrWebSocketMagicString;
                    byte[] arybytKey = Encoding.UTF8.GetBytes(strKey);
                    string strSHA1 = Encoding.ASCII.GetString(arybytKey, 0, arybytKey.Length);
                    byte[] arybytAccept = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(strSHA1));
                    strWebSocketAccept = Convert.ToBase64String(arybytAccept);
                    break;
                } else if (strParam.CompareTo(mcstrHdrWebSocketVersion) == 0) {
                    mintWebSocketVersion = 0;
                    int.TryParse(strValue, out mintWebSocketVersion);
                    if (mintWebSocketVersion < mcintMinWebSocketVersion) {
                        throw new Exception(string.Format("WebSocket ({0}) version not supported!"
                            , mintWebSocketVersion));
                    }
                }
            }
            if (blnWebsocket != true) {
                return;
            }
            Console.WriteLine(FrmMain.strTimeStampMsg("Received from web-client:"));
            Console.WriteLine(strRX);
            if (strWebSocketAccept != null) {
                string strResponse = "HTTP/1.1 101 Switching Protocols"
                                    + CSTR.CRLF + "Upgrade: " + mcstrWebsocket
                                    + CSTR.CRLF + "Connection: Upgrade"
                                    + CSTR.CRLF + mcstrHdrWebSocketAccept + ": "
                                                + strWebSocketAccept
                                    + CSTR.CRLF + CSTR.CRLF;
                Console.WriteLine("Response:");
                Console.WriteLine(strResponse);
                byte[] arybytResponse = Encoding.UTF8.GetBytes(strResponse);
                stream.Write(arybytResponse, 0, arybytResponse.Length);
                FrmMain.sobjThis.UpdateClientStats(eResponse: eCounterState.INCREMENT);
            }
        }
        /// <summary>
        /// Function <c>WebSocketServer</c>
        /// </summary>
        public void WebSocketServer() {
            Byte[] arybytData = new byte[mcintWebsocketReceiveBufferSize];
            while (mblnRunTillStopped == true) {
                try {
                    TcpClient tcpClient = mWebSocketServer.AcceptTcpClient();
                    if (tcpClient.Connected != true) {
                        continue;
                    }
                    tcpClient.ReceiveTimeout = mcintReceiveTimeout;
                    IPEndPoint ipEndPoint = tcpClient.Client.RemoteEndPoint as IPEndPoint;
                    IPAddress ipClient = ipEndPoint.Address;
                    FrmMain.sobjThis.AddSubscriber(ipClient);
                    Console.WriteLine(FrmMain.strTimeStampMsg("WebSocketServer(): client connected"));
                    NetworkStream stream = tcpClient.GetStream();
                    while (true) {
                        Thread.Sleep(mcintWebSocketServerThreadSleep);
                        if (mblnRunTillStopped != true) {
                            break;
                        }
                        int intRxLength = stream.Read(arybytData, 0, tcpClient.Available);
                        if (intRxLength == 0) {
                            break;
                        }
                        string strRX = Encoding.ASCII.GetString(arybytData);
                        FrmMain.sobjThis.UpdateClientStats(eRequest: eCounterState.INCREMENT);
                        if (Regex.IsMatch(strRX, "^GET") == true) {
                            WebSocketHandshake(arybytData, stream);
                        //First byte should be '129'
                        } else if (arybytData[0] == 0x81) {
                        //Is data masked?
                            bool blnFinished = ((arybytData[0] & 0x01) == 0x01);
                            bool blnMsgRecvd = ((arybytData[0] & 0x80) == 0x80);
                            byte bytMask = (byte)(arybytData[1] & 0x01);
                            ulong ulngMsgLength = (ulong)(arybytData[1] & 0x7f)
                                 ,ulngOffset = mcintWebSocketDataStartIdx;
                            if (ulngMsgLength == 0x7e) {
                                //Bytes are reverseved, websocket will print them in big-endian
                                ulngMsgLength = BitConverter.ToUInt16(new byte[] {
                                        arybytData[3], arybytData[2]}, 0);
                                ulngOffset += 2;
                            } else if (ulngMsgLength == 0x7f) {
                                ulngMsgLength = BitConverter.ToUInt64(new byte[] {
                                        arybytData[9], arybytData[8], arybytData[7]
                                       ,arybytData[6], arybytData[5], arybytData[4]
                                       ,arybytData[3], arybytData[2] }, 0);
                                ulngOffset += 8;
                            }
                            if (ulngMsgLength > 0 && bytMask > 0) {
                                byte[] arybytDecoded = new byte[ulngMsgLength];
                                byte[] arybytMasks = new byte[4] {arybytData[ulngOffset++]
                                                                 ,arybytData[ulngOffset++]
                                                                 ,arybytData[ulngOffset++]
                                                                 ,arybytData[ulngOffset++]};
                                for (ulong i=0; i<ulngMsgLength; i++) {
                                    arybytDecoded[i] = (byte)(arybytData[ulngOffset + i] ^ arybytMasks[i % 4]);
                                }
                                string strDecoded = Encoding.UTF8.GetString(arybytDecoded);
                                string[] arystrDecoded = strDecoded.Split('=');
                                if (arystrDecoded.Length == 2) {
                                    if (arystrDecoded[0].CompareTo(mcstrParamMsg) == 0) {
                                        MessageParamHandler(ipClient, arystrDecoded[1]);
                                    }
                                    if (stream.CanWrite == true) {
                                        byte[] arybytAck = new byte[] { (byte)'a'
                                                                      , (byte)'c'
                                                                      , (byte)'k' };
                                        stream.Write(arybytAck, 0, arybytAck.Length);
                                        stream.Flush();
                                    }
                                    FrmMain.sobjThis.UpdateClientStats(eResponse: eCounterState.INCREMENT);
                                    Console.WriteLine(string.Format("Sending ack to client {0}", ipClient));
                                }
                            }
                        } else {
                            Console.WriteLine("Bad response");
                        }
                    }
                } catch (Exception ex) {
                    FrmMain.UpdateTaskbar(ex.Message);
                }
            }
        }

The above works fine in that the handshaking is good, but I do not receive any messages on either the client or the server, I’ve spent hours searching for a solution and found nothing except lots of examples calling send as I do, but I don’t see anything received.

Can anyone help?

Try to narrow things down to either the client or the server: browsers have fully spec-compliant WS implementations, so as long as you follow the API, it should work just fine.

However, on a JS note: you’re basically writing C# using JS syntax atm, JS has different naming conventions:

  • Variables and functions use lowerInitialCamelCase
  • Classes use UpperInitialCamelCase
  • True constants (e.g. globals, static class properties etc.) use UPPER_SNAKE_CASE

And JS does things differently in sometimes subtle, sometimes not subtle at all ways, so for instance:

  • Don’t use var, use let for mutable variable assignment and const for immutable variable assignment. Both of those are block scoped, and behave like normal variables, whereasvar is function scoped and gets hoisted to the start of your function, no matter where you think you declared it. You actually declared it as undefined at the fop of your function, and you’re reassigning it where you think you declared it.
  • Comparison in JS uses ===, not ==, unless you know exactly why a type coerced comparison is the only way to do what you need (which is almost never =).
  • String concatenation was the source of so many bugs that it’s been superceded by templating strings, so rather than "Sending: \"" + cstrSendMsg + "\" to " + cstrServerSocketIP you use `Sending: "${cstrSendMsg}" to ${cstrServerSocketIP}`. Easier to compose, and way easier for others to read.
  • Rely on things being truthy and falsey: instead of if (msgdump == null || typeof msgdump === "undefined") you can almost always just use if (!msgDump). Similarly, if (Connected() != true)if (!connected()) (using JS naming conventions)
  • console.log can log whatever you need so instead of trying to turn things into strings, just log them directly and look at your browser’s dev tools “console” tab. There is no reason to debug dump string data into the webpage, every browser comes with dev tools (even mobile ones: you simply attach to them via USB)
  • Remember to clear your timer if you’re using an interval timer, because errors do not invalidate the timer, it’s just going to trigger the same code again at the next interval tick, and it’ll just error out again, and it’ll keep doing that. The more robust way to do “intervals” is to use setTimeout instead, which runs only once, with some code that schedules the next timeout after your real code has finished based on the clock times for when the timeout triggered, and what time it is now that your code’s done. (in the understanding that JS timers aren’t “true” timers, they only guarantee they will wait at least as long as you tell them to, but there are no rules on how long they will wait at most so a timer on a 10ms timeout will never trigger before 10ms from now, but if it triggers a full hour later, that’s 100% in spec)

With all that said though, it’s generally a good idea to, when you get stuck like this, run through the “minimal reproducible code” exercise, where you start slashing your code, removing things that aren’t directly related to seeing that first message show up (on both the client and server side) until you hit a point where you can’t remove any more code without either the problem going away (hurray, we found the root cause!) or the code literally can’t work anymore (at which point you have the idea code to put in a question).

On a final note though, since you’re asking about C# + JS, you might want to ask your question over on Stackoverflow.com as well, which should get way more eyes on it than posting to the Glitch forum =)