紹介
以前のHTTPプロトコルでは、インスタントメッセージのプッシュを実装したい場合、2つの方法しかありませんでした:
- ajaxポーリング手法: クライアントは指定された間隔で毎回リクエストを送信し、サーバーに新しいデータを要求します。
- ロング・ポーリング ロング・ポーリング手法: クライアントはサーバにリクエストを一度だけ送信し、サーバからデータが返されるまでペディング・ブロック状態を維持します。
これらの方法はどちらも非常にシンプルですが、唯一の欠点は、HTTP接続を確立し、サーバから受動的にデータを受け取ることしかできないということです。メッセージがプッシュされるのを待っている何千人ものユーザーがいて、突然サーバーが圧倒されてストライキを起こしたとしたら、恥ずかしいことです。
WebSocketは、HTTP5とその出現と一緒に、そのようなシナリオのために設計されており、実際には、HTTPプロトコルとは何の関係もありません、それはWebSocketは、クライアントとサーバーが全二重プロトコルに長い接続を行うための、ブランドの新しいと言うことができます。
WebSocketは、クライアントとサーバーが接続を確立するために一度だけ確立する必要がありますが、それはTCPプロトコルに基づいて構築されているため、それはまだ本質的に3つのハンドシェイクです。
TCP自体は持続的な接続であり、3回のハンドシェイクや4回のハンドシェイクは古くなりません。HTTPは単方向性で、サーバーはリクエストに応答するだけで、積極的にデータを送信することはできません。つまり、WebSocketはHTTPのパッチと言えます。
プロトコルの概要
WebSocketプロトコルはエクスプローラでこのように表示されます:
通常のリクエストとは異なり、WebSocket URL の先頭に ws が付いていることがわかります。これは HTTP リクエストではなく WebSocket リクエストであることをブラウザに伝えるもので、この時点でブラウザは自動的にプロトコルをアップグレードします。
デフォルトの ws ポートは次のとおりです。
wss は TLS で暗号化された ws です。
通常の HTTP リクエストとは異なり、WebSocket リクエストはアプリケーションにいくつかのフィールドを追加します:
Sec-WebSocket-AcceptSec-WebSocket-AcceptSec-WebSocket-Key: Sec-WebSocket-Key の値が固定アルゴリズムで暗号化され、応答ヘッダーの値が同じままであるときのみ、接続は確立されたと認識され、クロスプロトコル攻撃を回避します。Sec-WebSocket-VersionSec-WebSocket-Version: このヘッダーフィールドの値は13でなければなりません。なぜなら、9、 10、11、12のような多くのテストされたバージョンが存在し、それらは現在 有効ではないと考えられているからです。Sec-WebSocket-Extensions: この属性は、接続が確立されたときにサーバーが処理できるクライアント側の 拡張を格納します。- Upgrade: この HTTP プロトコルが WebSocket にアップグレードされたことを伝えます。
クライアントがサーバに対して WebSocket リクエストを開始するとき、現在の接続がすでに確立されている場合にのみ、接続を再確立できます。WebSocket は長い接続であるため、クライアントは大量の WebSocket 接続を作成するスクリプトによる DDOS 攻撃を避けるために、同じホストへの接続数を制限するよう注意する必要があります。クライアントがプロキシ経由でサービスにアクセスしている場合、クライアントはそのプロキシに接続し、そのプロキシ経由でサーバとの TCP 接続を確立する必要があります。
Sec-WebSocket-VersionSec-WebSocket-Extensionsサーバはクライアントの WebSocket リクエストを受信すると、リクエストを解析して Sec-WebSocket-Key、クライアントの送信元アドレス、要求されたリソースの名前などを取得する必要があります。Sec-WebSocket-Accept構文解析が完了すると、サーバに接続できる場合、サーバはクライアントに応答を返し、応答にはクライアント接続の識別子である , が含まれます。
クライアントとサーバーが接続を確立すると、両者は通信できるようになります:
プロトコルの構造
WebSocket プロトコルのグローバル構造は、各フィールドの意味を大まかに解析すると以下のようになります:
FIN: これがメッセージの最後のフィールドであることを示します。
RESV1/RESV2/RESV3:拡張プロトコルがあるかどうかを識別します。これが 1 の場合、EXTEND PAYLOAD が 0 であれば、WebSocket 接続は切断されます。
OPCODE: WebSocket のアクションを示すアクションフレームであるオペコードを識別します。デフォルトの識別コードは以下のとおりです:
- x0 : 連続メッセージ・スライス
- x1 : テキスト・ベースのメッセージ・スライス。
- x2 : バイナリ・タイプのメッセージ・スライス。
- x3-7 : 後で使用するために予約されたデータ・フレーム。
- x8 : 接続を閉じるコマンド。
- %x9 pingパケット
- xA: pong パケット
- xB-F: 後で使うために予約された制御フレーム。
xB-F:後で使用するために予約されている制御フレーム。
MASK: データがマスクされているかどうかを識別します。1 に設定されている場合、マスク・キーは MASKING KEY 領域に配置する必要があります。
PAYED LENGTH:送信データの長さ。
MASKING-KEY: マスキングキー。
- EXTENDED DATA:自分で定義した拡張プロトコル。
- APPLICATION DATA:ベース・データ・フレーム。
フロントエンド処理
HTML5は処理をカプセル化し、APIを呼び出すだけです。
WebSocket オブジェクトを直接作成し、onopenonclose などのメソッドをオブジェクトにバインドします。
関連する API はMDN WEB - API WebSocketで見ることができます。
バックエンド処理
WebSocketServerProtocolHandlerNetty を例に挙げると、WebSocket サービスを作成する場合、プロトコルの内容を使いやすいラッパークラスにカプセル化する WebSocket プロトコル用のプロセッサを含めることは避けられません:
WebSocketServerProtocolHandlerこのクラスに入ると、プロパティが定義されていることがわかります:
WebSocketServerProtocolHandshakeHandlerひとつは handlerAdded で、チャネルが接続されるたびにコールバックされます。
1つはdecodeで、実行されたデータ・フレームを操作します。
closeの前にframe.retrieve();を実行するのは、フレームがcloseに必要だからであり、すべてのNetty操作が非同期であるため、フレームが使い切られずに解放されるのを防ぐためです。
メソッドを見てみましょう:
WebSocketServerProtocolHandshakeHandler.obj;でトリガーイベントをバインドする際、互換性を保つために2つ設定されています。
WebSocketServerProtocolHandshakeHandler.foo;Sec-WebSocket-Acceptハンドシェイカーファクトリーを通して作成された、ハンドシェイクメソッドに注目する必要があります。このメソッドは、実際には応答データを送信するためにあります。
まず HTTP の集約と圧縮のためのプロセッサを削除し、次に HttpRequestDecoder があるかどうかを調べ、なければ WebSocket コーデックをその前に追加します。HTTP コーデックがある場合は、コーデックを WebSocket コーデックに置き換え、レスポンスの送信が成功したら、HttpServerCodec または HttpResponseEncoder を削除します。
この処理の後、HTTPコーデックが削除されます。このようにすることで、ユーザーが間違ったプロセッサを追加しても、プログラムがWebSocket接続を正常に実行できるようになります。
まとめ
WebSocketプロトコルは、長い接続を介してデータを転送するために使用されますが、本質はプロトコル形式を定義し、その中にデータを置くことです。機能的には、HTTPとWebSocketの唯一の違いは、クライアントとサーバーが受動的ではなく、お互いにメッセージをプッシュできることです。
HTTP 1.1 では keep-alive リクエストヘッダ属性が追加されました。keep-alive の役割は接続を維持することで、他の HTTP リクエストがチャネルを再利用できるようにします。各 HTTP リクエストはリクエストヘッダを保持しますが、WebSocket のロングコネクションでは各コネクションに 1 つのクライアントが存在します。
わかりやすい例えに、カスタマーサービスに電話をかけるとします。キープアライブとは、一方の当事者が話し終えてから後ろの人に電話を渡し、後ろの人がカスタマーサービスに新しい問題を反映させることを意味します。WebSocketとは、一方の当事者が話し終え、カスタマーサービスからのフィードバックを聞いてから電話を切ることを意味し、2人は切断されます。
WebSocketプロトコルの詳細を理解したい場合は、ここにお勧めの記事です:





