Search code examples
c#c#-4.0websocketserver-sideserver

C# - Websocket - Sending Message Back To Client


I've been working on a C# Web Socket server for roughly 24 hours.

I've currently figured out how to complete a handshake and get the connection initialized.

Also I've figured out how to take the byte[] data and decode it into a it's original string.

But now I'm stuck and looking for help.

I can't seem to figure out how to put together a proper data structure and send it back to the client. If you send the original data you received the WebSocket on the client-side will tell you the data cannot be masked (which is why it needs to be decoded).

So basically, what I'm asking in short is how do I structure response data to send back to the WebSocket client?

I've been using https://www.rfc-editor.org/rfc/rfc6455 as a resource for my research.

Please keep in mind that I'm just using a regular socket for this.

Here is my decode code:

if (dataBuffer.Length > 0)
{
    if (dataBuffer[0] == 129)
    {
        int msg_length = dataBuffer[1] - 128;
        if (msg_length <= 125)
        {
            // Msg ready to decode.
            Log.info("Message Length: " + msg_length);


            Byte[] decoded = new Byte[dataBuffer.Length];
            Byte[] encoded = new Byte[dataBuffer.Length - 6];

            Array.Copy(dataBuffer, 6, encoded, 0, msg_length);

            Byte[] key = new Byte[4] { dataBuffer[2], dataBuffer[3], dataBuffer[4], dataBuffer[5] };
            for (int i = 0; i < encoded.Length; i++)
            {
                decoded[i] = (Byte)(encoded[i] ^ key[i % 4]);
            }

            Log.info("MSG: " + Encoding.UTF8.GetString(decoded));

            byte[] return_msg = new byte[decoded.Length + 8];

            return_msg[0] = 1;
            return_msg[1] = 0;
            return_msg[2] = 0;
            return_msg[3] = 0;
            // OP Code
            return_msg[4] = 0x1;
            return_msg[5] = 0x0;
            return_msg[6] = 0x0;
            return_msg[7] = 0x0;

            Array.Copy(decoded, 0, return_msg, 8, decoded.Length);

            socket.Send(return_msg);
        }
        else if (msg_length == 126)
        {
            // Longer Message
            msg_length = dataBuffer[2] + dataBuffer[3];

            Log.info("Message Length: " + msg_length);

            Byte[] key = new Byte[4] { dataBuffer[4], dataBuffer[5], dataBuffer[6], dataBuffer[7] };

            Byte[] decoded = new Byte[dataBuffer.Length];
            Byte[] encoded = new Byte[dataBuffer.Length - 8];

            Array.Copy(dataBuffer, 8, encoded, 0, msg_length);

            for (int i = 0; i < encoded.Length; i++)
            {
                decoded[i] = (Byte)(encoded[i] ^ key[i % 4]);
            }

            Log.info("MSG: " + Encoding.UTF8.GetString(decoded));
            byte[] return_msg = new byte[decoded.Length + 4];

            return_msg[0] = 129;
            return_msg[1] = 0;
            return_msg[2] = 0;
            return_msg[3] = 0;
                    
            Array.Copy(decoded,0,return_msg,4,decoded.Length);

            socket.Send(return_msg);
        }
        else if (msg_length == 127)
        {
            // Huge Message:
            Log.info("BIG MESSAGE");
        }
    }

}

Solution

  • Take a look to this two articles about writing C# WebSocket servers:

    https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_servers

    https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_server

    It seems like they are both the same article, but they are not! In this part of the first link you have the explanation about how to build frames.

    UPDATE

    The first byte contains several information:

    • FIN :Indicates that the frame contains a complete message.
    • RSV1: Option 1
    • RSV2: Option 2
    • RSV3: Option 3
    • OptCode: Indicates the type of frame.

    If you want to send a Text message smaller than 125 bytes, lets say your message has 90 bytes, in the first byte you will put the bit 0 to 1(more significant), the next 3 to 0, unless you want to enable options, and the next 4 would be 0001, indicating a Text frame. So your first bye would be 10000001, or 129.

    Now in the second byte, the first bit indicates if the frame is masked. You don't mask frames from server to client, so you set a 0. The next 7 bits indicates rather the length, or the type of frame length. Because you are sending a small frame, you can indicate any value up to 125 in those 7 bits. So because the frame length is 90 bytes, the second byte of the header would be 01011010, or 90.

    So when sending from server to client a Text frame of 90 byes, the first two bytes conforming the header would be 129 and 90. The rest of the message would be 90 bytes of UTF8 encoded bytes.

    If the frame is longer than 125 bytes, also is the length of the header, check the spec. If the frame need to be masked (like the frames you get from the client), the first 4 bytes of the body contain the masking key. As you see there are some tidbits to cope with, so I recommend you to read the spec : https://www.rfc-editor.org/rfc/rfc6455