0

I successfully made a WebSocket server in C# which I can connect to. I make the handshake as RFC 6455 requires it.

Whatever I send (via WebSocket.send()) to it (for example "asd") the stream only has 9 bytes of data which is "unrepresentable" by UTF8.

using System.Net.Sockets;
using System.Net;
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Security.Cryptography;

class Server
{
    public static void Main()
    {
        TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);

        server.Start();

        TcpClient client = server.AcceptTcpClient();
        NetworkStream stream = client.GetStream();

        Boolean isHandshaked = false;

        while (true)
        {
            while (!stream.DataAvailable)
            {
            }

            Byte[] bytes = new Byte[client.Available];

            stream.Read(bytes, 0, bytes.Length);

            if (!isHandshaked)
            {
                Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + Environment.NewLine
                    + "Connection: Upgrade" + Environment.NewLine
                    + "Upgrade: websocket" + Environment.NewLine
                    + "Sec-WebSocket-Accept: " + Convert.ToBase64String(
                        SHA1.Create().ComputeHash(
                            Encoding.UTF8.GetBytes(
                                new Regex("Sec-WebSocket-Key: (.*)").Match(
                                    Encoding.UTF8.GetString(bytes)
                                ).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
                            )
                        )
                    ) + Environment.NewLine
                     + Environment.NewLine);

                stream.Write(response, 0, response.Length);

                isHandshaked = true;
            }
            else
            {
                Console.WriteLine(Encoding.UTF8.GetString(bytes));
            }
        }
    }
}

Where I missed something?

3 Answers3

2

Messages between client and server are not sent as plain text. See the data framing section of the standard for how to encode/decode them.

For your example of a client sending a 3 byte string, this will result in a message of

  • 1 byte - 0x81 - saying that this is a non-fragmented text message
  • 1 byte - 0x83 - saying that the message body is 3 bytes long and it's contents are masked (all client -> server messages use masking. server -> client messages must not be masked). The top bit (byte_val & 0x80) is set if a message is masked. The remaining 7 bits (byte_val & 0x7F) gives the length of the messages up to 125 bytes. See link below for how to determine the length of longer messages.
  • 4 bytes - mask. Always 4 bytes. Content determined by the client and should change for each message
  • 3 bytes - message. Can be decoded using the mask and algorithm in section 5.3 of the spec.

You can unmask a message using code like the following

byte mask[4];
byte[] msg_data;
// read message, initialising mask, msg_data
for (int i=0; i<msg_data.Length; i++)
{
    msg_data[i] = msg_data[i] ^ mask[i%4]
}

If you need more detail, this previous post explains message sending/receiving and includes some helpful pseudo code.

Community
  • 1
  • 1
simonc
  • 41,632
  • 12
  • 85
  • 103
0

Having an empty loop to check for data i.e. while (!stream.DataAvailable){} is really a bad practice that you can avoid.

The read method is a blocking method, so it will wait until the data is available

int bufferSize = 1024; // change it as you want
byte[] message = new byte[bufferSize];
readLength = stream.Read(message, 0, bufferSize);
iTech
  • 18,192
  • 4
  • 57
  • 80
  • I use it to wait for `client.Available` to be available in order to specify exactly the size of byte array. What happens in your code if the incoming data is larger than 1 kB? I think you get truncated data. – Sándor Rakonczai Feb 08 '13 at 21:27
  • The data will **not** be truncated, the read function is within the while loop, so simply it will be read in the next iteration. Use larger buffer size as needed if you want to read all data at once. Bottom line, the empty loop is a waste and not good practice. – iTech Feb 08 '13 at 21:32
0

You can try code below and see if it works, i suspect you are not reading full response.

    byte[] buffer = new byte[4155];
    int bytesRead = 0;

    using (var input = client.GetStream())
    {
      while (true)
                    {
                        bytesRead = input.Read(buffer, 0, buffer.Length);
                        totalBytes += bytesRead;
                        if (bytesRead > 0)
                            // keep processing ur data here, add it to another buffer maybe
                        else
                            break; // come out of while loop if there is no data
                    }

    }
  }
DotNetUser
  • 6,494
  • 1
  • 25
  • 27