- ベストアンサー
ObjectInputStreamとStreamCorruptedExceptionについて
- ObjectInputStreamとStreamCorruptedExceptionについての質問です。
- ソースコードをコンパイルして実行すると、StreamCorruptedExceptionが発生します。
- StreamCorruptedExceptionは、読み込まれた制御情報が内部整合検査に違反した場合にスローされます。正しい実行方法を教えてください。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
こんばんは。 普通にバイナリデータを送りたいだけであれば、クライアント側はファイル読み込みにFileInputStream、ソケットへの出力にOutputStreamを、サーバ側はファイル書出しにFileOutputStream、ソケットからの入力にInputStreamを使うのが一番シンプルだと思います。 ※下のサンプルを実行すると作業フォルダに、test.dataとserved_test.dataというファイルを作成します(既存の場合は上書き) - - - - - - - - - - - - - - - - - - - - [クライアント] import java.io.*; import java.net.*; class TestClient { public static void main(String[] args) { if(!new File("test.data").exists()) { TestClient.makeTestData(); } try { FileInputStream fis = new FileInputStream("test.data"); Socket s = new Socket("localhost", 10000); OutputStream os = s.getOutputStream(); byte b = -1; while((b = (byte)fis.read()) != -1) { os.write(b); c++; } os.close(); s.close(); fis.close(); } catch(ConnectException ce) { System.out.println("リモートアドレス/ポート上で待機しているプロセスがありません"); } catch(Exception e) { e.printStackTrace(); } } public static void makeTestData() { byte[] testData = new byte[1024 * 8]; for(int i = 0; i < testData.length; i++) { testData[i] = 0x01; } try { FileOutputStream fos = new FileOutputStream("test.data"); fos.write(testData); fos.flush(); fos.close(); } catch(Exception e) { e.printStackTrace(); } } } - - - - - - - - - - - - - - - - - - - - [サーバ] import java.io.*; import java.net.*; class TestServer { public static void main(String[] args) { try { FileOutputStream fos = new FileOutputStream("served_test.data"); ServerSocket ss = new ServerSocket(10000); System.out.println("受付開始"); InputStream is = ss.accept().getInputStream(); byte b = -1; while((b = (byte)is.read()) != -1) { fos.write(b); } System.out.println("受付終了"); is.close(); ss.close(); fos.close(); } catch(Exception e) { e.printStackTrace(); } } } - - - - - - - - - - - - - - - - - - - - > ObjectOutputStream clientOut = new ObjectOutputStream(soket.getOutputStream()); > BufferedOutputStream clientBuf = new BufferedOutputStream(clientOut, 1024); このコードだとclientOutはObjectOutputStreamのオブジェクトとしては使われていません。 ストリームへの書出しを担当しているのはBufferedOutputStreamのオブジェクトです。 No.3の方も書いているように、ObjectInputStream、ObjectOutputStreamは「直列化」(シリアライゼーション)データを扱うものです。 http://java.sun.com/j2se/1.4/ja/docs/ja/api/java/io/ObjectInputStream.html > クライアントからの切断もサーバー側で感知させる クライアント側でファイル末尾まで正常に出力が完了したら、完了通知のコード(数値や文字列)を送る。完了通知のコードが届かない場合は、サーバ側でクライアントから切断されたと判断する。という方法が考えられます。
その他の回答 (6)
- koki_m
- ベストアンサー率83% (45/54)
>> UKYさん 間違いの指摘ありがとうございます。 完全に基本部分を忘れてました(笑) intの255=(3バイト省略) 11111111はbyteでは-1ですよね。 おかげ様でこれからは間違えないように書けます。 for文で書いた方が変数宣言もできてスマートですね。 こちらも参考にさせて頂きます。
- UKY
- ベストアンサー率50% (604/1207)
>> koki_mさん while((b = (byte)fis.read()) != -1) { os.write(b); } これはまずいですよ。先に byte にキャストして比較すると読み込んだバイトデータとして 0xff が入っていたときにそこで処理が終わってしまいます。 必ず int のまま比較して、後で byte にキャストしないと。 for (int b; (b = fis.read()) != -1; ) { os.write((byte) b); }
- koki_m
- ベストアンサー率83% (45/54)
すいません。No.4の訂正です。 [クライアント]の while((b = (byte)fis.read()) != -1) { os.write(b); c++; } は、 while((b = (byte)fis.read()) != -1) { os.write(b); } です。
- UKY
- ベストアンサー率50% (604/1207)
基本的には、バイナリデータを読み取るだけなら FileInputStream だけでできます。というか、InputStream と名の付くものはすべてバイナリデータを読み取る機能を備えています。 問題は、読み取ったデータをどのように扱いたいかということです。 例えばテキストファイルを読み込む場合、単純にバイナリデータとして読み取っただけでは意味がなく、それを文字列に変換してやらなければいけません。そのためには InputStream に InputStreamReader を「かます」とか、読み取ったデータをデコーダに掛けるなどといったことをします。 あるいは JPEG 形式の画像ファイルを読み込む場合、読み込んだデータを画像処理プログラムに渡すなどといったことをするでしょう。 ObjectInputStream は、「直列化」という特殊な方法で作られたバイナリデータをオブジェクトに復元するために使います。つまり、そういう特殊な形式のデータしか読み込むことができません。
補足
みなさま色々アドバイス頂き、ありがとうございます。 今やろうとしている事は、Socket通信でクライアントからサーバーへバイナリデータ(約8K)を送信させようとしています。 送信する1回のバイナリデータのサイズは不確定で、多数のクライアントから1つのサーバーに大量のデータが送信されることが 予想されています。 そこで色々調べていたのですが、どうやら『JAVAのSocket通信で、1回の送信で送れる最大バイト数は1024byte』らしいとの事でした。 そのため、送受信ともに1024バイトずつ処理を実行させようと考えました。 ソースを以下に提示します。 /* ----------------------- クライアント側 ("Test.data"ファイルはObjectOutputStreamクラスを使用して作成しています)-----------------------*/ //バイナリデータファイルの読み込み BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Test.data")); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); int i; while((i = bis.read()) != -1){ bOut.write(i); } byte[] bData = bOut.toByteArray(); //出力ストリームを取得 ObjectOutputStream clientOut = new ObjectOutputStream(soket.getOutputStream()); BufferedOutputStream clientBuf = new BufferedOutputStream(clientOut, 1024); //データ長を送信 int length = bData.length; int offset = 0; while(length > 1024){ clientBuf.write(bData, offset, 1024); offset += 1024; length2 -= 1024; } if (length > 0 ) { clientBuf.write(bData, offset, length); } clientBuf.flush(); clientBuf.close(); /* ----------------------- サーバー側 --------------------------------------------------------------------------------------------------*/ //入力ストリームを取得 ObjectInputStream serverIn = new ObjectInputStream(socket.getInputStream()); BufferedInputStream serverBuf = new BufferedInputStream(serverIn); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); //データ長を受信 byte[] bufByte = new byte[1024]; int length = serverIn.readInt(); while (length > 1024) { serverBuf.read(bufByte, 0, 1024); bOut.write(bufByte, 0, 1024); length -= 1024; } if (length > 0) { serverBuf.read(bufByte, 0, length); bOut.write(bufByte, 0, length); } byte[] bData = bOut.toByteArray(); String message = new String (bData); if(message == null){ //クライアントからの切断!? } 上記のようにしているのですが、サーバー側のObjectInputStreamでreadInt()メソッドをコールした際に 戻り値が負の整数となってしまいます。 また同時にクライアントからの切断もサーバー側で感知させるようにもしたいです。 この他の手法でもまったく構いませんので、何かよい解決方法をご教授頂きたいと思います。 よろしくお願いいたします。m(_ _)m
- koki_m
- ベストアンサー率83% (45/54)
こんばんは。 > 内部整合検査に違反した場合にスローされる おそらく読込もうとしたファイルが、オブジェクトをバイナリで書き出したファイルではない。ということだと思います。 指定ファイル(C:\\aa\\ddd.txt)がObjectOutputStreamで書き出したバイナリデータであれば上記例外は出ないようです。 - - - - - - - - - - - - - - - - - - - - - import java.io.*; public class Test { public static void main( String[] args ){ try{ String testString = "This is a test."; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\aa\\ddd.txt")); oos.writeObject(testString); oos.close(); FileInputStream fis = new FileInputStream( "C:\\aa\\ddd.txt" ); //FileInputStream fis = new FileInputStream( "C:\\aa\\aa.jpg" ); ObjectInputStream ois = new ObjectInputStream( fis ); String returnString = (String)ois.readObject(); System.out.println(returnString); ois.close(); fis.close(); } catch(Exception w){ w.printStackTrace(); } } } - - - - - - - - - - - - - - - - - - - - > 正しく実行させるにはどのようにすればいいのでしょうか? 何が正しい実行なのかがちょっと分からないですが、ソースコードから2通り推測できます。 ・書き出したオブジェクトを読み込みたい →上記のように書き出したオブジェクトファイルを指定してください。 (ファイル名は.txtのままですが、oos.writeObject(testString);でオブジェクトファイルとなります) ・テキストや画像を読み込みたいが、間違ってObjectInputStreamを使おうとしていた →テキストファイルはBufferedReader等、画像はDataInputStream等を使用します。
お礼
詳しい回答ありがとうございます。 バイナリデータを読み込むというのが目的でした。 でObjectInputStreamクラスを使用したのですがそもそも 使い方が間違っているのでしょうか?
- UKY
- ベストアンサー率50% (604/1207)
何だかとんちんかんなことをしているような……。 ObjectInputStream はどのような機能を持ったクラスでどのようにして使うかを知らずに、出任せでプログラムを書いていませんか? とりあえず、このソースを読んだだけでは何をするためのプログラムを作ろうとしているのかよくわからないので、説明をお願いします。
補足
回答ありがとうございます FileInputStreamクラスと ObjectInputStreamクラスを使用して バイナリファイルデータの読み込みをしたいと思っています。 読み込むデータのサイズは8Kで、Socketにてクライアントからデータの送信をさせようとしています。
補足
UKY様、koki_m様 大変ご丁寧な回答ありがとうございました。m(_ _)m おかげさまで何とか無事に送受信ができました。 ですがもう少しお聞きしてよろしいでしょうか? 実はサーバー側の仕組みなのですが、下記(1)(2)のスレッドが用意されています。 ---------------------------------------------------------------------------------- (1)クライアントから接続を受けるスレッド(1ポート毎に用意) ⇒接続を受けた後、(2)のスレッドに処理を完全に渡す (socket.accept().getInputStream()によるInputStreamを渡す) (2)へ処理を渡した後、また別のクライアントからの接続待ちを行う。 (2)クライアントからのデータ受信を行うスレッド(1クライアント毎に用意) ⇒(1)から引き継いだInputStreamにより、データの受信を行う。 受信後は、データをByteデータに加工し、また別のスレッドに渡す。 その後、再度同じクライアントからのデータ受信待ちを行う。 (1度接続されてからは切断されるまで、ひたすらデータを受けつづける) ---------------------------------------------------------------------------------- ですので1回のデータ送信後にすぐSocketをCloseするということができません。 ひたすらSocketは開いたままなのですが、1回分の送信データのみを確実にByteデータにして 保持したいのです。そしてクライアントからの切断も判断させたいのです。 何度もご質問して申し訳ありませんが、何卒ご回答宜しくお願いいたします。