0

TCP ソケットからすべてのデータを取得する際に問題が発生しています。

私のサーバーでは、次のようなソケットからデータを読み取っています。

        int len;
        byte[] buffer = new byte[2000];
        try {
            this.in = new DataInputStream(this.socket.getInputStream());
            this.out = new DataOutputStream(this.socket.getOutputStream());
            running = true;

            while (running) {
                len = in.read(buffer);
                if (len < 0) {
                    running = false;
                } else {
                    parsePacket(buffer, len);
                }
            }

        } catch (IOException ex) {
            System.out.println("Catch IOException: " + ex);
            ex.printStackTrace();
        } finally {
            try {
                System.out.println("Closing");
                in.close();
                out.close();
                socket.close();
            } catch (IOException ex) {
                System.out.println("Finally IOException: " + ex);
            }
        }

パケット形式は次のようになります。

[ヘッダー][データ][ターミネーター]

  • ヘッダー --> メッセージの開始を識別する一連の文字 (パケットの長さに関する情報はありません)。
  • データ --> 次のようにセグメントに分割されます。[サイズ セグメント。1][データセグメント。1][サイズ区分。2][データセグメント。2][サイズ区分。3][データセグメント。3]....[サイズ区分。N][データセグメント。N]
  • ターミネータ --> [0x00]

データは非常に高速に受信されるため (200 ミリ秒以下の場合もあります)、次のようなメッセージが表示されることread(buffer)があります。buffer

  • [HEADER1][DATA1][TERM1] または、
  • [HEADER1][DATA1][TERM1][HEADER2][DATA2][TERM2].............[HEADER N][DATA N][TERM N]または、
  • [HEADER1][DATA1][TERM1][HEADER2][DATA2][TERM2].............[HEADER N][DAT (最後のメッセージが不完全)

このparsePacket()メソッドは、上記の形式のメッセージを解析することができ、次にさらにメッセージがある場合は、それらも (再帰的に) 解析されます。ただし、最後のメッセージが不完全な場合は解析されません (それは望ましくありませんが、今まで適切な解決策が見つかりませんでした)。

メッセージ内のデータは、MySQL データベースに保存されます (JDBC ドライバーを使用)。メッセージの各解析には、データベースへの複数のクエリが含まれる場合があります。データの受信、解析、保存に 1 つのスレッドしか使用していないため、コードの実行は本来の速度よりも速くありません...データはできるだけ早く受信して保存する必要があります。

私が議論したいいくつかの点:

  • メッセージの一部を失うことなくすべてのメッセージを取得するための最良の方法は何でしょうか?
  • データを受信して​​保存する方法を改善するにはどうすればよいですか? (データはできるだけ早く保存する必要があります!)
4

5 に答える 5

2

TCP はすでにストリーム プロトコルであるため、このデータを読み取る最も簡単な方法はストリームとしてです。イベントを処理するリスナーを追加します。

DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

try {
   while(true) {
       listener.startOfMessage();
       for(int segSize; (segSize = dis.readInt()) > 0;) {
          byte[] bytes = new byte[segSize];
          dis.readFully(bytes);
          listener.data(bytes);
       }
       int footer = dis.read();
       // check footer ??
       listener.endOfMessage();
   }
} catch(EOFException endOfStream) {
   // handle or ignore
} finally {
   // close everything.
}

自分でバッファリングを行う場合、メッセージを再アセンブルし、不完全なメッセージを保持する必要があります。これは、ここでは何のメリットもない頭痛の種です。

データは非常に高速に受信されます (200 ミリ秒以下の場合もあります)。

200 ミリ秒は、お使いの CPU ごとに約 6 億クロック サイクルです。これはコンピュータにとって永遠です。:)

上記のコードは、200 ミリ秒で 20,000 件のメッセージを処理する必要があります。これ以上必要な場合は、代わりに NIO を使用できますが、その必要はないと思います。

データはできるだけ早く保存する必要があります。

MySQLは問題ないと思います。「できるだけ早く」ではありませんが、使用しないと言ったことに理由はありません。

于 2013-01-11T09:51:32.200 に答える
1

Stringから制作していますbufferよね?この場合、parsePacketメソッドのインターフェイスを変更し、ループを次のように変換することをお勧めします。

        String tail = "";
        String line = "";
        while (running) {
            len = in.read(buffer);
            if (len < 0) {
                running = false;
            } else {
                line = tail + new String(buffer);
                tail = parsePacket(line, len);
            }
        }

あなたのparsePacket場合は、終了していない行の末尾を切り取り、メソッドから返す必要があります。

于 2013-01-11T09:43:28.633 に答える
0

TCP は、パケット サービスではなく、ストリームトランスポート サービスを提供します。「パケット化」を実現するには、プロトコルはパケット自体をフレーム化する必要があります。あなたの場合、フレーミングは[TERMINTAOR]マーカーで達成されます。クライアント側ですべきことは次のとおりです。

  1. bufferマーカーが含まれているかどうかを確認します。そうでない場合は、を発行しreadてデータを追加し、buffer手順 1 に戻ります。
  2. バッファからパケットを解析して消費する
  3. ステップ 1 に戻ります。
于 2013-01-11T09:42:35.970 に答える
-1

TCP はストリーミング プロトコルです。一方のソケットに書き込まれたすべてのバイトを、書き込まれた順序でもう一方のソケットに配信します。それらが入れられたのと同じサイズの「チャンク」で到着することは保証されませ。読み取りは、特定の書き込みで書き込まれたバイト数よりも多いまたは少ないバイトを取得する可能性があります。しかし、すべてのバイトがそこにあり、すべて正しい順序になっています。

これに対する解決策は、メッセージ境界を定義するプロトコル (メッセージ ターミネータ、長さヘッダー、または XML のような自己記述形式) を使用することです。

于 2013-01-11T09:44:21.073 に答える
-1

TCP はストリーミング プロトコルであり、あるポートから同じチャンク サイズの別のポートへのメッセージのサイズを保証しません。読み取り時に、1 回の書き込みで書き込まれるバイト数が増減する場合があります。

于 2013-01-11T09:55:37.457 に答える