「HelloWorld」WebSocketの例の作成


86

次のコードが機能しない理由がわかりません。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はデータを送信できません。sendメソッドを配置すると、接続が確立されても送信されません。どうすればこれを機能させることができますか?


10
この「質問」はもはや質問ではないように思われるため、StackOverflowの形式にはあまり適していません。FWIW、クライアントのメッセージは暗号化されておらず、フレームの一部として送信されるランダムな値に対してXORによってマスク(難読化)されています。このプロトコルの詳細は、トラフィックを誤解する可能性のあるプロキシサーバーに対するポイズニング攻撃を回避するために存在します。
EricLaw 2012

2
おかげで、この答えは非常に役立ちます:)ねえ、ただ1つのことは、この「静的プライベート文字列guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";」です。物事は常に一定ですか?そうでない場合、これらの値はどこで入手できますか?
チャーミー2013年

2
私はこれを手に入れました:「サーバーはクライアントに送信するフレームをマスクしてはいけません」
Charmie 2013年

6
元の質問はそのままにしておくべきだったでしょう。質問の目的は、解決策ではなく、問題を提示することです。ご想像のとおり、答えは解決策です。
Kehlan Krumme 2013年

2
WebSocketのURLが「/ service」(ws:// localhost:8080 / service)で終わるのはなぜですか?'ws:// localhost:8080'だけではないのはなぜですか?
andree 2014年

回答:


72

WebSocketsは、TCPストリーミング接続に依存するプロトコルです。WebSocketsはメッセージベースのプロトコルですが。

あなたがあなた自身のプロトコルを実装したい場合、私は(18/04/12用)の最新の安定した仕様を使用することをお勧めしますRFC 6455。この仕様には、ハンドシェイクとフレーミングに関して必要なすべての情報が含まれています。同様に、ブラウザ側およびサーバー側から動作するシナリオに関する説明のほとんど。コードの実装中は、サーバー側に関する推奨事項に従うことを強くお勧めします。

簡単に言うと、WebSocketの操作について次のように説明します。

  1. サーバーソケット(System.Net.Sockets)を作成して、それを特定のポートにバインドし、接続の非同期受け入れでリッスンし続けます。そんな感じ:

    ソケット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. ハンドシェイクを実装する受け入れ関数「OnAccept」が必要です。システムが1秒あたりの大量の接続を処理することを意図している場合、将来的には別のスレッドに配置する必要があります。

    private void OnAccept(IAsyncResult result){
    {を試してください
        ソケットクライアント= null;
        if(serverSocket!= null && serverSocket.IsBound){
            client = serverSocket.EndAccept(result);
        }
        if(client!= null){
            / * ClientSocketのハンドシェイクと管理* /
        }
    } catch(SocketException例外){
    
    } 最終的に {
        if(serverSocket!= null && serverSocket.IsBound){
            serverSocket.BeginAccept(null、0、OnAccept、null);
        }
    }
    }
  3. 接続が確立されたら、ハンドシェイクを行う必要があります。仕様1.3Opening Handshakeに基づいて、接続が確立された後、いくつかの情報を含む基本的なHTTP要求を受け取ります。例:

    GET / chat HTTP / 1.1
    ホスト:server.example.com
    アップグレード:websocket
    接続:アップグレード
    Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ ==
    起源:http://example.com
    Sec-WebSocket-プロトコル:チャット、スーパーチャット
    Sec-WebSocket-バージョン:13

    この例は、プロトコル13のバージョンに基づいています。古いバージョンにはいくつかの違いがありますが、ほとんどの最新バージョンは相互互換性があることに注意してください。ブラウザが異なれば、追加のデータが送信される場合があります。たとえば、ブラウザとOSの詳細、キャッシュなど。

    提供されたハンドシェイクの詳細に基づいて、回答行を生成する必要があります。それらはほとんど同じですが、提供されたSec-WebSocket-Keyに基づくAccpet-Keyが含まれます。仕様1.3では、応答キーの生成方法が明確に説明されています。これが私がV13で使用している私の関数です:

    static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    プライベート文字列AcceptKey(ref string key){
        文字列longKey = key + guid;
        SHA1 sha1 = SHA1CryptoServiceProvider.Create();
        byte [] hashBytes = sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(longKey));
        Convert.ToBase64String(hashBytes);を返します。
    }
    

    ハンドシェイクの答えは次のようになります。

    HTTP / 1.1101スイッチングプロトコル
    アップグレード:websocket
    接続:アップグレード
    Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK + xOo =

    ただし、acceptキーは、クライアントから提供されたキーと、前に提供したAcceptKeyメソッドに基づいて生成されたものである必要があります。同様に、acceptキーの最後の文字の後に、2つの新しい行「\ r \ n \ r \ n」を入力してください。

  4. サーバーからハンドシェイク応答が送信された後、クライアントは「onopen」機能をトリガーする必要があります。これは、後でメッセージを送信できることを意味します。
  5. メッセージは生の形式では送信されませんが、データフレーミングがあります。また、クライアントからサーバーへも、メッセージヘッダーで提供された4バイトに基づいてデータのマスキングを実装します。サーバー間ではありますが、データにマスキングを適用する必要はありません。セクション5をお読みください。仕様のデータフレーミング。これが私自身の実装からのコピー&ペーストです。これはすぐに使用できるコードではなく、変更する必要があります。WebSocketフレーミングを使用した読み取り/書き込みのアイデアと全体的なロジックを提供するためだけに投稿しています。このリンクに移動します。
  6. フレーミングが実装されたら、ソケットを使用して正しい方法でデータを受信することを確認してください。たとえば、TCPは依然としてストリームベースのプロトコルであるため、一部のメッセージが1つにマージされるのを防ぐためです。つまり、特定のバイト数のみを読み取る必要があります。メッセージの長さは常にヘッダーに基づいており、ヘッダー自体にデータ長の詳細が提供されます。したがって、Socketからデータを受信するときは、最初に2バイトを受信し、フレーミング仕様に基づいてヘッダーから詳細を取得します。次に、マスクがさらに4バイトを提供した場合、データの長さに基づいて1、4、または8バイトの長さになります。そしてデータの後、それは自己です。読んだら、マスキング解除を適用すると、メッセージデータを使用できるようになります。
  7. いくつかのデータプロトコルを使用することをお勧めします。トラフィックが節約され、JavaScriptのクライアント側で簡単に使用できるJSONを使用することをお勧めします。サーバー側では、いくつかのパーサーを確認することをお勧めします。それらはたくさんあります、グーグルは本当に役に立ちます。

独自のWebSocketプロトコルを実装することには、プロトコル自体を制御できるだけでなく、確かにいくつかの利点と優れた経験があります。ただし、それを行うにはある程度の時間を費やし、実装の信頼性が高いことを確認する必要があります。

同時に、グーグルが(再び)十分に持っているすぐに使えるソリューションを見るかもしれません。


私はハンドシェークで立ち往生していると思います。新しい接続を受信したら、長いキーと短いキーのsha1ハッシュをクライアントに送信する必要がありますか?
遠野ナム

セクション3で詳細を追加しました。サーバー側からのハンドシェイクに関する詳細について説明します。
moka 2012

1
また、要求に応じてプロトコルが提供されている場合は、Sec-WebSocket-Protocol行の応答でも同じものを使用するようにしてください。ただし、リクエストで提供された場合のみ。また、応答にバージョンは必要ありません。そして最後に別の改行を追加します。同様に、UTF8を使用してエンコードされた応答文字列全体を送信します:Encoding.UTF8.GetBytes(responseBytes)
moka 2012

私たちは親しい。助けてくれてありがとう。メッセージを送信できるようになりましたが、メッセージは暗号化されていると思います。...私はすぐに作業を開始することを、私の編集を見てみましょう
遠野ナム

データフレーミングを実装する必要があります。これは、WebSocketプロトコルの実装で最も複雑なビットになると思います。実装からpostにコピー&ペーストコードを追加しますが、変更することがいくつかあるため、必ず編集してください。ただし、全体として、フレームを操作するためのアイデアとロジックが提供されます。
moka 2012

10

(OPに代わって投稿された回答)

データを送信できるようになりました。これは、あなたの回答と@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>

そのコードを実行すると、クライアントとサーバーの両方からデータを送受信できます。唯一の問題は、メッセージがサーバーに到着したときに暗号化されることです。プログラムの実行手順は次のとおりです。

ここに画像の説明を入力してください

クライアントからのメッセージがどのように暗号化されているかに注意してください。


5

WebSocketは、クライアントとサーバー間のハンドシェイクを含むプロトコル実装されます。通常のソケットのように機能するとは思いません。プロトコルを読み、アプリケーションにそれを話してもらいます。または、既存のWebSocketライブラリ、またはWebSocketAPIを備えた.Net4.5betaを使用します


もちろん、それらは通常のソケットと非常によく似ています。ソケットはアプリケーションプロトコルよりも低いレベルにあるため、ソケットに依存していません。つまり、ソケットでFTP、SMTP、HTTP、WebSocketなどを実行できます。彼女がプロトコルに正しく従うことを確認するのは実装者の責任です。そうでない場合、誰もサーバーと通信できなくなります。
SRM

5

簡単な実例はどこにも見つかりませんでした(1月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でどのように機能するかについてさらに調査することができます。

リンクとリソース

ここでは、プロトコルspecifcationは次のとおりです。http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3

実例のリスト:


また、UTF8エンコーディングを使用しています。ASCIIなどの別のものを使用する必要がありますか?
遠野ナム

@TonoNam:私が知る限り、UTF8エンコーディングは正しいです-私はHTML5の専門家ではないので、はっきりとはわかりません。
caesay 2012

サファリで動作させました!!! 私はそれがグーグルクロームで動作するようにするためにそれが必要です。すべての例が接続されていますが、いずれもデータを正常に送信できません。頑張ります。助けてくれてありがとう!
遠野ナム

もちろん....それでもうまくいかない場合は、この質問を賞金稼ぎにします!私はこれが機能するのを見て本当に興味があります。助けてくれてありがとう
Tono Nam

プロトコル仕様のリンクが古くなっています。Safariは引き続きこれを使用しますが、他のブラウザは互換性のないRFC6455に移行しました。また、最初の接続にはある程度のネゴシエーションが必要であるというのは正しいですが、それ以降のメッセージでは必要ありません。
simonc 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.