web-dev-qa-db-ja.com

WebSocket送信のチャンキング

私はより定期的にWebSocket接続を使用しているため、内部で物事がどのように機能するかに興味がありました。だから、私はしばらくの間、無限の仕様書を掘り下げましたが、今のところ、送信ストリーム自体のチャンクについては何も見つけることができませんでした

WebSocketプロトコルはそれをデータフレームと呼びます(これは純粋なデータストリームを記述するため、非制御フレームとも呼ばれます)。仕様を理解している限り、定義された最大長と定義されたMTU(最大転送単位)値はありません。つまり、単一のWebSocketデータフレームには、spec(!)によって無限のデータが含まれることがあります(ここで間違っている場合、私を修正してください、私はまだこれについて学生です)。

それを読んだ後、私はすぐに小さなNodeWebSocketサーバーをセットアップしました。私は強力なAjax履歴(ストリーミングとCometでも)を持っているため、私の期待は元々「転送中にデータを読み取るための何らかのインタラクティブモードである必要があります」。しかし、私は間違っていますよね?

私は小さな4kbのデータで始めました。

サーバー

testSocket.emit( 'data', new Array( 4096 ).join( 'X' ) );

予想どおり、これは1つのデータチャンクとしてクライアントに到着します

クライアント

wsInstance.onmessage = function( data ) {
    console.log( data.length ); // 4095
};

そのため、ペイロードを増やしましたが、実際には、ある時点でクライアント側のonmessageハンドラーが繰り返し起動し、転送をチャンクすることを期待していました。しかし、私のショックに、それは決して起こりませんでした(node-serverfirefoxchromeおよびsafariクライアント側)。私の最大のペイロードは80 MB

testSocket.emit( 'data', new Array( 1024*1024*80 ).join( 'X' ) );

そして、それはまだクライアント上の1つの大きなデータチャンクで到着しました。もちろん、接続が非常に良好であっても、これには時間がかかります。ここの質問は

  • XHR readyState3 modeと同様に、それらのストリームをチャンクする可能性はありますか?
  • 単一のWSデータフレームにサイズ制限はありますか
  • ウェブソケットはそのような大きなペイロードを転送することになっていないのですか? (これはなぜ定義された最大サイズがないのか不思議に思うでしょう)

私はまだWebSocketsの間違った視点から見るかもしれません、おそらく大量のデータ量を送信する必要性はそこにはないので、送信する前に自分で論理的にデータをチャンク/分割する必要がありますか?

43
jAndy

まず、WebSocketprotocolとWebSocket[〜#〜] api [〜#〜を区別する必要があります]ブラウザー内

WebSocketプロトコルには2 ^ 63オクテットのフレームサイズ制限がありますが、WebSocketメッセージは無制限の数のフレームで構成できます。

ブラウザー内のWebSocket APIは、フレームベースまたはストリーミングAPIを公開せず、メッセージベースAPIのみを公開します。着信メッセージのペイロードは、JavaScriptに提供される前に(ブラウザーのWebSocket実装内で)常に完全にバッファリングされます。

他のWebSocket実装のAPIは、WebSocketプロトコル経由で転送されるペイロードへのフレームベースまたはストリーミングベースのアクセスを提供する場合があります。たとえば、 AutobahnPython はそうです。こちらの例で詳細を読むことができます https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/streaming

開示:私はアウトバーンの原著者であり、タベンドで働いています。

その他の考慮事項:

ブラウザJS WebSocket APIにフレーム/ストリーミングAPIがない限り、完全なWSメッセージのみを受信/送信できます。

単一(プレーン)WebSocket接続では、複数のメッセージのペイロードをインターリーブできません。つまり、大きなメッセージを使用する場合、それらは順番に配信され、大きなメッセージがオンザフライで送信されている間に小さなメッセージを送信することはできません。

次のWebSocket拡張機能があります(拡張機能は、プロトコルを拡張するための組み込みメカニズムです):WebSocket多重化。これにより、単一の基盤となるTCP=接続を介した複数の(論理)WebSocket接続が可能になります。

注:単一のJS/HTMLページtodayから単一のターゲットサーバーへの複数のWS接続を(異なる基盤TCP上で)開くことができます。

また、アプリケーション層で「チャンク」を行うこともできます。小さなWSメッセージで自分のものを送信して、自分で組み立て直します。

理想的な世界では、ブラウザにメッセージ/フレーム/ストリーミングAPIとWebSocket多重化があることに同意します。それはすべての力と便利さを与えるでしょう。

80
oberstet

RFC 6455セクション1.1

これはWebSocketプロトコルが提供するものです:[...] Webページからリモートサーバーへの双方向通信のHTTPポーリングの代替

前述のように、WebSocketはWebページとサーバー間の通信用です。 WebページとWebブラウザの違いに注意してください。使用されている例は、ブラウザゲームやチャットアプリケーションであり、これらは多くの小さなメッセージを破ります。

1つのメッセージで多くのMBを送信する場合、WebSocketを意図したとおりに使用していないと思います。ファイルを転送する場合は、Plain Old Http Requestを使用して、Content-Dispositionブラウザがファイルをダウンロードできるようにします。

そのため、このような大量のデータを送信する理由を説明すると、おそらく誰かがWebSocketsを使用するよりも洗練されたソリューションを思い付くことができます。

また、クライアントまたはサーバーは、大きすぎるメッセージを拒否する場合があります(明示的には明記されていませんがhow拒否します):

RFC 6455セクション10.4

複数のフレームからの再アセンブリ後のフレームサイズまたは合計メッセージサイズに関する実装固有および/またはプラットフォーム固有の制限がある実装は、これらの制限を超えないように保護する必要があります。 (たとえば、悪意のあるエンドポイントは、単一の大きなフレーム(たとえば、サイズ2 ** 60)を送信するか、小さなフレームの長いストリームを送信して、ピアのメモリを使い果たしたり、サービス拒否攻撃を仕掛けたりすることができます。断片化されたメッセージの一部です。)このような実装は、複数のフレームからの再組み立て後、フレームサイズと合計メッセージサイズに制限を課す必要があります。

10
CodeCaster