2

Socketsを使用してクライアント/サーバーチャットを開発しましたが、うまく機能しますが、Deflate圧縮を使用してデータを送信しようとすると、機能しません。出力は「空」です(実際には空ではありませんが、以下で説明します)。 )。

圧縮/解凍部分は100%動作しているので(私はすでにテスト済みです)、問題は送信/受信部分の他の場所にあるはずです。

次の方法を使用して、クライアントからサーバーにメッセージを送信します。

// streamOut is an instance of DataOutputStream
// message is a String

if (zip) { // zip is a boolean variable: true means that compression is active
    streamOut.write(Zip.compress(message)); // Zip.compress(String) returns a byte[] array of the compressed "message"
} else {
    // if compression isn't active, the client sends the not compressed message to the server (and this works great)
    streamOut.writeUTF(message);
}
streamOut.flush();

そして、私はこれらの他の方法を使用してクライアントからサーバーへのメッセージを受け取ります:

// streamIn is an instace of DataInputStream

if (server.zip) { // same as before: true = compression is active
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    byte[] buf = new byte[512];
    int n;

    while ((n = streamIn.read(buf)) > 0) {
        bos.write(buf, 0, n);
    }

    byte[] output = bos.toByteArray();
    System.out.println("output: " + Zip.decompress(output)); // Zip.decompress(byte[]) returns a String of decompressed byte[] array received
} else {
    System.out.println("output: " + streamIn.readUTF()); // this works great
}

プログラムを少しデバッグすると、whileループが終了しないことがわかりました。

byte[] output = bos.toByteArray();
System.out.println("output: " + Zip.decompress(output));

呼び出されることはありません。

これらの2行のコードをwhileループ(bos.write()の後)に入れると、すべて正常に機能します(クライアントから送信されたメッセージが出力されます)。しかし、受信したbyte []配列のサイズが異なる可能性があるため、これが解決策ではないと思います。このため、問題は受信部分にあると思いました(クライアントは実際にデータを送信できます)。

そのため、私の問題は受信部分のwhileループになりました。私は試してみました:

while ((n = streamIn.read(buf)) != -1) {

条件が!= 0の場合でも、以前と同じです。ループが終了することはないため、出力部分が呼び出されることはありません。

4

3 に答える 3

2

-1は、ソケットが閉じているか壊れている場合にのみ返されます。圧縮されたコンテンツを送信した後でソケットを閉じると、コードが機能し始めます。しかし、もっと(将来の)チャットメッセージのためにソケットを開いたままにしておきたいのではないかと思います。したがって、個別のメッセージが完全に送信されたことをクライアントに通知する他の方法が必要です。Patrickが提案したように、各zip形式のペイロードの前にメッセージの長さを送信できます。

ただし、deflate形式自体で何かを活用できる場合があります。ラストブロックインストリームマーカーがあると思います。java.util.zip.Inflaterを使用している場合は、Inflater.finished()を参照してください。

于 2012-07-25T18:51:34.317 に答える
1

読み取り関数は、ストリームが閉じられるまで-1を返しません。サーバーからクライアントに送信する必要のあるバイト数を計算し、クライアント側でそのバイト数を読み取ることができます。

バイト数の計算は、Zip.compress関数から返されたバイト配列の長さを実際のメッセージの前に送信し、readInt関数を使用してその数を取得するのと同じくらい簡単です。

このアルゴリズムを使用すると、解凍する前に正しいバイト数を読み取ることができるため、クライアントが実際に0バイトを読み取った場合でも、必要なすべてのバイトを受信するまで読み取りを続けます。streamIn.read(buf, 0, Math.min(bytesLeft, buf.length))を実行して、必要な数のバイトのみを読み取ることができます。

于 2012-07-25T18:28:03.597 に答える
0

問題は、ストリームの操作方法です。クライアントがデータとして何を期待できるかを知るために、メタデータを送信する必要があります。理想的には、ストリームを読み取るためのプロトコル/ステートマシンを作成しています。たとえば、手っ取り早い解決策として、データサイズや終了シーケンスなどを送信します。

解決策の例:
サーバー:圧縮データの前に「データサイズ」を送信します
クライアント:「データサイズ」バイトを待ちます。ここで、読み取りが「データサイズ」値以上になるまでループします。何かのようなもの:

while( streamIn.ready() && dataRead < dataExpected)
{
    dataRead += streamIn.read(buf);
}

もちろん、同様のコードを使用して、以前にdataExpectedを読み取る必要があります。

ヒント:データが失われる可能性があることを気にしない場合は、UDPを使用することもできます。データグラムを使用してプログラミングする方が簡単です...

于 2012-07-25T19:54:09.823 に答える