- ベストアンサー
大きな容量のファイルを転送する
UserModeLinuxを起動する際のルートファイルシステム(4294971392バイト) をサーバ側からクライアント側に転送したいと考えています。 以下のプログラムを実行した結果、エラーが出てしまいます。 容量が大きなファイルのため、出てきてしまうエラーだと思うのですが。 またおかしなことに、エラーが出るのでプログラムを停止して クライアント側のディレクトリを確認すると ルートファイルシステムが存在し、UserModeLinuxを起動することもできました。しかしコンソールにはエラーが表示されるので困っています。 このような場合にどのように対処するべきでしょうか。 サーバ側 import java.net.*; import java.io.*; public class FileTransferServerroot { public static void main(String[] args) throws IOException{ if (args.length != 2) throw new IllegalArgumentException("Arguments should be host,port and filepath"); int serverPort = Integer.parseInt(args[0]); String filename = args[1]; byte[] data = new byte[32]; //ソケットの作成 ServerSocket socket = new ServerSocket(serverPort); Socket sock = socket.accept(); System.out.println("Connected to server"); //ストリームの作成 FileInputStream fin = new FileInputStream(filename); OutputStream out = sock.getOutputStream(); //ファイルの内容を読み出し、送信する System.out.println("Sending file : " + filename); int totalSize = 0; int len = 0; for(;;){ len = fin.read(data); totalSize += len; out.write(data, 0, len); if(totalSize == 4294971392L) break; } fin.close(); fin = null; System.out.println("linux.umlを送信完了しました"); socket.close(); } } クライアント側 import java.net.*; import java.io.*; public class FileTransferClientroot { public static void main(String[] args) throws IOException{ if (args.length != 3) throw new IllegalArgumentException("An argument should be port and filename"); String host = args[0]; int servPort = Integer.parseInt(args[1]); String filename = args[2]; System.out.println("Output file name : " + filename); //Create FileOutputStream FileOutputStream fout = new FileOutputStream(filename); //Create ServerSocket Socket servSock = new Socket(host, servPort); int recvMsgSize; int bufSize = 32; System.out.println("Size of ReceiveBuffer : " + bufSize); byte[] byteBuffer = new byte[bufSize]; //Create InputStream InputStream in = servSock.getInputStream(); //Read message and print it out int totalByte = 0; //while((recvMsgSize = in.read(byteBuffer)) != -1){ for(;;){ recvMsgSize = in.read(byteBuffer); totalByte += recvMsgSize; fout.write(byteBuffer,0,recvMsgSize); if(totalByte == 4294971392L) break; } System.out.println("linux.umlを受信完了しました"); servSock.close(); fout.close(); fout = null; } } 実行結果(サーバ側) Connected to server Sending file : uml-root-hardy Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException at java.net.SocketOutputStream.socketWrite(Unknown Source) at java.net.SocketOutputStream.write(Unknown Source) at FileTransferServerroot.main(FileTransferServerroot.java:25) 実行結果(クライアント側) Output file name : uml-root-hardy Size of ReceiveBuffer : 32 Exception in thread "main" java.lang.IndexOutOfBoundsException at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(Unknown Source) at FileTransferClientroot.main(FileTransferClientroot.java:28)
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
> -1を返すというのはソケットを閉じていますよね。 > できればソケットを閉じずに2つのファイルを転送したいと考えているのですが。 やっぱり勘違いしていましたか。 ストリームが最後に達しただけでTCP/IPのセッションは閉じませんよ。 そもそも、最初のコードで if(totalByte == 4294971392L) break; これが機能しないのは、通信中に recvMsgSize = -1 が発生し totalByte が期待通りの値にならなくなるからです。 >>out は BufferedOutputStream で宣言 >>in は BufferedInputStream で宣言 >それぞれどのように宣言すればよいでしょうか。 まずはマニュアルを見る癖をつけてください。 http://java.sun.com/javase/ja/6/docs/ja/api/java/io/BufferedOutputStream.html http://java.sun.com/javase/ja/6/docs/ja/api/java/io/BufferedInputStream.html BufferedOutputStream out = new BufferedOutputStream(sock.getOutputStream()); BufferedInputStream in = new BufferedInputStream(servSock.getInputStream()) ですよ。
その他の回答 (5)
- junkUser
- ベストアンサー率56% (218/384)
> どのように対処すればエラーなく動作するでしょうか。 以下のように変更します。 totalSize はループ以降で使用されないので削除しました。 サーバー側 ------------ for(;;){ len = fin.read(data); totalSize += len; out.write(data, 0, len); if(totalSize == 4294971392L) break; } ------------ ↓ out は BufferedOutputStream で宣言 OutputStream ではディスクI/Oの影響を直接受けるため、Buffered で安定的に送信する。 ------------ while((len = fin.read(data) != -1){ out.write(data, 0, len); } out.flush(); ------------ クライアント側 ------------ //while((recvMsgSize = in.read(byteBuffer)) != -1){ for(;;){ recvMsgSize = in.read(byteBuffer); totalByte += recvMsgSize; fout.write(byteBuffer,0,recvMsgSize); if(totalByte == 4294971392L) break; } ------------ ↓ in は BufferedInputStream で宣言 InputStream で宣言すると、ループが速すぎて受信完了前に -1 になることがあるため ------------ while((recvMsgSize = in.read(byteBuffer)) != -1){ fout.write(byteBuffer,0,recvMsgSize); } ------------
補足
-1を返すというのはソケットを閉じていますよね。 できればソケットを閉じずに2つのファイルを転送したいと考えているのですが。 fout.close(); fout = null; と行うのは、ソケットも閉じてしまいますか? また >out は BufferedOutputStream で宣言 >in は BufferedInputStream で宣言 それぞれどのように宣言すればよいでしょうか。 どうしてもうまくいかないのですが。
- tom11
- ベストアンサー率53% (134/251)
失礼 >UserModeLinuxを起動する際のルートファイルシステム(4294971392バイト) 読み飛ばしていました。!!! No3は、忘れてください。 ただ逆に、4294971392この数字にこだわる必要が ないのでは?? なんで、質問プログラムの注釈文にあるように >//while((recvMsgSize = in.read(byteBuffer)) != -1){ この方法を避けたのですか??
補足
お返事ありがとうございます。 この数字はプロパティから確認したため、そうしました。 ソケットを閉じるのを避けたのは、 このファイル転送プログラムは大きなプログラムの一部分で ソケットを閉じたり開いたりするのをしないように 指示を受けているからです。
- tom11
- ベストアンサー率53% (134/251)
質問プログラムとNo2さんの回答を見ると 答えが、すでに出ているような気もするのですが。 私の勘違いかな??? 別な視点で、疑問点!!! >if(totalByte == 4294971392L) この数字、4294971392Lは、何処まで、正確なのですか このif文、1でも、ずれると、無限ループに 陥りますが。 No2さんも、書いているように、 totalByte == 4294971392Lをtrueにするのは、 かなりの至難のわざのようなきもするのですが。。 もともと、大きな数字ですし。 ちなみに、もともとのデータが、 4294971392Lより大きく 4294971392Lで切りたいなら、また、工夫が必要ですが、 大体 4294971392Lの付近でデータを切っていいなら、 if(totalByte >= 4294971392L) これなら、無限ループには、ならないのでは??? また、もともとのデータが、4294971392Lより小さいと この評価の前に、エラーが出ますが。 もともと、データどういうものか解らないので、 この辺で、
- junkUser
- ベストアンサー率56% (218/384)
int totalSize = 0; これを long totalSize = 0; で解決します。 もっときれいに書くのでしたら、ファイルを read で読み取った時点で判別したほうが良いでしょう。 現在の状態ですと、 if(totalByte == 4294971392L) は常にfalseになるため、ファイルの最後その次のループで len = -1 recvMsgSize = -1 が入ります。その後、サーバー、クライアント 25: out.write(data, 0, len); 28: fout.write(byteBuffer,0,recvMsgSize); でArrayIndexOutOfBoundsExceptionが発生し、停止します。
補足
お返事ありがとうございます。 longtotalSize=0; とすることでその部分は解決しました。 もう一つの方ですが if(totalByte == 4294971392L) としてもご指摘どおり サーバ側でArrayIndexOutOfBoundsException クライアント側でjava.lang.IndexOutOfBoundsException のエラーが出てしまいます。 どのように対処すればエラーなく動作するでしょうか。 教えてください。
- tom11
- ベストアンサー率53% (134/251)
表題とは、関係ないのですが。 >if(totalSize == 4294971392L) となっているのに >int totalSize = 0; longの値を評価しているのに、int型で、宣言しますね。 これって、問題ないの???
補足
ご指摘ありがとうございました。 たしかに long totalSize = 0; とするべきでした。
補足
本当にありがとうございます。 無事ファイル転送を完了することができました。 失礼します。