迅速に知ってもらう|のWebSocketプロトコルは、詳細かつ実践

アウトライン

のみに滞在するのWebSocketプロトコル长连接の?WebSokcet契約のために特別にどのようにそれを達成するには?密かに私たちの背中と後ろのブラウザが何をやっていましたか?

以来練習はプロトコルをテストするための唯一の基準であるので、これは設計用WebSocketプロトコルを学ぶために、クライアントとサーバーのデータ転送時間になります。

文書用WebSocketプロトコルに基づいて、RFC6455のからの要約(ギャングスター翻訳した中国語版を

あなたはTCPプロトコルの十分な理解がない場合はヒント、それを読むことをお勧めしますTCPプロトコル冊子を

このホワイトペーパーでは、すでにのWebSocketを使用する方法を知っているプリセット

ビルドにテスト環境

  • オペレーティングシステムBug10
  • ソフトウェアWireSharkキャプチャ用
  • 地元のテストのために良いのWebSocketサーバーとクライアントを書きます

WireSharkの設定

我々は、すべてのWireSharkのクロールネットワークパケットに使用することができます知っているように、我々はのWebSocketプロトコルサーバとクライアントを使用してプロセスを分析するためにWireSharkのを使用できるように、広く、セキュリティおよびテストの分野に適用され、密かに何かを言いました。

最初のステップは、あなたが聴きたいカードを選択します

私たちは地元のテスト環境、そのアドレス私たちの訪問を使用しているのでlocalhost、私たちは図の赤いボックスにマークカードでローカルループネットワーク、に耳を傾ける必要があります。

かかわらず、フィルタリングデータの第2ステップ、

提供される、以下に示すようにtcp.port == 8080のみデータポート8080傍受我々を示し、また、再び後述

WebSocketプロトコルは、詳細な

ハンドシェイクハンドシェイクプロトコルを - オープニング

クライアントがサーバーとのWebSocket接続を確立したい場合は、以下のプロセスを経る必要があります

0x01のクライアントが要求ハンドシェイクを作ります

クライアントとサーバ間のTCPコネクションを確立した後、クライアントは、HTTPメッセージの形でサーバにハンドシェイク要求を送信し、HTTPパケット以下の要件を満たす必要があります。

  • HTTPパケット合法的で、そして道を要求しなければなりませんGET

  • HTTPパケットヘッダは、これはのWebSocketハンドシェイク要求されたマークするために、次のメッセージを含んでいなければなりません

Upgrade: websocket
Connection: Upgrade
复制代码
  • HTTPパケットのメッセージヘッダ含んでいなければならないSec-WebSocket-KeyのWebSocketプロトコルは、主に乱用を防止するためにチェックするために使用することがフィールドを、このフィールドは、一度だけ発生することができ、その値のアルゴリズムの詳細については後述します

  • ブラウザからの要求した場合、HTTPパケットヘッダのメッセージ含まれている必要がありOriginフィールドを、要求がこの分野の他の形態を含んでもよいです。

  • HTTPパケットヘッダは、メッセージに含まれていなければならないSec-WebSocket-Versionバージョン用WebSocketを示すために、その値がなければなりません13

  • HTTPパケットヘッダ含有することができるSec-WebSocket-Protocol実行するサブプロトコルクライアント希望を示すために、

  • HTTPパケットヘッダ含有することができるSec-WebSocket-Extensionsクライアントが所望の拡張を行うことを示しているに、(例えば、メッセージ圧縮プラグ)

  • HTTPパケットが他のメッセージヘッダを含めることができます

HTTPリクエストのハンドシェイククライアント後にサーバーが応答を返す前に、クライアントがサーバーにデータを送信することはできません

0x02のサーバの応答

サーバーはWebSocketの接続を確立するために、クライアントからのハンドシェイク要求を受信することを選択した場合、サーバーはクライアントとの次を完了する必要があります。

クライアントのハンドシェイク要求はRFC6455の定義を満たしていることを、クライアントからのハンドシェイクの要求が正当であると仮定

1、クライアントへのサーバーは、彼らがのWebSocketプロトコルを処理することを証明しなければなりません。証明は、次の

Base64で暗号化を使用してクライアントで16バイトのランダムに生成されたバイト配列のサイズ、バイト配列、暗号化された文字列を移入有し得Sec-WebSocket-Key、図1に示すように、フィールド。

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
复制代码

WebSocketプロトコルを処理できるサーバーを証明するために、する必要があるSec-WebSocket-Key値が取られ、次の操作を行いdGhlIHNhbXBsZSBub25jZQ==魔法の文字列文字列、258EAFA5-E914-47DA-95CA-C5AB0DC85B11接続、取得した次の文字列は、

dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11

図に示すように、SHA-1アルゴリズムは、上記の文字列の派生したハッシュ値(バイト配列)を行います。

0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea
复制代码

バイト配列の上にBase64で暗号化アルゴリズムのドローストリングを実行するためにs3pPLMBiTxaQ9kYGzzhZRbK+xOo=、その後、ハンドシェイク応答の値の書き込みSec-WebSocket-Acceptあなたが反応して、次のフィールドが表示されます意味のフィールドを、

Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
复制代码

上記からRFC6455ドキュメント。あなたは(そのような公共の数字の文字など)は、サードパーティのインターフェースと呼ばれてきた場合、それは一種デジャヴのではないでしょうか?

クライアントは、サーバーが正しくのWebSocketプロトコルを扱うことができるかどうかを確認するためには、この値をチェックします。

私たちは、Webアプリケーションコンテナ/フレームワークは、彼らがこの分野に対処する方法を見ることができます

一人のプレイヤー桟橋

主要なクラスのハンドシェイクプロトコル処理org.eclipse.jetty.websocket.server.HandshakeRFC6455

public class HandshakeRFC6455 implements WebSocketHandshake
{
    /**
     * RFC 6455 - Sec-WebSocket-Version
     * 验证了上文的说法,即版本号必须为13
     * 没办法规矩是人定的,(ˉ▽ˉ;)...
     */
    public static final int VERSION = 13;

    @Override
    public void doHandshakeResponse(ServletUpgradeRequest request, ServletUpgradeResponse response) throws IOException
    {
        String key = request.getHeader("Sec-WebSocket-Key");
        if (key == null)
            throw new BadMessageException("Missing request header 'Sec-WebSocket-Key'");

        // build response
        response.setHeader("Upgrade", "WebSocket");
        response.addHeader("Connection", "Upgrade");
        //没错,关键类就在这儿 AcceptHash.hashKey(key)
        response.addHeader("Sec-WebSocket-Accept", AcceptHash.hashKey(key));

        request.complete();

        response.setStatusCode(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
        response.complete();
    }
}
复制代码

AcceptHash.hashKey(key)

public class AcceptHash
{
    //魔法值,注意其编码的方式
    private static final byte[] MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(StandardCharsets.ISO_8859_1);
    
    public static String hashKey(String key)
    {
        try
        {
            //使用SHA1求对应字符串的散列值
            MessageDigest md = MessageDigest.getInstance("SHA1");
            //获取Sec-WebSocket-Key
            md.update(key.getBytes(StandardCharsets.UTF_8));
            //此法相当于将两个字符串连接在一起
            md.update(MAGIC);
            //digest()会获取经过SHA1算法计算之后的字节数组
            return Base64.getEncoder().encodeToString(md.digest());
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }
}

复制代码

IIプレイヤーエクスプレス-WS

発現-WS依存WS、コードは図の中に配置されているのWebSocket、server.js

小さな問題を残して、JSコードに比べて、あなたはJavaのコーディング値は、文字列を指定した理由を知りたいですか?

図2は、プロセス用WebSocketプロトコルの後に、サーバは、クライアント、すなわち、処理からの要求にハンドシェイクの拡張処理の必要性を要求するSec-WebSocket-Extensionsフィールド。このフィールドは、クライアントが拡張子をロードするようにサーバーを望んでいることを示しているが、実際には、プラグインがロードされ、サーバー方、サーバはプラグインのリターンメッセージヘッダをサポートしていることを示します。

例えば、クライアントは、サーバーの負荷を期待して、以下のプラグイン拡張要求を送信permessage-deflateし、client_max_window_bitsプラグインを

Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
复制代码

サーバーのみをサポートしている場合permessage-deflate、プラグインを、サーバーが返されます

Sec-WebSocket-Extensions: permessage-deflate
复制代码

その後の通信プロセスでは、サービスとクライアントにのみロードされますpermessage-deflateプラグイン(このプラグインで、練習の過程で言及され、それが長い間私を悩まました)

3、 であること、処理するサブプロトコルのクライアント・サーバ・サポートに通知Sec-WebSocket-Protocolフィールドをし、サポートされているプロトコルのいずれかを選択してクライアントに返します。処理は、以下の

    var protocol = req.headers['sec-websocket-protocol'];
    //如果存在sec-websocket-protocol字段
    if (protocol) {
      //客户端传过来的所有的协议
      protocol = protocol.trim().split(/ *, */);

      //
      // Optionally call external protocol selection handler.
      // handleProtocols 为钩子函数,即如果定义了此函数则将协议的选择交给此函数处理
      if (this.options.handleProtocols) {
        protocol = this.options.handleProtocols(protocol, req);
      } else {
        //否则,就默认呗,还能咋样
        protocol = protocol[0];
      }
      //如果选择到了协议,就返回Sec-WebSocket-Protocol
      if (protocol) {
        headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
        ws.protocol = protocol;
      }
    }
复制代码

このフィールドは、開発者のための作戦のためのより多くの部屋を予約すると等価です。詳しい情報

また、サーバーの応答ものWebSocket接続が正常に確立されたことを示すために、次のフィールドと値を含める必要があります

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: WebSocket
复制代码

練習

  • WebSocketのサーバーとクライアントを書きます。
  • サーバはマシンがカードのために待機していない場合は、ローカルループネットワークをリッスンしている場合は、開いてWiresharkの
  • フィルタリングルール、スヌープ・ポートの値が、このテスト・サーバーのポート8080を設定します
tcp.port == 8080
复制代码

以下に示すように、WireSharkのWebSocketのプロトコルおよびハンドシェーク通信パケットをキャプチャされます。実際にはハンドシェイクプロトコルは、すなわち、サーバとクライアントは、彼らがデータ処理プロセスにどのようなアプローチのお互いを知らせることができ、交渉プロセスとして見ることができます。

1.クライアントは、ハンドシェイク要求を開始します

2.サーバー要求応答ハンドシェーク

示されるように、クライアントとのハンドシェイク後3プロトコルの切り替えは、合意がWebSocketのプロトコルに変換された場合、ケア赤いボックスの標準の内容は、クライアントに直ちにサーバ本実施例でデータを送信します

私は、あなたがデータを転送するための鍵のWebSocketプロトコルです内容が記さ最初の黄色のボックスの内容は、メモをとっていると考えています。これはまた、我々はChromeが私たちに基本的な内容をレンダリングしていないため、WireSharkのキャプチャを使用する必要が理由です、それだけでデータがサーバから返されたものを私たちに教えてくれます。

データ送信

本当になぜこのデザインのようなWebSocketのデータ転送プロトコルを理解するには、次の概念を知っている必要があります。

  • WebSocketプロトコルは、アプリケーション層プロトコルTCPプロトコルであります
  • TCPはバイトストリームベースのトランスポートプロトコルであります
  • フローには境界がありません

我々は、境界データは、我々は完全なデータを取得するためにバイトストリームを分割するために形成するもので知っていなければならないという別の方法が何であるかを知らなければならないことをコンセプト上記の手段。

以下のようなWebSocketは、データ通信プロトコルを定義します

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+
复制代码

それはを開始する前に必要であるpayload、我々は荷重伝達に持っていることを理解すべきであるペイロードデータを説明します。

次の表は、各フィールドのWebSocket種のために説明されています。

フィールド名 サイズ 効果
END 1位 フラグこのデータパケットは、メッセージの最後のパケットであります
RSV1、RSV2、RSV2 各フラグそれぞれ三の合計 実際の展開に関連する3つの主なフラグは以下の詳細を与えます
オペコード 4位 この負荷フラグデータパケットコンテンツサポートのそれが4桁を有しているので、タイプ(すなわちペイロードは、実用的な伝送とみなすことができる)ので、16種類、一般的に使用される0001、すなわち、テキスト、0002バイナリデータ
マスク 1位 このビットがマスキングキーが仕事である場合、この種は、マスク機能は、実際の種に説明する具体的方法は、データパケットの負荷(ペイロード)マスク動作するかどうかを表します。
ペイロードLEN 7位 このフィールドは7したがって、このフィールドは、最大転送バイト数127を支持しているので、それは、負荷の長さを表します。
拡張ペイロード長 16ビットまたは64ビット 値はペイロードフィールドlenの場合126、ペイロードの全体の長さ7 + 16のビットをペイロードフィールドの値がlen場合、127次いで、ペイロード全体の長さであります7 + 64
マスキングキー 0または4バイト マスクビットが1で、このフィールドの関数である場合、このデータ・フィールドは、主に、マスキング動作を実行するために使用されます
ペイロード・データ Nバイト 送信すべきコンテンツデータパケット、およびペイロードによって定義された長さは、ペイロードLENを拡張LEN

今、あなたはのWebSocketプロトコルは国境がまだ流れて定義されている方法を知っていますか?

練習

学ぶための最善の方法は、オリノコを与え、練習に来ている! - トロル

テスト環境:バグ10 +クロム+桟橋

データを送信します

まず、春のニュース聞かせてjuejin,kesanサーバーに(長さ12)

以下の情報に注目

  • FIN = 1 これが最後のデータパケット/ので、何も間違ったFIN = 1であるので、我々は、完全に十分な送信されたメッセージのパケットを送信するので
  • RSV1 = 1, RSV2 = 0, RSV3 = 0我々は3つのフラグを言ったが、ビットWireSharkのこの時間はまた、我々は、関連する送信許可展開示唆上記でPre-Message Compressedプラグイン
  • OPCODE = 0001テキストメッセージの、ロード/伝送
  • Payload length = 14 ロード・コンテンツへのバイト数は、あなたがまだ問題を参照して、14のですか?
  • Masking-key = 7eca6052する負荷マスク7eca6052は通常マスクは、クライアントの中にサーバーにデータを送信して表示されます

カンカンコンテンツの送信は、あなたはまだ問題を見てみましょうか?

そのようにデータはどこにありますか?待って、あなたはマスクがZeyang除去した後、データマスクで見てみましょうを忘れてはいけません

正しいか間違って、正しいか間違ってマスクを除去した後、長さが適切ではない、隣に見Decompressed payloadて?行こうと見ます

最後に右!今、あなたは、伝送に何が起こったか知っていますか?一瞬考えてみましょう

RSV1 = 1説明クローム対応のプラグインして、最後にそれをプラグイン有効にどのような、メッセージの送信がでプラグインを有効になっているので、桟橋はまた、このプラグインをサポートしていますか?実際には、ハンドシェイクプロトコルは、ハンドシェイクプロトコルクライアントで実行し、サーバプラグインスルーを可能にするために協力してネゴシエートし、両方場合、上述されたSec-WebSocket-Extensions例示します。そしてプラグインが有効になっているpermessage-deflateメッセージを圧縮するために使用されるプラグイン、。

しかし、恥ずかしい、とメッセージ圧縮長くԾㅂԾをも動作になっていないよう。

私たちは別の方法で、髪1の多くのサーバにそれをテストし、何が起こるかを見ます

轟音ああ、この関数は、データ長が41バイト圧縮前のデータのみ5バイトを圧縮され、それが適用されないことがわかります。(このプラグインを達成するために、この記事の範囲内ではありません)

だから、私は希望カンカンのWebSocketのパケット圧縮は、我々が想定したデータを有効化されていませんか?

女王のバグ10の持ってくださいBug多PDF阅读器EPUB阅读器エッジブラウザの選手のデビューを

はい、エッジはサポートされていないpermessage-deflateデータ圧縮プラグインを

エッジ子供より悲惨な、彼の父親Microsoftは不思議がGoogleの隣のクロム自分の息子を誘拐しに、彼にプラグイン恐ろしいことを、明らかにしませんでした。

まあ、データを解凍することなく、限り、それはあなたが直接データを取得することができますマスクを除去見ることができるように、パケットのルックスが好きで、元のデータ、見てみましょう

マスクのみに4バイトのデータイソブチルマスクを除去する、または以下のコードを参照し、バイト4バイト少ない場合よりも4に分割する必要があります

    if (remaining >= 4 && (offset & 3) == 0)
    {
        payload.putInt(start, payload.getInt(start) ^ maskInt);
        start += 4;
        offset += 4;
    }
    else
    {
        payload.put(start, (byte)(payload.get(start) ^ maskBytes[offset & 3]));
        ++start;
        ++offset;
    }
复制代码
データを受信

以下に示すように、メッセージがクライアントに送信されず、サーバは、プラグマスク、従ってRSV1、RSV2を有効にしない、RSV3 = 0

もちろん、あなたは、ブラウザとサーバーが密かにテストプロセスでプレーしてきたでしょうpongpongpong以下に示すように、ゲームを

あなたは知恵のスタッフハートビート接続がわからない場合実際には、これはサービスとクライアントの演奏ハートビート接続があり、それが推奨される読書

概要

WebSocketプロトコルの詳細は言及されていないについて、彼らは私が(●∀●)あなたが本当に提案し合意のデザインのすべての詳細を理解したり、RFC6455の文書を読みたい場合は、すべての後に、一部の情報発信を通じてあるだろうことを学んだの話を歪みの程度。

私が学びましたか?

  • それは言葉である流是没有边界(マスター張さんからパンフレットあなたはときに彼は非常に迅速に理解、他のアプリケーション層プロトコルTCPトランスポートプロトコルの概念を理解すれば、)。

  • ソケットはまた、実質的にWebSocketを、AIOに適用されるすべての最適化手法に適用され、NIOなどのような

  • データ圧縮プラグイン本当に大丈夫を有効にしますか?必ずしもそうではありません、またはスコアは、データの圧縮や解凍にはあまりにも時間がかかるかどうか、すべての後に、やりがいのある依頼する必要があり、あなたは、特定のビジネスシナリオを見ています

  • ハッシュ関数に基づいて、本人確認の方法は、単に検証側チェック・リクエスタのために得られたハッシュ値の組み合わせの合意された形でパラメータのパイルと特定の文字列(キー、マジックナンバー)でありますアイデンティティ情報。

一部の人々はそれを見て、その後、他の分野でそれについて話を、私たちは見てする必要があります(^ _ ^

おすすめ

転載: juejin.im/post/5dce168af265da0bdc4c19d2