title: C# WebSocket Version 8+ Server Communication
body: <p>I've been working on a hello-world sort of WebSocket app to see if I can get it working in VS 2010.  The client side code is very straight forward, and it works great in Safari because Safari still runs on the WebSocket 76 protocol.  </p>
  
  <p>Google Chrome on the other hand runs the newer Sec-WebSocket-Version code which is more complicated.  I "borrowed" some code as a base from a code-plex project to get it working for the older 76 and 75 protocols: <a href="http://wsaspnet.codeplex.com/SourceControl/changeset/view/58121#1561340" rel="nofollow noreferrer">http://wsaspnet.codeplex.com/SourceControl/changeset/view/58121#1561340</a>  </p>
  
  <p>I then proceeded to upgrade the code I found there until it could perform the new handshake (in the code below).  The part I have finally hit a wall at and cannot seem to find any help on is communication to a Sec-WebSocket-Version 8+ socket.  It used to be you could just append a 0x00 to the beginning, and a 0xFF to the end and tada! it worked! but now apparently there is more to be done to fix a security problem.  I can tell that the socket is closing when I send a message by debugging the code, and I read in several places that chrome likes to close a socket when it receives a message it doesn't understand.</p>
  
  <p>So then I found this post:  <a href="https://stackoverflow.com/questions/8125507/how-can-i-send-and-receive-websocket-messages-on-the-server-side">How can I send and receive WebSocket messages on the server side?</a> where the guy had the same issue as me, but I can't seem to translate the pseudo-code that his answerer posted into C#.  Can anyone help me out?</p>
  
  <p>The client code:</p>
  
  <pre><code>&lt;div id="ComLog" style="height:600px;"&gt;
  
  
  &lt;/div&gt;
  &lt;input type="button" onclick="javascript:connectToServer();" value='connect' /&gt;
  
  &lt;script type="text/javascript"&gt;
      var sock;
      function connectToServer() {
          try {
              sock = new WebSocket("ws://localhost:8181/websock");
              //sock = new WebSocket("ws://192.168.0.100:8181/websock");
  
              //sock = new WebSocket("ws://websockets.org:8787");
  
              sock.onopen = sockOpen;
              sock.onerror = sockError;
              sock.onclose = sockClosed;
              sock.onmessage = sockMessage;
          } catch (e) {
              log("error:" + e);
          }
      }
  
      function sockOpen() {
          log("connected");
      }
  
      function sockError(error, p2) {
          log("socket error!");
      }
  
      function sockClosed() {
          log("socket closed");
      }
  
      function sockMessage(event) {
          log("&lt;b&gt;Server:&lt;/b&gt; " + event.data);
      }
  
      function log(msg) {
          var txtLog = document.getElementById("ComLog");
          txtLog.innerHTML += msg + "&lt;/br&gt;";
      }
  &lt;/script&gt;
  </code></pre>
  
  <p>The Server code:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Net.Sockets;
  using System.Net;
  using System.IO;
  using System.Web;
  using System.Collections.Specialized;
  using System.Text.RegularExpressions;
  using System.Threading;
  using System.Security.Cryptography;
  
  namespace WebSocks
  {
      public class WebSockServer
      {
  
          /// &lt;summary&gt;
          /// Port number to listen on
          /// &lt;/summary&gt;
          private const int PortNumber = 8181;
  
          /// &lt;summary&gt;
          /// Socket which awaits connections
          /// &lt;/summary&gt;
          private static Socket ListenerSocket;
  
          /// &lt;summary&gt;
          /// Thread in which we await for incomming connections.
          /// &lt;/summary&gt;
          static System.Threading.Thread _serverThread;
  
  
          static WebSockServer() { }
  
          /// &lt;summary&gt;
          /// Starts thread with listening socket.
          /// &lt;/summary&gt;
          public static void Start()
          {
              System.Threading.ThreadStart ts = new System.Threading.ThreadStart(Listen);
              _serverThread = new System.Threading.Thread(ts);
              _serverThread.Start();
          }
  
          /// &lt;summary&gt;
          /// Stops listening for connections.
          /// &lt;/summary&gt;
          public static void End()
          {
              _serverThread.Abort();
              ListenerSocket.Dispose();
          }
  
          public static void Listen()
          {
              //Start listening
              ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
              EndPoint ep = new IPEndPoint(IPAddress.Parse("0.0.0.0"), PortNumber);
              ListenerSocket.Bind(ep);
              ListenerSocket.Listen(5);
  
              while (true)
              {
                  //New client
                  using (Socket client = ListenerSocket.Accept())
                  {
                      //Receiving clientHandshake
                      string clientHandshake = String.Empty;
                      byte[] buffer = null;
                      int readBytes = 0;
                      do
                      {
                          buffer = new byte[client.Available];
                          readBytes = client.Receive(buffer);
                          clientHandshake += Encoding.UTF8.GetString(buffer);
                      }
                      while (client.Available &gt; 0);
  
                      //Last eight bytes are body of requets (we should include it in response)
                      byte[] secKey3 = buffer.Skip(readBytes - 8).Take(8).ToArray();
  
                      //Variables we can extract from clientHandshake
                      string clientOrigin = String.Empty;
                      string secKey1 = String.Empty;
                      string secKey2 = String.Empty;
                      string WebSocketVersion = String.Empty;
                      int WSV = 0;
                      string WebSocketKey = String.Empty;
  
                      //Extracting values from headers (key:value)
                      string[] clientHandshakeLines = Regex.Split(clientHandshake, Environment.NewLine);
                      foreach (string hline in clientHandshakeLines)
                      {
                          int valueStartIndex = hline.IndexOf(':') + 2;
                          if (valueStartIndex &gt; 0)
                          {
                              if (hline.StartsWith("Origin"))
                              {
                                  clientOrigin = hline.Substring(valueStartIndex, hline.Length - valueStartIndex);
                              }
                              else if (hline.StartsWith("Sec-WebSocket-Key2"))
                              {
                                  secKey2 = hline.Substring(valueStartIndex, hline.Length - valueStartIndex);
                              }
                              else if (hline.StartsWith("Sec-WebSocket-Key1"))
                              {
                                  secKey1 = hline.Substring(valueStartIndex, hline.Length - valueStartIndex);
                              }
  
                              if (hline.StartsWith("Sec-WebSocket-Version"))
                              {
                                  WebSocketVersion = hline.Replace("Sec-WebSocket-Version: ", "");
                                  WSV = Convert.ToInt32(WebSocketVersion);
                              }
  
                              if (hline.StartsWith("Sec-WebSocket-Key"))
                              {
                                  WebSocketKey = hline.Replace("Sec-WebSocket-Key: ", "");
                              }
                          }
                      }
  
  
  
  
                      if (!String.IsNullOrEmpty(WebSocketVersion)) //WebSocketVersion 8 and up handshake check
                      {
                          //New WebSocketVersion number, included after Version 8
                          StringBuilder mResponse = new StringBuilder();
                          mResponse.AppendLine("HTTP/1.1 101 Switching Protocols");
                          mResponse.AppendLine("Upgrade: WebSocket");
                          mResponse.AppendLine("Connection: Upgrade");
                          mResponse.AppendLine(String.Format("Sec-WebSocket-Accept: {0}", ComputeWebSocketHandshakeSecurityHash09(WebSocketKey)) + Environment.NewLine);
  
                          byte[] HSText = Encoding.UTF8.GetBytes(mResponse.ToString());
  
                          client.Send(HSText, 0, HSText.Length, 0);
                      }
                      else
                      {
                          //This part is common for all websockets editions (v. 75 &amp; v.76)
                          client.Send(Encoding.UTF8.GetBytes("HTTP/1.1 101 Web Socket Protocol Handshake" + Environment.NewLine));
                          client.Send(Encoding.UTF8.GetBytes("Upgrade: WebSocket" + Environment.NewLine));
                          client.Send(Encoding.UTF8.GetBytes("Connection: Upgrade" + Environment.NewLine));
  
  
                          if (String.IsNullOrEmpty(secKey1) &amp;&amp; String.IsNullOrEmpty(secKey2))  //75 or less handshake check
                          {                 
                              client.Send(Encoding.UTF8.GetBytes(String.Format("WebSocket-Origin: {0}", clientOrigin) + Environment.NewLine));
                              client.Send(Encoding.UTF8.GetBytes("WebSocket-Location: ws://localhost:8181/websock" + Environment.NewLine));
                              client.Send(Encoding.UTF8.GetBytes(Environment.NewLine));
                          }
                          else //76 handshake check
                          {
                              //Keys present, this means 76 version is used. Writing Sec-* headers 
                              client.Send(Encoding.UTF8.GetBytes(String.Format("Sec-WebSocket-Origin: {0}", clientOrigin) + Environment.NewLine));
                              client.Send(Encoding.UTF8.GetBytes("Sec-WebSocket-Location: ws://localhost:8181/websock" + Environment.NewLine));
                              client.Send(Encoding.UTF8.GetBytes(Environment.NewLine));
  
                              //Calculating response body
                              byte[] secret = CalculateSecurityBody(secKey1, secKey2, secKey3);
                              client.Send(secret);
                          }
                      }
  
                      Thread.Sleep(1000);
  
                      SendMessage("This message will terminate in 5 seconds...", client, WSV);
  
                      Thread.Sleep(1000);
  
                      SendMessage("4", client, WSV);
  
                      Thread.Sleep(1000);
  
                      SendMessage("3", client, WSV);
  
                      Thread.Sleep(1000);
  
                      SendMessage("2", client, WSV);
  
                      Thread.Sleep(1000);
  
                      SendMessage("1", client, WSV);
  
                      Thread.Sleep(1000);
  
                  }
              }
          }
  
          private static void SendMessage(string Msg, Socket client, int WebSockVersion)
          {
              if (WebSockVersion &gt;= 8)
              {
                  //This is the section that doesn't work
                  client.Send(Encoding.UTF8.GetBytes(Msg));
  
              }
              else 
              {
                  client.Send(new byte[] { 0x00 });
                  client.Send(Encoding.UTF8.GetBytes(Msg));
                  client.Send(new byte[] { 0xFF });
              }
          }
  
  
          public static byte[] CalculateSecurityBody(string secKey1, string secKey2, byte[] secKey3)
          {
              //Remove all symbols that are not numbers
              string k1 = Regex.Replace(secKey1, "[^0-9]", String.Empty);
              string k2 = Regex.Replace(secKey2, "[^0-9]", String.Empty);
  
              //Convert received string to 64 bit integer.
              Int64 intK1 = Int64.Parse(k1);
              Int64 intK2 = Int64.Parse(k2);
  
              //Dividing on number of spaces
              int k1Spaces = secKey1.Count(c =&gt; c == ' ');
              int k2Spaces = secKey2.Count(c =&gt; c == ' ');
              int k1FinalNum = (int)(intK1 / k1Spaces);
              int k2FinalNum = (int)(intK2 / k2Spaces);
  
              //Getting byte parts
              byte[] b1 = BitConverter.GetBytes(k1FinalNum).Reverse().ToArray();
              byte[] b2 = BitConverter.GetBytes(k2FinalNum).Reverse().ToArray();
              //byte[] b3 = Encoding.UTF8.GetBytes(secKey3);
              byte[] b3 = secKey3;
  
              //Concatenating everything into 1 byte array for hashing.
              List&lt;byte&gt; bChallenge = new List&lt;byte&gt;();
              bChallenge.AddRange(b1);
              bChallenge.AddRange(b2);
              bChallenge.AddRange(b3);
  
              //Hash and return
              byte[] hash = MD5.Create().ComputeHash(bChallenge.ToArray());
              return hash;
          }
  
          public static String ComputeWebSocketHandshakeSecurityHash09(String secWebSocketKey)         
          {             
              const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";             
              String secWebSocketAccept = String.Empty;              
              // 1. Combine the request Sec-WebSocket-Key with magic key.             
              String ret = secWebSocketKey + MagicKEY;              
              // 2. Compute the SHA1 hash             
              SHA1 sha = new SHA1CryptoServiceProvider();             
              byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));              
              // 3. Base64 encode the hash             
              secWebSocketAccept = Convert.ToBase64String(sha1Hash);              
              return secWebSocketAccept;         
          } 
      }
  }
  </code></pre>
  
  <p>The apparent untranslated "answer" to all my problems:</p>
  
  <pre><code>bytesFormatted[0] = 129
  
  indexStartRawData = -1 // don't matter what value is here; it will be set now:
  
  if bytesRaw.length &lt;= 125
      bytesFormatted[1] = bytesRaw.length
  
      indexStartRawData = 2
  
  else if bytesRaw.length &gt;= 126 and bytesRaw.length &lt;= 65535
      bytesFormatted[1] = 126
      bytesFormatted[2] = ( bytesRaw.length &gt;&gt; 8 ) AND 255
      bytesFormatted[3] = ( bytesRaw.length      ) AND 255
  
      indexStartRawData = 4
  
  else
      bytesFormatted[1] = 127
      bytesFormatted[2] = ( bytesRaw.length &gt;&gt; 56 ) AND 255
      bytesFormatted[3] = ( bytesRaw.length &gt;&gt; 48 ) AND 255
      bytesFormatted[4] = ( bytesRaw.length &gt;&gt; 40 ) AND 255
      bytesFormatted[5] = ( bytesRaw.length &gt;&gt; 32 ) AND 255
      bytesFormatted[6] = ( bytesRaw.length &gt;&gt; 24 ) AND 255
      bytesFormatted[7] = ( bytesRaw.length &gt;&gt; 16 ) AND 255
      bytesFormatted[8] = ( bytesRaw.length &gt;&gt;  8 ) AND 255
      bytesFormatted[9] = ( bytesRaw.length       ) AND 255
  
      indexStartRawData = 10
  
  // put raw data at the correct index
  bytesFormatted.put(bytesRaw, indexStartRawData)
  
  
  // now send bytesFormatted (e.g. write it to the socket stream)
  </code></pre>
  
  <p>~~~~~~~~~~~Edit~~~~~~~~~~~~</p>
  
  <p>Current version of send function:</p>
  
  <pre><code>private static void SendMessage(string Msg, Socket client, int WebSockVersion)
          {
              if (WebSockVersion &gt;= 8)
              {
                  bool IsFinal = true;
                  int OpCode = 1;
                  int? Mask = null;
                  int PayloadLength = Encoding.UTF8.GetBytes(Msg).Length;
                  byte[] buffer = Encoding.UTF8.GetBytes(Msg);
  
                  int offset = 0;
                  buffer[offset++] = (byte)((IsFinal ? 128 : 0) | ((int)OpCode &amp; 15));
                  if (PayloadLength &gt; ushort.MaxValue)
                  { // write as a 64-bit length
                      buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 127);
                      buffer[offset++] = 0;
                      buffer[offset++] = 0;
                      buffer[offset++] = 0;
                      buffer[offset++] = 0;
                      buffer[offset++] = (byte)(PayloadLength &gt;&gt; 24);
                      buffer[offset++] = (byte)(PayloadLength &gt;&gt; 16);
                      buffer[offset++] = (byte)(PayloadLength &gt;&gt; 8);
                      buffer[offset++] = (byte)(PayloadLength);
                  }
                  else if (PayloadLength &gt; 125)
                  { // write as a 16-bit length
                      buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 126);
                      buffer[offset++] = (byte)(PayloadLength &gt;&gt; 8);
                      buffer[offset++] = (byte)(PayloadLength);
                  }
                  else
                  { // write in the header
                      buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | PayloadLength);
                  }
                  if (Mask.HasValue)
                  {
                      int mask = Mask.Value;
                      buffer[offset++] = (byte)(mask &gt;&gt; 24);
                      buffer[offset++] = (byte)(mask &gt;&gt; 16);
                      buffer[offset++] = (byte)(mask &gt;&gt; 8);
                      buffer[offset++] = (byte)(mask);
                  }
                  //stream.Write(buffer, 0, offset);
  
                  client.Send(buffer, 0, PayloadLength, SocketFlags.None);
  
              }
              else 
              {
                  client.Send(new byte[] { 0x00 });
                  client.Send(Encoding.UTF8.GetBytes(Msg));
                  client.Send(new byte[] { 0xFF });
              }
          }
  </code></pre>