WebSocketシリーズの基礎知識と設計思想
1. WebSocket の概要
WebSocket は、サーバーとの双方向セッションの作成を可能にする高度なテクノロジーです。この API を通じて、サーバーにデータをポーリングすることなく、サーバーにメッセージを送信し、イベント ドリブンの応答を受信できます。
双方向の会話とは、クライアントとサーバーの両方が WebSocket を介して相互にデータを送信できること、つまり、サーバーがクライアントにデータをプッシュでき、クライアントも WebSocket を介してデータを送信できることを意味します。たとえば、チャット ルーム機能の 1 つは典型的なシナリオです。
1. WebSocket を使用する理由
WebSocket を使用しない場合、長い接続を確立する必要がある場合は、いくつかの方法があります。
- 投票
- ロングポーリング (一般的に使用されます)
- SSE(サーバー送信イベント)
以下に、これらについて簡単に紹介します。
- ポーリング
ポーリングは、クライアントで長い接続をシミュレートするために使用される最も古い方法です。これは、 HTTP リクエストを定期的にサーバーに送信することで、クライアントがサーバーにデータを送信することをシミュレートし、クライアントが HTTP リクエストを送信した後にサーバーのデータを返します。
このソリューションでは、クライアントのデータをほぼリアルタイムで受信できますが、欠点も明らかです。クライアントのリクエストが返された後にサーバーのデータを戻す必要があります。HTTP リクエストの間隔が短すぎると、ネットワークのオーバーヘッドが大きくなり、間隔が長すぎると、データが適切なタイミングで配信されなくなります。
- ロングポーリング
ロングポーリングはポーリングの改良版です。クライアントが HTTP リクエストを送信し、サーバーがリクエストを受信すると、サーバーはまずリクエストを維持し、戻りません。特定の時間内 (HTTP 判定タイムアウトは通常 30 秒であるため、通常は 30 秒)、サーバーにデータがない場合はリクエストに応答し、サーバーに送信するデータがある場合はすぐにデータを送信します。 HTTP リクエストの応答を通じてクライアントに渡されます。応答を受信した後、クライアントはすぐに次の HTTP リクエストを開始します。
このソリューションは、ポーリング中にサーバー データが時間内に配信されないという問題を解決できますが、ネットワーク コストが高いという問題はまだ解決できません。
- SSE (サーバー送信イベント)
SSE は、サーバーからクライアントにデータをプッシュする新しいプロトコルです。カスタマイズされた SSE プロトコルを介した一方向のデータ プッシュを実装します。SSE の欠点は、データはサーバーからクライアントにのみ転送できるが、クライアントからサーバーにデータを転送できないことです。
2.WebSocket は次の問題を効果的に解決できます。
- 帯域幅の問題: HTTP と比較して、WebSocket のプロトコル ヘッダーは小さく、オンデマンドで配信されます。
- データのリアルタイム性の問題: ポーリングやロングポーリングと比較して、WebSocket は遅延が少なくリアルタイムにデータを送信できます。
- 状態の問題: HTTP のステートレス リクエストと比較して、WebSocket は接続の確立後に特定の状態を維持できます。
2. WebSocketプロトコルの内容
WebSocket プロトコルは HTTP プロトコルからアップグレードされます。WebSocket 接続を確立するには、HTTP プロトコルに基づいて 2 つのハンドシェイクを追加するだけで済みます (SSL 暗号化が必要な場合は、SSL ハンドシェイク プロセスも必要です)。以下では、次のヘッダー関連フィールドを簡単に紹介します。
1. リクエストヘッダー
リクエストヘッダーは次のとおりです。
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
で:
- ホスト:server.example.com : 接続するWebSocketアドレスを示します。
- 接続: アップグレード: HTTP 接続をアップグレードする必要があります。
- アップグレード: websocket : HTTP 接続を WebSocket 接続にアップグレードします。
- Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ== : クライアントによって生成された WebSocket 接続キー。
- Sec-WebSocket-Protocol: チャット、スーパーチャット: クライアントが受け入れられるプロトコルを指定します。
- Sec-WebSocket-Version: 13 : WebSocket のバージョン番号。
2. ヘッダーに応答する
応答ヘッダーは次のとおりです。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
で:
- アップグレード: websocket : HTTP 接続を WebSocket 接続にアップグレードすることを確認します。
-接続: アップグレード: HTTP 接続をアップグレードすることを確認します。 - Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo : クライアントの接続キーに基づいてサーバーによって生成されたサーバー キー。
- Sec-WebSocket-Protocol: chat : 選択された WebSocket プロトコル。
3. WebSocket API の概要
WebSocket プロトコルについて予備的な理解ができたので、具体的な使用シナリオで WebSocket を使用する方法を見てみましょう。
WebSocket の API は多くありませんが、使用順序に従ってみましょう。
- 接続を確立する
- 知らせを受け取りました
- メッセージを送ります
- 近い接続
1. 接続を確立する
WebSocket はインスタンスを初期化することで接続を確立し、openイベントのコールバック関数を使用して接続が正常に確立されたことを確認します。具体的な例は次のとおりです。
const webSocket = new WebSocket('ws://server.example.com');
webSocket.addEventListener('open', (event) => {
// 建立连接成功
});
WebSocket との ws 接続を確立する場合、URL はドメイン名または IP アドレスにすることができますが、確立された接続が wss (暗号化された WebSocket) の場合は、対応する証明書を構成する必要があるため、URL はドメイン名である必要があります。ドメイン名に固有のものです。
2. メッセージを受信する
WebSocket は、メッセージイベントを通じてメッセージを受信します。
socket.addEventListener('message', function (event) {
console.log('Message from server', event.data);
});
WebSocket は、 String、ArrayBuffer、Blobの 3 つのデータ型を渡すことができるため、メッセージを受信するときは、これらのいずれかになります。この中で最もよく使われるのはStringとArrayBufferです。
- String型の場合、JSON やその他の形式などの文字列処理関数を通じて、関連する変換を直接実行できます。
- バイナリBlob型の場合は、ArrayBuffer と DataView を使用して処理する必要があります。
3.メッセージを送信する
WebSocket はsendメソッドを通じてメッセージを送信します。
webSocket.send(data);
この例のデータ フィールドは、受信メッセージに記載されている 3 つのデータ型 (String、ArrayBuffer、Blob) のいずれかである可能性があります。
4. 接続を閉じます
4.1 パッシブシャットダウン
サーバーが WebSocket 接続をアクティブに閉じると、WebSocket 経由でクライアントにクローズ パケットが送信され、WebSocket closeイベントがトリガーされます。このとき、クライアントは受動的に接続を閉じます。
webSocket.addEventListener('close', (closeEvent) => {
});
注: ネットワークが切断されても、終了パケットが受信されないため、WebSocket 接続は受動的には閉じられません。
4.2 アクティブシャットダウン
クライアントは、 WebSocket が提供するcloseメソッドを使用して、長い接続をアクティブに閉じることができます。
webSocket.close();
現在、このメソッドには 2 つのパラメータがあります。
- 最初のパラメータは閉じられた接続のステータス番号を示します。デフォルトは 1000 で、正常に閉じられていることを示します。
- 2 番目のパラメータはシャットダウンの理由で、123 バイト以下の UTF-8 テキストです。
4. 基礎知識
1. バックエンドの基本
-
バックエンドは、構成ファイルを通じて ServerEndpointExporter タイプの Bean を登録します。名前が示すように、このクラスはサービス エンドポイント エクスポーターと呼ばれます。Bean は、@ServerEndpoint アノテーションでマークされたクラスを自動的にスキャンし、テストします。
-
@ServerEndpoint アノテーションが付けられたクラスは、ServerEndpointExporter によってサービス エンドポイントとしてスキャンされ、登録されます。これは、WebSocket サーバーとして理解できます。@ServerEndpoint には、サービス エンドポイントに接続するための URI を指定するために使用される value 属性があります。 /chatServer/{id} などの URI テンプレートを含めると、ID が異なる限り複数の WebSocket 接続を作成できます。それ以外の場合、値が "/chatServer" に設定されている場合、サービス エンドポイントはその URI との接続を 1 つだけ受け入れます「/chatServer」。
-
@ServerEndpoint でマークされたサービス エンドポイント クラスには、一般的に使用されるいくつかのアノテーションが含まれています。
- @OnOpen
接続の確立を監視するために使用されます。クライアントがサービス エンドポイントとの接続を確立すると、このアノテーションが付けられたメソッドが自動的にコールバックされます。
- @OnClose
接続の終了をリッスンするために使用されます。クライアントがサービス エンドポイントから切断されると、このアノテーションが付けられたメソッドがコールバックされます。
- @OnError
接続上のエラーを監視するために使用されます。クライアントとサービス エンドポイント間の接続で例外が発生すると、このアノテーションが付けられたメソッドがコールバックされます。
このメソッドのパラメータは Throwable、オプションの Session および 0 ~ n String パラメータである必要があり、String パラメータには @PathParam アノテーションを付ける必要があることに注意してください。
- @OnMessage
クライアントがサーバーにメッセージを送信することを監視するために使用されます。クライアントとサーバーがメッセージを送信すると、このアノテーションが付けられたメソッドがコールバックされます。
- @OnPathParam
接続上の URI モジュールの値を取得するために使用されます
- セッションオブジェクト
セッションは webSocket 接続であり、対応するセッションを使用して getBasicRemote().sendText("message") を呼び出し、メッセージを送信します。
2. フロントエンドの基礎知識
- サービス エンドポイントに接続するためのソケット URL を構築します。WebSocket は ws プロトコルを使用するため、ws で始まります。URI は、@ServerEndpoint(value="/chatServer/{username} など) バックエンド サービス エンドポイントによって定義された値と一致します。 ")
let socketUrl = "ws://localhost:8080/chatServer/" + username;
- socketUrlに基づいてソケットオブジェクトを作成します。
const socket = new WebSocket(socketUrl);
- ソケットオブジェクトのプロパティにコールバック関数を設定します。
//连接建立时触发
socket.opopen = () - > {
}
//接收到来自服务器的消息时触发
socket.onmessage = () -> {
}
//连接关闭时触发
socket.onclose= () -> {
}
//连接发生异常时触发
socket.οnerrοr= ()-> {
}
- ソケット オブジェクト メソッドを使用してメッセージを送信する
socket.send("message");
- ソケット接続を閉じます
socket.close();
5. 全体的な実装アイデア
バックエンドは @ServerEndpointExporter と @ServerEndpoint を使用して WebSocket サービス エンドポイントを登録し、アノテーションを通じて対応するイベントをリッスンします。フロントエンドは WebSocket オブジェクトを作成し、WebSocket プロパティのコールバック関数 listen および send() メソッドを設定することによってサーバーにメッセージを送信します。
事業内容:
バックエンドは、現在接続しているユーザーのセッションを保存し、これらのセッション接続にメッセージを送信するためにコレクションを使用する必要があります。セッションごとにキー タグを使用するのが最善です。
たとえば、hashMap を使用する場合、キーはユーザー名、値はユーザーのセッションであり、メッセージを受信すると、メッセージ内のユーザー名に従って対応するメッセージを取得し、それにメッセージを送信します。
6. まとめ
この記事ではWebSocketに関する基礎知識を中心に紹介します。
WebSocket の長時間接続により、クライアントとサーバーは関連するパフォーマンスの問題を引き起こすことなく大量のデータを送信できるため、Web 側に大きな機能強化がもたらされます。現在、Web 側は WebSocket を使用して IM 関連機能や、サーバーとの大量のデータ通信を必要とするリアルタイム コラボレーションやその他の機能を開発でき、以前のようにロング ポーリング ハック方式を使用する必要はありません。