web-dev-qa-db-ja.com

奇妙に結合されたWebSocketメッセージをどのように処理しますか?

node ws library (Ubuntu 16.04のノード10.8.0)を使用して外部のWebSocket APIに接続しています。私は単にjsonを解析してそれをコールバックに渡すリスナーを持っています:

this.ws.on('message', (rawdata) => {
    let data = null;
    try {
        data = JSON.parse(rawdata);
    } catch (e) {
        console.log('Failed parsing the following string as json: ' + rawdata);
        return;
    }
    mycallback(data);
});

rawDataが次のようなエラーを受け取るようになりました(無関係なコンテンツをフォーマットして削除しました)。

�~A
{
    "id": 1,
    etc..
}�~�
{
    "id": 2,
    etc..

それから私は疑問に思いました。これらのキャラクターは何ですか?構造を見ると、最初の奇妙な記号は配列の開始ブラケット([)と2番目のカンマ(,)オブジェクトの配列を作成します。

次に、JSON解析エラーが発生するたびにrawdataをファイルに書き込むことにより、問題をさらに調査しました。 1時間ほどで、これらのエラーファイルの約1500が節約されました。つまり、これはたくさん発生します。ターミナルでこれらのファイルをいくつかcatedしました。そのうちの例を以下にアップロードしました:

enter image description here

ここで興味深いことがいくつかあります。

  1. ファイルは常にこれらの奇妙な兆候の1つで始まります。
  2. ファイルは、個別に受信する必要があった複数のメッセージから存在するように見えます。奇妙なサインがそれらの個々のメッセージを分けています。
  3. ファイルは常に未完成のjsonオブジェクトで終わります。
  4. ファイルの長さはさまざまです。それらは常に同じサイズであるとは限らないため、特定の長さで切り取られません。

私はWebSocketの経験があまりありませんが、私のWebSocketが何らかの方法でメッセージのストリームを受け取り、これらの奇妙な記号をセパレーターとして連結し、最後のメッセージをランダムに切り捨てている可能性がありますか?多分私は常に非常に高速なメッセージのストリームを取得しているからですか?

それとも、それらの個々のメッセージを組み合わせるという点で、サーバー側のエラー(または機能)が原因である可能性がありますか?

誰かがここで何が起こっているのか知っていますか?すべてのヒントは大歓迎です!

[編集]

@bendataclearは、それをutf8として解釈することを提案しました。だから私はそうしました、そして私は以下の結果のスクリーンショットを貼り付けました。最初の出力はそのままで、2番目の出力はutf8として解釈されます。私にはこれは何のようにも見えません。もちろん、utf8に変換し、それらの文字で分割することもできます。最後のメッセージは常に切り捨てられますが、これにより少なくとも一部のメッセージが読みやすくなります。他のアイデアはまだ歓迎します。

enter image description here

23
kramer65

私の仮定は、あなたが英語/ ASCII文字のみを操作していて、何かがおそらくストリームを混乱させているということです。 [〜#〜]ノート[〜#〜]:私は仮定しています)、あります特殊文字がない場合は、json文字列全体をこの関数に渡すことをお勧めします。

function cleanString(input) {
    var output = "";
    for (var i=0; i<input.length; i++) {
        if (input.charCodeAt(i) <= 127) {
            output += input.charAt(i);
        }
    }
    console.log(output);
}

//example
cleanString("�~�")

参照することができます JavaScript文字列から無効なUTF-8文字を削除する方法?

[〜#〜]編集[〜#〜]

Internet Engineering Task Force(IETF) による記事から、

間違ったエンコーディングを使用してテキストデータを送信すると、一般的なセキュリティ問題が発生します。このプロトコルは、(バイナリまたは他のタイプとは対照的に)テキストデータタイプのメッセージにUTF-8でエンコードされたデータが含まれることを指定します。長さはまだ示されていますが、このプロトコルを実装するアプリケーションは、長さを使用してフレームが実際に終了する場所を判断し、不適切な方法でデータを送信する必要があります


「ペイロードデータ」は、UTF-8でエンコードされたテキストデータです。特定のテキストフレームに部分的なUTF-8シーケンスが含まれる場合があることに注意してください。ただし、メッセージ全体に有効なUTF-8が含まれている必要があります。再構成されたメッセージの無効なUTF-8は、UTF-8エンコードされたデータのエラーの処理で説明されているように処理されますエンドポイントがバイトストリームをUTF-8として解釈する予定であるが、バイトストリームが実際には有効なUTF-8ストリームではない場合、そのエンドポイントはWebSocket接続に失敗する必要があります。このルールは、開始ハンドシェイクとその後のデータ交換の両方に適用されます。

エラー(または機能)は、個別のメッセージを組み合わせるサーバー側から発生していると思います。そのため、すべての文字を確実にするロジックを考え出すことをお勧めします(〜=〜) ] [〜#〜]は、最初に文字をUTF-8としてエンコードすることにより、UnicodeからASCII)に変換する必要があります。また、npm install --save-optional utf-8-validate仕様で要求されているように、メッセージに有効なUTF-8が含まれているかどうかを効率的にチェックします。

いくつかのチェックを行うために、if条件を渡すこともできます。

this.ws.on('message', (rawdata) => {
    if (message.type === 'utf8') { // accept only text
    }

これがお役に立てば幸いです。

4
antzshrek

あなたが持っている問題は、一方が解釈する反対側とは異なるエンコーディングでJSONを送信することです。

次のコードでこの問題を解決してみてください:

const { StringDecoder } = require('string_decoder');

this.ws.on('message', (rawdata) => {
    const decoder = new StringDecoder('utf8');
    const buffer = new Buffer(rawdata);
    console.log(decoder.write(buffer));
});

またはutf16

const { StringDecoder } = require('string_decoder');

this.ws.on('message', (rawdata) => {
    const decoder = new StringDecoder('utf16');
    const buffer = new Buffer(rawdata);
    console.log(decoder.write(buffer));
});

お読みください: String Decoder Documentation

0
Bharata

出力にスペースが含まれているようです。スペースがある場合、または特殊文字が見つかった場合は、Unicodeを使用してそれらを完全に埋めてください。

これはUnicode文字のリストです

これは私が考えるのを助けるかもしれません。

0
Praveen K Y

これらの文字は「置換文字」として知られています-不明な、認識できない、または表現できない文字を置き換えるために使用されます。

差出人: https://en.wikipedia.org/wiki/Specials_(Unicode_block)

置換文字�(多くの場合、白い疑問符が付いた黒いひし形または空の四角いボックス)は、Unicode規格のSpecialsテーブルのコードポイントU + FFFDにある記号です。これは、システムがデータのストリームを正しいシンボルにレンダリングできない場合の問題を示すために使用されます。通常、データが無効であり、どの文字とも一致しない場合に表示されます

チェック WebSocketプロトコルエラー処理のセクション8

8.1。サーバーからのUTF-8でのエラーの処理

クライアントがバイトストリームをUTF-8として解釈するが、バイトストリームが実際には有効なUTF-8ストリームではないことがわかった場合、有効なUTF-8シーケンスではないバイトまたはバイトシーケンスは、 U + FFFD置換文字。

8.2。クライアントからのUTF-8でのエラーの処理

サーバーがバイトストリームをUTF-8として解釈する必要があるが、バイトストリームが実際には有効なUTF-8ストリームではない場合、動作は未定義です。サーバーは、接続を閉じたり、無効なバイトシーケンスをU + FFFD置換文字に変換したり、データを逐語的に保存したり、アプリケーション固有の処理を実行したりできます。 WebSocketプロトコル上に階層化されたサブプロトコルは、サーバーの特定の動作を定義する場合があります。


これを処理する方法を使用している実装またはライブラリによって異なります。たとえば、この投稿 Node.jsを使用したWebソケットサーバーの実装

socket.ondata = function(d, start, end) {
    //var data = d.toString('utf8', start, end);
    var original_data = d.toString('utf8', start, end);
    var data = original_data.split('\ufffd')[0].slice(1);
    if (data == "kill") {
        socket.end();
    } else {
        sys.puts(data);
        socket.write("\u0000", "binary");
        socket.write(data, "utf8");
        socket.write("\uffff", "binary");
    }
};

この場合、がそれを実行することがわかりました:

var data = original_data.split('\ufffd')[0].slice(1);
if (data == "kill") {
    socket.end();
} 

この投稿からノードを最新の安定版に更新することもできます OpenSSLおよびBreaking UTF-8の変更(Node v0.8.27およびv0.10.29で修正)

これらのリリース以降、一致しないサロゲートペアを含む文字列を渡そうとすると、Nodeはその文字を未知のUnicode文字(U + FFFD)に置き換えます。古い動作を維持するには、環境変数NODE_INVALID_UTF8を何にでも(何もなし)環境変数がまったく存在する場合、以前の動作に戻ります。

0
nbari