2

サーバーとクライアントを持つ Java プログラムを作成し、Zip ファイルをサーバーからクライアントに送信しています。私はほとんどファイルを送信しました。しかし、受信すると、いくつかの矛盾が見つかりました。私のコードは常に完全なアーカイブを取得するとは限りません。BufferedReader が完全になる前に終了していると思います。クライアントのコードは次のとおりです。

public void run(String[] args) {
        try {
            clientSocket = new Socket("jacob-custom-pc", 4444);
            out = new PrintWriter(clientSocket.getOutputStream(), true);
            in = new BufferedInputStream(clientSocket.getInputStream());
            BufferedReader inRead = new BufferedReader(new InputStreamReader(in));
            int size = 0;
            while(true) {

                if(in.available() > 0) {    

                        byte[] array = new byte[in.available()];
                        in.read(array);
                        System.out.println(array.length);


                        System.out.println("recieved file!");
                        FileOutputStream fileOut = new FileOutputStream("out.zip");
                        fileOut.write(array);
                        fileOut.close();
                        break;
                    }
                }
            }
        } catch(IOException e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }

では、ファイルを書き込む前に完全なアーカイブがあることを確認するにはどうすればよいでしょうか?

4

5 に答える 5

2

送信側では、ファイルの書き込みを開始する前にファイル サイズを書き込みます。読み取り側 ファイルサイズを読み取り、予想されるバイト数がわかるようにします。次に、期待どおりの結果が得られるまで read を呼び出します。ネットワークソケットを使用すると、送信されたすべてのものを取得するために読み取りに複数の呼び出しが必要になる場合があります。これは、データが大きくなるにつれて特に当てはまります。

于 2013-09-12T15:03:33.730 に答える
0

TCP ソケットを使用しています。ZIP ファイルはおそらくネットワーク MTU よりも大きいため、複数のパケットに分割され、反対側で再構成されます。それでも、次のようなことが起こる可能性があります。

  • クライアントが接続します
  • サーバーが送信を開始します。ZIP ファイルは MTU よりも大きいため、複数のパケットに分割されます。
  • while (true)クライアントはビジー状態で、最初のパケットを取得するまで待機します。
  • クライアントはデータが到着したことに気づきます ( in.available() > 0)
  • クライアントは利用可能なすべてのデータを読み取り、それをファイルに書き込んで終了します
  • 最後のパケットが到着する

ご覧のとおり、クライアント マシンが非常に遅く、ネットワークが非常に高速で MTU が非常に大きい場合を除き、コードは設計上ファイル全体を受信しません。それがあなたがそれを構築した方法です。

別のアプローチ: データの前に長さを付けます。

Socket clientSocket = new Socket("jacob-custom-pc", 4444);
DataInputStream dataReader = new DataInputStream(clientSocket.getInputStream());
FileOutputStream out = new FileOutputStream("out.zip");
long size = dataReader.readLong();
long chunks = size / 1024;
int lastChunk = (int)(size - (chunks * 1024));
byte[] buf = new byte[1024];
for (long i = 0; i < chunks; i++) {
    dataReader.read(buf);
    out.write(buf);
}
dataReader.read(buf, 0, lastChunk);
out.write(buf, 0, lastChunk);

サーバーはDataOutputStream、実際のファイルの前にファイルのサイズを送信するために使用します。私はこれをテストしませんでしたが、動作するはずです。

于 2013-09-12T15:19:22.040 に答える
0

HTTP はcontent-length: x+\n をバイト単位で送信します。これはエレガントです。接続が壊れている場合は TimeoutException をスローする可能性があります。

于 2013-09-12T15:08:50.033 に答える
0

ソケットストリームを介してファイル全体を受信したことを確認するにはどうすればよいですか?

コードを修正することによって。InputStream.available()ストリームの終わりのテストとして使用しています。それはそのためではありません。コピー ループを次のように変更します。これも非常に簡単です。

while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}

ゼロより大きい任意のバッファー サイズ (通常は 8192) で使用します。

于 2013-09-13T03:05:48.573 に答える
-1

In.available()in.read()現時点でブロック(待機)せずに消費されるデータがないことを示しているだけですが、ストリームの終わりを意味するものではありません。ただし、それらは TCP/IP パケットでいつでも PC に到着する可能性があります。通常、使用することはありませんin.available()In.read()ストリームを完全に読み取るには、すべてで十分です。入力ストリームを読み取るためのパターンは次のとおりです。

byte[] buf;
int size;

while ((size = in.read(buf)) != -1)
  process(buf, size);

// end of stream has reached

このようにして、ストリームを最後まで完全に読み取ります。

更新複数のファイルを読み取りたい場合は、ストリームを「パケット」にチャンクし、すべてのファイルに整数サイズのプレフィックスを付けます。次に、 の代わりに size バイトが受信されるまで読み取りますin.read = -1

update2in.availableとにかく、データのチャンク間の区切りには決して使用しないでください。これを行うと、着信データ間に時間遅延があることを意味します。これは、リアルタイム システムでのみ実行できます。しかし、Windows、Java、および TCP/IP はすべて、リアルタイムと互換性のないこれらのレイヤーです。

于 2013-09-12T16:01:06.027 に答える