Створення прикладу WebSocket “Hello World”


85

Я не розумію, чому я не можу змусити наступний код працювати. Я хочу підключитися до 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, однак, не може надсилати дані. Кожного разу, коли я розміщую метод відправки, він не надсилатиметься, навіть якщо встановлено з'єднання. Як я можу зробити цю роботу?


9
Здається, це "питання" вже не є питанням, і, отже, насправді не підходить для формату StackOverflow. FWIW, повідомлення клієнта не зашифровано, воно замасковане (затуманене) XOR'ом проти випадкового значення, яке передається як частина кадру. Ця деталь протоколу існує, щоб уникнути атак отруєння проксі-серверами, які можуть неправильно сприймати трафік.
EricLaw

1
дякую, ця відповідь дуже корисна :) привіт, лише одне, це "статичний приватний рядок GUID =" 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 ";" річ завжди постійна? якщо ні, де я можу отримати ці значення?
Charmie

1
я отримав таке: "Сервер не повинен маскувати будь-які кадри, які він надсилає клієнту"
Charmie

5
Ймовірно, вам слід було залишити вихідне запитання цілим. Мета питань - представити проблему, а не рішення. Відповіді, як ви могли здогадатися, стосуються рішень.
Kehlan Krumme

1
Чому URL-адреса WebSocket закінчується на '/ service' (ws: // localhost: 8080 / service)? Чому не просто 'ws: // localhost: 8080'?
andree

Відповіді:


72

WebSockets - це протокол, який покладається на потокове з'єднання TCP. Хоча WebSockets - це протокол на основі повідомлень.

Якщо ви хочете реалізувати власний протокол, то я рекомендую використовувати останню і стабільну специфікацію (на 18.04.12) RFC 6455 . Ця специфікація містить всю необхідну інформацію щодо рукостискання та обрамлення. Як і більшість описів сценаріїв поведінки як з боку браузера, так і з боку сервера. Настійно рекомендуємо дотримуватись рекомендацій щодо сторони сервера під час впровадження вашого коду.

Кілька слів я б описав роботу з WebSockets таким чином:

  1. Створіть серверний сокет (System.Net.Sockets), прив'язуючи його до певного порту, і продовжуйте слухати з асинхронним прийняттям з'єднань. Щось таке:

    Socket serverSocket = новий сокет (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    serverSocket.Bind (новий IPEndPoint (IPAddress.Any, 8080));
    serverSocket.Listen (128);
    serverSocket.BeginAccept (null, 0, OnAccept, null);
  2. Ви повинні мати прийнятну функцію "OnAccept", яка реалізує рукостискання. Надалі це має бути в іншому потоці, якщо система призначена для обробки величезної кількості з'єднань в секунду.

    приватна порожнеча OnAccept (результат IAsyncResult) {
    спробуй {
        Клієнт сокету = null;
        if (serverSocket! = null && serverSocket.IsBound) {
            client = serverSocket.EndAccept (результат);
        }
        if (client! = null) {
            / * Встановлення зв'язку та управління ClientSocket * /
        }
    } catch (виняток SocketException) {
    
    } нарешті {
        if (serverSocket! = null && serverSocket.IsBound) {
            serverSocket.BeginAccept (null, 0, OnAccept, null);
        }
    }
    }
  3. Після встановлення зв’язку вам потрібно зробити рукостискання . Відповідно до специфікації 1.3 Відкриття рукостискання , після встановлення з'єднання ви отримаєте базовий HTTP-запит із деякою інформацією. Приклад:

    GET / чат HTTP / 1.1
    Ведучий: server.example.com
    Оновлення: websocket
    Підключення: оновлення
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ ==
    Походження: http://example.com
    Sec-WebSocket-Protocol: чат, суперчат
    Версія Sec-WebSocket: 13

    Цей приклад заснований на версії протоколу 13. Майте на увазі, що старіші версії мають деякі відмінності, але в основному останні версії перехресні. Різні браузери можуть надсилати вам додаткові дані. Наприклад, деталі браузера та ОС, кеш та інші.

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

    статичний приватний рядок guide = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    приватний рядок AcceptKey (посилальний ключ рядка) {
        рядок longKey = ключ + керівництво;
        SHA1 sha1 = SHA1CryptoServiceProvider.Create ();
        байт [] hashBytes = sha1.ComputeHash (System.Text.Encoding.ASCII.GetBytes (longKey));
        повернути Convert.ToBase64String (hashBytes);
    }
    

    Відповідь рукостискання виглядає так:

    HTTP / 1.1 101 Протоколи комутації
    Оновлення: websocket
    Підключення: оновлення
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK + xOo =

    Але ключ прийняття повинен бути згенерованим на основі наданого ключа від клієнта та методу AcceptKey, який я надав раніше. Крім того, переконайтеся, що після останнього символу клавіші прийняття ви поставили два нові рядки "\ r \ n \ r \ n".

  4. Після того, як відповідь на рукостискання надіслано з сервера, клієнт повинен запустити функцію " onopen ", це означає, що ви можете надсилати повідомлення після.
  5. Повідомлення не надсилаються у необробленому форматі, але вони мають фреймінг даних . І від клієнта до сервера, а також реалізуйте маскування даних на основі передбачених 4 байт в заголовку повідомлення. Хоча від сервера до клієнта не потрібно застосовувати маскування даних. Прочитайте розділ 5. Обрамлення даних у специфікації. Ось copy-paste з моєї власної реалізації. Це не готовий до використання код, і його потрібно модифікувати, я публікую його лише для того, щоб дати уявлення та загальну логіку читання / запису за допомогою кадрування WebSocket. Перейдіть за цим посиланням .
  6. Після реалізації кадру переконайтеся, що ви отримуєте дані правильно, використовуючи сокети. Наприклад, щоб запобігти об’єднанню деяких повідомлень в одне, оскільки TCP все ще є потоковим протоколом. Це означає, що вам потрібно прочитати ТІЛЬКИ певну кількість байтів. Довжина повідомлення завжди базується на заголовку та наданих деталях довжини даних у самому заголовку. Отже, отримуючи дані від Socket, спочатку отримайте 2 байти, отримайте деталі із заголовка на основі специфікації Framing, потім, якщо маска надала ще 4 байти, а потім довжину, яка може бути 1, 4 або 8 байтів залежно від довжини даних. А після даних він сам. Прочитавши, застосуйте демаскинг, і дані ваших повідомлень будуть готові до використання.
  7. Можливо, вам захочеться використовувати якийсь протокол даних , я рекомендую використовувати JSON через економію трафіку та простий у використанні на стороні клієнта в JavaScript. Для серверної сторони ви можете перевірити деякі аналізатори. Їх багато, Google може бути дуже корисним.

Впровадження власного протоколу WebSockets, безумовно, має певні переваги та великий досвід, який ви отримуєте, а також контроль над самим протоколом. Але вам доведеться витратити на це певний час і переконатися, що впровадження є надзвичайно надійним.

У той же час ви можете переглянути готові до використання рішення, яких Google (знову ж таки) вистачає.


Я думаю, я застряг на рукостисканні. при отриманні нового з'єднання я повинен відправити клієнту хеш-код sha1 довгого ключа плюс короткого ключа, так?
Тоно Нам,

Я додав більше інформації в розділ 3. Він описує більше деталей щодо рукостискання з боку сервера.
moka

1
А також переконайтеся, що якщо в запиті надані протоколи, використовуйте їх у відповідь для рядка Sec-WebSocket-Protocol. Але лише за умови наявності в запиті. Також вам не потрібна версія для відповіді. І додайте ще один NewLine до кінця. А також відправити цілий рядок відповіді, закодований за допомогою UTF8: Encoding.UTF8.GetBytes (responseBytes)
мока

Ми поруч. Велике спасибі за допомогу. Я можу надіслати повідомлення зараз, але я вважаю, що воно зашифровано. Погляньте на мою редакцію, над якою я незабаром почну працювати ...
Тоно Нам,

Вам потрібно застосувати фреймінг даних, це, я вважаю, найскладніший біт у реалізації протоколу WebSockets. Я додаю код копіювання та вставлення з моєї реалізації в пост, але обов’язково редагуйте його, оскільки він має щось змінити, але загалом дає уявлення та логіку роботи з фреймом.
moka

9

(Опублікована відповідь від імені ОП) .

Я можу надіслати дані зараз. Це моя нова версія програми завдяки вашим відповідям та коду @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>

Коли я запускаю цей код, я можу надсилати та отримувати дані як від клієнта, так і від сервера. Єдина проблема полягає в тому, що повідомлення зашифровуються, коли надходять на сервер. Ось кроки роботи програми:

введіть тут опис зображення

Зверніть увагу, як шифрується повідомлення від клієнта.


2
Щось не так з вашим прикладом. Коли я натискаю клавішу Enter, з’єднання переходить у закрите.
Річард Агірре

@RichardAguirre: Я розмістив це від імені ОП (див. Перший рядок відповіді) - наразі вони не отримають повідомлення про ваше повідомлення. Ви можете спробувати пінгувати їх під питанням, але їм, швидше за все, знадобиться набагато більше деталей, ніж це.
половинка

6

WebSockets реалізовані за допомогою протоколу, який передбачає рукостискання між клієнтом та сервером . Не думаю, що вони працюють дуже як звичайні розетки. Ознайомтеся з протоколом і отримайте свою програму, щоб поговорити. Або скористайтеся наявною бібліотекою WebSocket або .Net4.5beta, яка має API WebSocket .


Звичайно, вони працюють дуже як звичайні розетки. Сокет знаходиться на нижчому рівні, ніж прикладний протокол, і як такий не залежить від нього. Це означає, що ви можете запускати FTP, SMTP, HTTP, WebSockets тощо ... на сокеті. Реалізатор повинен переконатися, що вона правильно дотримується протоколу, інакше ніхто не зможе спілкуватися з сервером.
SRM

4

Я не міг знайти простий робочий приклад ніде (станом на 19 січня), тому ось оновлена ​​версія. У мене є хромована версія 71.0.3578.98.

Сервер C # Websocket:

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

namespace WebSocketServer
{
    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(1); //just one socket
        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);
                Console.WriteLine("=====================");
            }
            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
                     ;

                client.Send(System.Text.Encoding.UTF8.GetBytes(response));
                var i = client.Receive(buffer); // wait for client to send a message
                string browserSent = GetDecodedData(buffer, i);
                Console.WriteLine("BrowserSent: " + browserSent);

                Console.WriteLine("=====================");
                //now send message to client
                client.Send(GetFrameFromString("This is message from server to client."));
                System.Threading.Thread.Sleep(10000);//wait for message to be sent
            }
        }
        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));
    }

    //Needed to decode frame
    public static string GetDecodedData(byte[] buffer, int length)
    {
        byte b = buffer[1];
        int dataLength = 0;
        int totalLength = 0;
        int keyIndex = 0;

        if (b - 128 <= 125)
        {
            dataLength = b - 128;
            keyIndex = 2;
            totalLength = dataLength + 6;
        }

        if (b - 128 == 126)
        {
            dataLength = BitConverter.ToInt16(new byte[] { buffer[3], buffer[2] }, 0);
            keyIndex = 4;
            totalLength = dataLength + 8;
        }

        if (b - 128 == 127)
        {
            dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);
            keyIndex = 10;
            totalLength = dataLength + 14;
        }

        if (totalLength > length)
            throw new Exception("The buffer length is small than the data length");

        byte[] key = new byte[] { buffer[keyIndex], buffer[keyIndex + 1], buffer[keyIndex + 2], buffer[keyIndex + 3] };

        int dataIndex = keyIndex + 4;
        int count = 0;
        for (int i = dataIndex; i < totalLength; i++)
        {
            buffer[i] = (byte)(buffer[i] ^ key[count % 4]);
            count++;
        }

        return Encoding.ASCII.GetString(buffer, dataIndex, dataLength);
    }

    //function to create  frames to send to client 
    /// <summary>
    /// Enum for opcode types
    /// </summary>
    public enum EOpcodeType
    {
        /* Denotes a continuation code */
        Fragment = 0,

        /* Denotes a text code */
        Text = 1,

        /* Denotes a binary code */
        Binary = 2,

        /* Denotes a closed connection */
        ClosedConnection = 8,

        /* Denotes a ping*/
        Ping = 9,

        /* Denotes a pong */
        Pong = 10
    }

    /// <summary>Gets an encoded websocket frame to send to a client from a string</summary>
    /// <param name="Message">The message to encode into the frame</param>
    /// <param name="Opcode">The opcode of the frame</param>
    /// <returns>Byte array in form of a websocket frame</returns>
    public static byte[] GetFrameFromString(string Message, EOpcodeType Opcode = EOpcodeType.Text)
    {
        byte[] response;
        byte[] bytesRaw = Encoding.Default.GetBytes(Message);
        byte[] frame = new byte[10];

        int indexStartRawData = -1;
        int length = bytesRaw.Length;

        frame[0] = (byte)(128 + (int)Opcode);
        if (length <= 125)
        {
            frame[1] = (byte)length;
            indexStartRawData = 2;
        }
        else if (length >= 126 && length <= 65535)
        {
            frame[1] = (byte)126;
            frame[2] = (byte)((length >> 8) & 255);
            frame[3] = (byte)(length & 255);
            indexStartRawData = 4;
        }
        else
        {
            frame[1] = (byte)127;
            frame[2] = (byte)((length >> 56) & 255);
            frame[3] = (byte)((length >> 48) & 255);
            frame[4] = (byte)((length >> 40) & 255);
            frame[5] = (byte)((length >> 32) & 255);
            frame[6] = (byte)((length >> 24) & 255);
            frame[7] = (byte)((length >> 16) & 255);
            frame[8] = (byte)((length >> 8) & 255);
            frame[9] = (byte)(length & 255);

            indexStartRawData = 10;
        }

        response = new byte[indexStartRawData + length];

        int i, reponseIdx = 0;

        //Add the frame bytes to the reponse
        for (i = 0; i < indexStartRawData; i++)
        {
            response[reponseIdx] = frame[i];
            reponseIdx++;
        }

        //Add the data bytes to the response
        for (i = 0; i < length; i++)
        {
            response[reponseIdx] = bytesRaw[i];
            reponseIdx++;
        }

        return response;
    }
}
}

Клієнт html та 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">
        var socket = new WebSocket('ws://localhost:8080/websession');
        socket.onopen = function() {
           // alert('handshake successfully established. May send data now...');
		   socket.send("Hi there from browser.");
        };
		socket.onmessage = function (evt) {
                //alert("About to receive data");
                var received_msg = evt.data;
                alert("Message received = "+received_msg);
            };
        socket.onclose = function() {
            alert('connection closed');
        };
    </script>
</head>
<body>
</body>
</html>


3

Проблема

Оскільки ви використовуєте WebSocket, витратник правильний. Отримавши початкові дані від 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

Список робочих прикладів:


Також я використовую кодування UTF8. Чи варто використовувати інший, такий як ASCII?
Тоно Нам,

@TonoNam: Наскільки мені відомо, кодування UTF8 є правильним - хоча я не фахівець з HTML5, тому я точно не знаю.
cesay

Я змусив це працювати з сафарі !!! Мені він потрібен, щоб він працював із google chrome. Всі приклади з'єднуються, але жоден з них не надсилає дані. Я продовжуватиму намагатися. Велике спасибі за допомогу!
Тоно Нам,

Звичайно .... Якщо я все ще не змушу його працювати, я зроблю це питання щедрим! Мені справді цікаво бачити, як це працює. Дякую за допомогу
Тоно Нам

Ваше посилання на специфікацію протоколу застаріло. Safari все ще використовує це, але інші браузери перейшли на несумісний RFC 6455 . Крім того, незважаючи на те, що ви праві, що початкове з’єднання вимагає певних переговорів, подальші повідомлення цього не роблять.
simonc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.