11

TCP ソケットを使用して通信するクライアント/サーバーを実装しました。ソケットに書き込んでいるデータは、文字列化された JSON です。最初はすべてが期待どおりに機能しますが、書き込み速度を上げていくと、最終的に JSON 解析エラーが発生し、クライアントの最初の部分が古い書き込みの最後に新しい書き込みの始まりを受け取ることになります。

サーバーコードは次のとおりです。

var data = {};
data.type = 'req';
data.id = 1;
data.size = 2;
var string = JSON.stringify(data);
client.write(string, callback());

クライアントサーバーでこのコードを受け取る方法は次のとおりです。

client.on('data', function(req) {
    var data = req.toString();
    try {
        json = JSON.parse(data);
    } catch (err) {
         console.log("JSON parse error:" + err);
    } 
});

レートが上がると発生するエラーは次のとおりです。

SyntaxError: Unexpected token {

これは、現在のリクエストの終わりにタグ付けされている次のリクエストの始まりのようです。

使ってみました; 各 JSON リクエストの末尾に区切り文字として使用し、次を使用します。

 var data = req.toString().substring(0,req.toString().indexOf(';'));

ただし、このアプローチでは、JSON 解析エラーが発生する代わりに、書き込み速度が 1 秒あたり 300 を超えると、クライアント側で一部の要求が完全に失われるようです。

TCP ソケットを介して着信要求を区切るためのベスト プラクティスまたはより効率的な方法はありますか?

ありがとう!

4

5 に答える 5

28

説明してくれてありがとう、TCPソケットを介してデータが送受信される方法をよりよく理解するのに役立ちました。以下は、最後に使用したコードの簡単な概要です。

var chunk = "";
client.on('data', function(data) {
    chunk += data.toString(); // Add string on the end of the variable 'chunk'
    d_index = chunk.indexOf(';'); // Find the delimiter

    // While loop to keep going until no delimiter can be found
    while (d_index > -1) {         
        try {
            string = chunk.substring(0,d_index); // Create string up until the delimiter
            json = JSON.parse(string); // Parse the current string
            process(json); // Function that does something with the current chunk of valid json.        
        }
        chunk = chunk.substring(d_index+1); // Cuts off the processed chunk
        d_index = chunk.indexOf(';'); // Find the new delimiter
    }      
});

コメント歓迎...

于 2012-10-16T16:11:40.703 に答える
6

区切り記号を使用して正しい方向に進んでいます。ただし、デリミタの前のものを抽出して処理し、その後のものを破棄することはできません。区切り文字の後に取得したものはすべてバッファリングしてから、その次に来るものを連結する必要があります。これは、特定のイベントの後に任意の数 (0 を含む) の JSON "チャンク" が発生する可能性があることを意味しますdata

基本的に、バッファを保持し、それを に初期化し""ます。イベントごとdataに、受け取ったものをすべてバッファーの最後に連結し、split それを区切り文字のバッファーに連結します。結果は 1 つ以上のエントリになりますが、最後のエントリは完全ではない可能性があるため、バッファをテストして区切り文字で終わっていることを確認する必要があります。そうでない場合は、最後の結果をポップして、バッファを設定します。次に、残っている結果をすべて処理します (何もない可能性があります)。

于 2012-10-14T10:00:33.573 に答える
3

TCP は、受信したデータのチャンクをどこで分割するかについて保証しないことに注意してください。保証されるのは、接続が完全に失敗しない限り、送信したすべてのバイトが順番に受信されることだけです。

dataソケットがデータを持っていると言うたびにノードイベントが発生すると思います。技術的には、JSON データのバイトごとに個別のイベントを取得できますがdata、それでも OS が許可されている範囲内です。誰もそんなことはしませんが、コードは堅牢であるためにはいつでも突然起こり得るように書かれている必要があります。データ イベントを結合し、意味のある境界に沿ってデータ ストリームを再分割するのは、ユーザー次第です。

これを行うには、「完全な」データのチャンクの末尾に追加されたデータを含め、「完全」ではないデータをバッファリングする必要があります。区切り記号を使用している場合は、区切り記号の後のデータを破棄しないでください。さらにデータが表示され、最終的に別の区切り記号または終了イベントが表示されるまで、常に接頭辞として保持してください。

もう 1 つの一般的な選択肢は、すべてのデータに長さフィールドのプレフィックスを付けることです。固定の 64 ビット バイナリ値を使用するとします。次に、常に 8 バイトに加えて、それらのバイトの値が示すより多くの値が到着するのを待ちます。受信データの 10 バイトのチャンクがあるとします。1 つのイベントで 2 バイト、次に 5、次に 4 を取得する場合があります。この時点で長さを解析し、さらに 7 バイトが必要であることがわかります。これは、3 番目のチャンクの最後の 3 バイトがペイロードであったためです。次のイベントに実際に 25 バイトが含まれている場合、最初の 7 と前の 3 を取得して解析し、バイト 8 ~ 16 で別の長さフィールドを探します。

これは不自然な例ですが、トラフィック レートが低い場合、ネットワーク レイヤーは通常、指定されたチャンクでデータを送信することに注意してください。OS が一度に複数の書き込みからパケットを構築し始めると、ユーザーではなくネットワークにとって便利な粒度で分割が開始され、それに対処する必要があります。

于 2012-10-14T10:19:27.153 に答える
-4

endイベントとデータなしで試してください

var data = '';

client.on('data', function (chunk) {
  data += chunk.toString();
});

client.on('end', function () {
  data = JSON.parse(data); // use try catch, because if a man send you other for fun, you're server can crash.
});

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

于 2012-10-13T11:55:52.450 に答える