Создание примера WebSocket" Hello World"

Я не понимаю, почему я не могу сделать следующий код работать. Я хочу подключиться с помощью JavaScript к моему консольному приложению сервера. А затем отправить данные на сервер.

вот код сервера:

    static void Main(string[] args)
    {            
        TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 9998);
        server.Start();
        var client = server.AcceptTcpClient();
        var stream = client.GetStream();

        while (true)
        {
            var buffer = new byte[1024]; 
            // wait for data to be received
            var bytesRead = stream.Read(buffer, 0, buffer.Length);                
            var r = System.Text.Encoding.UTF8.GetString(buffer);
            // write received data to the console
            Console.WriteLine(r.Substring(0, bytesRead));
        }
    }

и вот JavaScript:

        var ws = new WebSocket("ws://localhost:9998/service");
        ws.onopen = function () {
            ws.send("Hello World"); // I WANT TO SEND THIS MESSAGE TO THE SERVER!!!!!!!!
        };

        ws.onmessage = function (evt) {
            var received_msg = evt.data;
            alert("Message is received...");
        };
        ws.onclose = function () {
            // websocket is closed.
            alert("Connection is closed...");
        };

когда я запускаю этот код вот что получается:

обратите внимание, что при запуске JavaScript сервер принимает и успешно устанавливает соединение. JavaScript не может отправлять данные. Всякий раз, когда я размещаю метод отправки, он не будет отправлять, даже если соединение установлено. Как я могу это сделать?

4 ответов


WebSockets-это протокол, основанный на потоковом соединении TCP. Хотя WebSockets-это протокол на основе сообщений.

если вы хотите реализовать свой собственный протокол, то я рекомендую использовать последнюю и стабильную спецификацию (для 18/04/12) RFC 6455. Эта спецификация содержит всю необходимую информацию относительно рукопожатия и кадрирование. Как и большинство описаний сценариев поведения со стороны браузера, а также со стороны сервера. Настоятельно рекомендуется следовать какие рекомендации говорят о стороне сервера во время реализации вашего кода.

в нескольких словах я бы описал работу с WebSockets следующим образом:

  1. создать сервер-сокет (система.Сеть.Сокеты) свяжите его с определенным портом и продолжайте слушать с асинхронным принятием соединений. Что-то вроде этого:--6-->

    Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
    serverSocket.Listen(128);
    serverSocket.BeginAccept(null, 0, OnAccept, null);
  2. вы должны есть!--16-->принимать функция "OnAccept", которая будет реализовать рукопожатие. В будущем он должен быть в другой ветке, если система предназначена для обработки огромного количества соединений в секунду.

    private void OnAccept(IAsyncResult result) {
    try {
        Socket client = null;
        if (serverSocket != null && serverSocket.IsBound) {
            client = serverSocket.EndAccept(result);
        }
        if (client != null) {
            /* Handshaking and managing ClientSocket */
        }
    } catch(SocketException exception) {
    
    } finally {
        if (serverSocket != null && serverSocket.IsBound) {
            serverSocket.BeginAccept(null, 0, OnAccept, null);
        }
    }
    }
  3. после установления соединения, вы должны сделать рукопожатие. На основе спецификации 1.3 Открытие Рукопожатие, после установления соединения вы получите базовый HTTP-запрос с некоторой информацией. Пример:

    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Origin: http://example.com
    Sec-WebSocket-Protocol: chat, superchat
    Sec-WebSocket-Version: 13

    этот пример основан на версии протокола 13. Иметь в виду что более старые версии имеют некоторые различия, но в основном последние версии являются кросс-совместимыми. Различные браузеры могут отправлять вам дополнительные данные. Например, детали браузера и ОС, кэш и другие.

    на основе предоставленных деталей рукопожатия вы должны генерировать строки ответа, они в основном одинаковы, но будут содержать Accpet-Key, который основан на предоставленном Sec-WebSocket-Key. В спецификации 1.3 четко описано, как генерировать ключ ответа. Вот моя функция, которую я использовал для Версии V13:

    static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    private string AcceptKey(ref string key) {
        string longKey = key + guid;
        SHA1 sha1 = SHA1CryptoServiceProvider.Create();
        byte[] hashBytes = sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(longKey));
        return Convert.ToBase64String(hashBytes);
    }
    

    рукопожатие ответ выглядит так:

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

    но ключ accept должен быть сгенерирован на основе предоставленного ключа от клиента и метода AcceptKey, который я предоставил ранее. Также убедитесь, что после последнего символа ключа accept вы поставили две новые строки "\r\n\r\n".

  4. после того, как ответ на рукопожатие отправляется с сервера, клиент должен вызвать"onopen функция", что означает, что вы можете отправлять сообщения после.
  5. сообщения не отправляются в формате RAW, но у них есть Обрамление Данных. И от клиента к серверу также реализуйте маскировку данных на основе предоставленных 4 байтов в заголовке сообщения. Хотя от сервера к клиенту вам не нужно применять маскировку данных. Читать раздел 5. Кадрирование Данных в спецификации. Вот copy-paste из моей собственной реализации. Он не готов к использованию кода и должен быть изменен, я публикую его только для того, чтобы дать представление и общую логику чтения/записи с помощью WebSocket обрамление. Перейти к этой ссылке.
  6. после реализации кадрирования убедитесь, что вы правильно получаете данные с помощью сокетов. Например, чтобы предотвратить слияние некоторых сообщений в одно, поскольку TCP по-прежнему является потоковым протоколом. Это означает, что вам нужно прочитать только определенное количество байтов. Длина сообщения всегда основана на заголовке и предоставленных деталях длины данных в заголовке. Поэтому, когда вы получаете данные из сокета, сначала получите 2 байта, получите детали из заголовка, основанного на спецификации кадрирования, затем, если маска предоставила еще 4 байта, а затем длину, которая может быть 1, 4 или 8 байтов на основе длины данных. И после данных оно само. После прочтения примените демаскировку, и ваши данные сообщения будут готовы к использованию.
  7. вы можете использовать некоторые Протокол, я рекомендую использовать JSON из-за экономии трафика и прост в использовании на стороне клиента в JavaScript. Для серверной части вы можете проверить некоторые Парсеры. Есть много им google может быть очень полезен.

реализация собственного протокола WebSockets определенно имеет некоторые преимущества и большой опыт, который вы получаете, а также контроль над протоколом. Но вы должны потратить некоторое время на это и убедиться, что реализация очень надежна.

в то же время вы можете посмотреть в готовые к использованию решения, которые google (снова) имеют достаточно.


(размещен ответ от имени ОП).

теперь я могу отправлять данные. Это моя новая версия программы благодаря вашим ответам и коду @Maksims Mihejevs.

сервер

using System;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static Socket serverSocket = new Socket(AddressFamily.InterNetwork, 
        SocketType.Stream, ProtocolType.IP);
        static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

        static void Main(string[] args)
        {            
            serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
            serverSocket.Listen(128);
            serverSocket.BeginAccept(null, 0, OnAccept, null);            
            Console.Read();
        }

        private static void OnAccept(IAsyncResult result)
        {
            byte[] buffer = new byte[1024];
            try
            {
                Socket client = null;
                string headerResponse = "";
                if (serverSocket != null && serverSocket.IsBound)
                {
                    client = serverSocket.EndAccept(result);
                    var i = client.Receive(buffer);
                    headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0,i);
                    // write received data to the console
                    Console.WriteLine(headerResponse);

                }
                if (client != null)
                {
                    /* Handshaking and managing ClientSocket */

                    var key = headerResponse.Replace("ey:", "`")
                              .Split('`')[1]                     // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
                              .Replace("\r", "").Split('\n')[0]  // dGhlIHNhbXBsZSBub25jZQ==
                              .Trim();

                    // key should now equal dGhlIHNhbXBsZSBub25jZQ==
                    var test1 = AcceptKey(ref key);

                    var newLine = "\r\n";

                    var response = "HTTP/1.1 101 Switching Protocols" + newLine
                         + "Upgrade: websocket" + newLine
                         + "Connection: Upgrade" + newLine
                         + "Sec-WebSocket-Accept: " + test1 + newLine + newLine
                         //+ "Sec-WebSocket-Protocol: chat, superchat" + newLine
                         //+ "Sec-WebSocket-Version: 13" + newLine
                         ;

                    // which one should I use? none of them fires the onopen method
                    client.Send(System.Text.Encoding.UTF8.GetBytes(response));

                    var i = client.Receive(buffer); // wait for client to send a message

                    // once the message is received decode it in different formats
                    Console.WriteLine(Convert.ToBase64String(buffer).Substring(0, i));                    

                    Console.WriteLine("\n\nPress enter to send data to client");
                    Console.Read();

                    var subA = SubArray<byte>(buffer, 0, i);
                    client.Send(subA);
                    Thread.Sleep(10000);//wait for message to be send


                }
            }
            catch (SocketException exception)
            {
                throw exception;
            }
            finally
            {
                if (serverSocket != null && serverSocket.IsBound)
                {
                    serverSocket.BeginAccept(null, 0, OnAccept, null);
                }
            }
        }

        public static T[] SubArray<T>(T[] data, int index, int length)
        {
            T[] result = new T[length];
            Array.Copy(data, index, result, 0, length);
            return result;
        }

        private static string AcceptKey(ref string key)
        {
            string longKey = key + guid;
            byte[] hashBytes = ComputeHash(longKey);
            return Convert.ToBase64String(hashBytes);
        }

        static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
        private static byte[] ComputeHash(string str)
        {
            return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
        }
    }
}

JavaScript:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
        function connect() {
            var ws = new WebSocket("ws://localhost:8080/service");
            ws.onopen = function () {
                alert("About to send data");
                ws.send("Hello World"); // I WANT TO SEND THIS MESSAGE TO THE SERVER!!!!!!!!
                alert("Message sent!");
            };

            ws.onmessage = function (evt) {
                alert("About to receive data");
                var received_msg = evt.data;
                alert("Message received = "+received_msg);
            };
            ws.onclose = function () {
                // websocket is closed.
                alert("Connection is closed...");
            };
        };


    </script>
</head>
<body style="font-size:xx-large" >
    <div>
    <a href="#" onclick="connect()">Click here to start</a></div>
</body>
</html>

когда я запускаю этот код, я могу отправлять и получать данные как от клиента, так и от сервера. Единственная проблема заключается в том, что сообщения шифруются, когда они приходят на сервер. Вот шаги того, как программа работает:

enter image description here

обратите внимание, как зашифровано сообщение от клиента.


WebSockets являются реализовано с протоколом, которая включает рукопожатия между клиентом и сервером. Не думаю, что они работают как обычные розетки. Прочтите протокол и попросите ваше заявление об этом. Кроме того, использовать существующую библиотеку с WebSocket, или .Серии net4.5beta, который имеет WebSocket API.


вопрос

поскольку вы используете WebSocket, spender является правильным. После получения исходных данных из WebSocket, вам необходимо отправить сообщение рукопожатия с сервера C#, прежде чем любая дополнительная информация может течь.

HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: websocket
Connection: Upgrade
WebSocket-Origin: example
WebSocket-Location: something.here
WebSocket-Protocol: 13

что-то в этом роде.

вы можете сделать еще несколько исследований о том, как WebSocket работает на w3 или google.

ссылки и ресурсы

вот спецификация протокола: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3

список рабочих примеры: