Search code examples
c#websockettcplistener

Receiving data from a web client


[Edit] Please jump to the bottom for the latest edits...

I am developing a console application using a TcpListener to receive requests from clients:

mWebSocketServer = new TcpListener(ipWebSocketAddr, mcintWebSocketPort);

ipWebSocketAddr contains 192.168.5.73, mcintWebSocketPort contains 443. I start listening and create a thread to manage clients and received data:

try { 
    mWebSocketServer.Start();
    mWebSocketThread = new Thread(() => webSocketServer());
    mWebSocketThread.Start();
} catch (Exception ex) {
    FrmMain.UpdateTaskbar(ex.ToString());
}

The thread:

public async void webSocketServer() {
    Byte[] arybytData = new byte[1024];
    while (mblnRunTillStopped == true) {
        try {
            TcpClient tcpClient = mWebSocketServer.AcceptTcpClient();
            Console.WriteLine(FrmMain.strTimeStampMsg("webSocket client connected"));
            NetworkStream stream = tcpClient.GetStream();
            int i;
            while ((i = stream.Read(arybytData, 0, arybytData.Length)) != 0) {
                string strRX = System.Text.Encoding.ASCII.GetString(arybytData, 0, i);
                Console.WriteLine(FrmMain.strTimeStampMsg("From web-client: " + strRX));
                string strTX = "Acknowledge";
                byte[] arybytTX = System.Text.Encoding.ASCII.GetBytes(strTX);
                stream.Write(arybytTX, 0, arybytTX.Length);
            }
            Thread.Sleep(mcintWebSocketServerThreadSleep);
        } catch (Exception ex) {
            FrmMain.UpdateTaskbar(ex.Message);
        }
    }
}

A client would send data with:

webSocket.send(cstrSendMsg);

cstrSendMsg contains msg=US. In the C# application I report all received data to the console, what I see is:

GET / HTTP/1.1
Host: 192.168.5.73:443
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
Upgrade: websocket
Origin: http://192.168.5.73
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Sec-WebSocket-Key: rlhgDPkLxwNFeA6pMZtNwg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

I don't see anything that resembles the data, where do I look for that?

I'm using Chrome (Version 131.0.6778.86 (Official Build) (64-bit)) for the client and to debug.

This is the client-side script:

    webSocket = new WebSocket(cstrServerSocketIP);
    webSocket.onclose = (event) => {
        DebugMsg("webSocket.onclose");    
    };    
    webSocket.onerror = (event) => {
        DebugMsg("webSocket.onerror");   
        if (typeof event == "object" && typeof event.data != "undefined") {
            DebugMsg(event.data);    
        }
    };    
    webSocket.onmessage = (event) => {
        DebugMsg("webSocket.onmessage: " + event.data);
    };
    webSocket.onopen = (event) => {
        DebugMsg("webSocket.onopen: " + event.data);    
    };    

cstrServerSocketIP contains ws://192.168.5.73:443/, after single stepping through this I can see in the Chrome debugger:

WebSocket connection to 'ws://192.168.5.73:443/' failed: 

I have no idea why because no reason is given. This is a sample from the server side of a client interaction:

Received:
21-11-2024 14:46:03.937> From web-client:
GET / HTTP/1.1
Host: 192.168.5.73:443
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
Upgrade: websocket
Origin: http://192.168.5.73
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Sec-WebSocket-Key: iURnr6DRWqF2wbtypFsLig==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

Response:
HTTP/1.1 1-1 Switching Protocols
Connection: Update
Upgrade: websocket
Sec-WebSocket-Accept: AFRdDliX+pqKnGCr652yekplUAI=

Is there something wrong with the Sec-WebSocket-Accept value?

This is what I do:

  1. Take the received value associated with Sec-WebSocket-Key
  2. Build a new string which is the value associated with Sec-WebSocket-Key concatenated with "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
  3. Create a SHA1 using the result of 2.
  4. Base64 encode result from 3.
  5. Write result from 4 into header Sec-WebSocket-Accept.

[Edit2] I've modified the original thread so the same thread should work for both HTTP and Websockets, the modification, which is a work in progress, but it seems to have broken the web-server:

        public async void httpServer() {
            while (mblnRunTillStopped == true) {
                try {
                    HttpListenerContext ctx = await mListener.GetContextAsync();
                    HttpListenerRequest req = ctx.Request;
                    if (req.IsWebSocketRequest) {
                        HttpListenerWebSocketContext ctxWS = await ctx.AcceptWebSocketAsync(null);
                        Console.WriteLine("HERE");
                    }

It doesn't get to the Console.WriteLine("HERE");

I still see in the client debugger:

WebSocket connection to 'ws://192.168.5.73:443/' failed: 

[Another Edit] Can someone verify if I have prepared response header correctly?

He is a sample request:

GET / HTTP/1.1
Host: 192.168.5.73:443
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
Upgrade: websocket
Origin: http://192.168.5.73
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Sec-WebSocket-Key: GysunwWXiQEX05sMpW7Y1g==

I then take the value GysunwWXiQEX05sMpW7Y1g== and append 258EAFA5-E914-47DA-95CA-C5AB0DC85B11

string strKey = strWebSocketKey + mcstrWebSocketMagicString; 

Then I create a SHA1 key:

byte[] arybytSHA1 = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(strKey));

And finally base64 encode:

strWebSocketAccept = Convert.ToBase64String(arybytSHA1);   

The response:

HTTP/1.1 1-1 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: PuuJ05IxiUQuB9aBpBZCIBKCU38=

Something is clearly wrong because the client shows:

file.js:33 WebSocket connection to 'ws://192.168.5.73:443/' failed

I found this post: generate "Sec-WebSocket-Accept" from "Sec-WebSocket-Key" And have used the answer to compare and verify mine is correct, but still the client reports Failed ?


Solution

  • As it turns out its the simplest of mistakes that cause the problems, In my original post I pasted in the problem:

    HTTP/1.1 1-1 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: PuuJ05IxiUQuB9aBpBZCIBKCU38=
    

    The above response was built on something I found online, the actual problem and error is on the very first line:

    1-1
    

    It should and must be:

    101
    

    Interestingly and amazingly, my son suggested I try using https://claude.ai, which I did, I opened it up and pasted in the HTTP request headers, then posted under it, "What's wrong with the above?", instantly it came back with the correction.