public static byte[] GenerateResponseHandshake(ClientHandshake clientHandshake)
        {
            var responseHandshake = new ServerHandshake();
            responseHandshake.Location = "ws://" + clientHandshake.Host + clientHandshake.ResourcePath;
            responseHandshake.Origin = clientHandshake.Origin;
            responseHandshake.SubProtocol = clientHandshake.SubProtocol;

            var challenge = new byte[8];
            Array.Copy(clientHandshake.ChallengeBytes.Array, clientHandshake.ChallengeBytes.Offset, challenge, 0, 8);

            responseHandshake.AnswerBytes = CalculateAnswerBytes(clientHandshake.Key1, clientHandshake.Key2, clientHandshake.ChallengeBytes);

            return CreateServerHandshake(responseHandshake);
        }
        public static ClientHandshake ParseClientHandshake(ArraySegment<byte> byteShake)
        {
            // the "grammar" of the handshake
            var pattern = @"^(?<connect>[^\s]+)\s(?<path>[^\s]+)\sHTTP\/1\.1\r\n" +  // request line
                          @"((?<field_name>[^:\r\n]+):\s(?<field_value>[^\r\n]+)\r\n)+"; // unordered set of fields (name-chars colon space any-chars cr lf)

            // subtract the challenge bytes from the handshake
            var handshake = new ClientHandshake();
            ArraySegment<byte> challenge = new ArraySegment<byte>(byteShake.Array, byteShake.Count - 8, 8); // -8 : eight byte challenge
            handshake.ChallengeBytes = challenge;

            // get the rest of the handshake
            var utf8_handshake = Encoding.UTF8.GetString(byteShake.Array, 0, byteShake.Count - 8);

            // match the handshake against the "grammar"
            var regex = new Regex(pattern, RegexOptions.IgnoreCase);
            var match = regex.Match(utf8_handshake);
            var fields = match.Groups;

            // save the request path
            handshake.ResourcePath = fields["path"].Value;

            // run through every match and save them in the handshake object
            for (int i = 0; i < fields["field_name"].Captures.Count; i++)
            {
                var name = fields["field_name"].Captures[i].ToString();
                var value = fields["field_value"].Captures[i].ToString();

                switch (name.ToLower())
                {
                    case "sec-websocket-key1":
                        handshake.Key1 = value;
                        break;
                    case "sec-websocket-key2":
                        handshake.Key2 = value;
                        break;
                    case "sec-websocket-protocol":
                        handshake.SubProtocol = value;
                        break;
                    case "origin":
                        handshake.Origin = value;
                        break;
                    case "host":
                        handshake.Host = value;
                        break;
                    case "cookie":
                        // create and fill a cookie collection from the data in the handshake
                        handshake.Cookies = new HttpCookieCollection();
                        var cookies = value.Split(';');
                        foreach (var item in cookies)
                        {
                            // the name if before the '=' char
                            var c_name = item.Remove(item.IndexOf('='));
                            // the value is after
                            var c_value = item.Substring(item.IndexOf('=') + 1);
                            // put the cookie in the collection (this also parses the sub-values and such)
                            handshake.Cookies.Add(new HttpCookie(c_name.TrimStart(), c_value));
                        }
                        break;
                    default:
                        // some field that we don't know about
                        if (handshake.AdditionalFields == null)
                            handshake.AdditionalFields = new Dictionary<string, string>();
                        handshake.AdditionalFields[name] = value;
                        break;
                }
            }
            return handshake;
        }
        /// <summary>
        /// Establishes the connection
        /// </summary>
        public void Connect()
        {
            string host = uri.Host;
            StringBuilder path = new StringBuilder(uri.AbsolutePath);
            if (path.Length == 0)
            {
                path.Append('/');
            }

            string query = uri.Query;
            if (!string.IsNullOrEmpty(query))
            {
                path.Append("?");
                path.Append(query);
            }

            string origin = "http://" + host;

            networkStream = CreateSocket();
            if (networkStream != null)
            {
                if (uri.Port != 80)
                {
                    host = host + ":" + uri.Port;
                }

                ClientHandshake shake = new ClientHandshake();
                shake.Host = host;
                shake.Origin = origin;
                shake.AdditionalFields = headers;
                shake.Key1 = Guid.NewGuid().ToString();
                shake.Key2 = Guid.NewGuid().ToString();
                shake.Key1 = shake.Key1.Replace('-', ' ').Substring(0, 10);
                shake.Key2 = shake.Key2.Replace('-', ' ').Substring(0, 10);
                var baseChallenge = Guid.NewGuid().ToString().Substring(0, 8);
                var challenge = baseChallenge.Substring(0, 2) + " " + baseChallenge.Substring(3, 2) + " " + baseChallenge.Substring(5, 2);
                shake.ChallengeBytes = new ArraySegment<byte>(Encoding.UTF8.GetBytes(challenge));
                shake.ResourcePath = path.ToString();
                //outputStream = new StreamWriter(networkStream, Encoding.UTF8);
                var response = shake.ToString();
                byte[] encodedHandshake = Encoding.UTF8.GetBytes(response);
                networkStream.Write(encodedHandshake, 0, encodedHandshake.Length);
                networkStream.Flush();

                //This needs to be implemented for security
            //                var expectedAnswer = Encoding.UTF8.GetString(HandshakeHelper.CalculateAnswerBytes(shake.Key1, shake.Key2, shake.ChallengeBytes));

                inputStream = new StreamReader(networkStream);
                //var input = inputStream.ReadToEnd();
                string header = inputStream.ReadLine();
                if (!header.Equals("HTTP/1.1 101 WebSocket Protocol Handshake"))
                {
                    if (OnError != null)
                        OnError(new InvalidOperationException("Invalid handshake response"));
                    throw new InvalidOperationException("Invalid handshake response");
                }

                header = inputStream.ReadLine();
                if (!header.Equals("Upgrade: WebSocket"))
                {
                    if (OnError != null)
                        OnError(new InvalidOperationException("Invalid handshake response"));
                    throw new InvalidOperationException("Invalid handshake response");
                }

                header = inputStream.ReadLine();
                if (!header.Equals("Connection: Upgrade"))
                {
                    if (OnError != null)
                        OnError(new InvalidOperationException("Invalid handshake response"));
                    throw new InvalidOperationException("Invalid handshake response");
                }

                // Ignore any further response
                do
                {
                    header = inputStream.ReadLine();
                } while (!header.Equals(""));

                handshakeComplete = true;

                connection = new WebSocketConnection(networkStream, isSocketIo);
                SubscribeToConnectionEvents();
            }
            else
            {
                throw new InvalidOperationException("Could not create socket");
            }
        }