dポイントプレゼントキャンペーン実施中!

このようなソースがあり

-------------------------------------------------
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.Serializable;
import java.io.ByteArrayInputStream;


public class TTT{

public static void main( String[] args ){

try{


FileInputStream fis = new FileInputStream( "C:\\aa\\ddd.txt" );
//FileInputStream fis = new FileInputStream( "C:\\aa\\aa.jpg" );
ObjectInputStream ois = new ObjectInputStream( fis );

}catch( Exception w ){
w.printStackTrace();
}

}


}
-------------------------------------------------


これをコンパイルをかけて実行すると
java.io.StreamCorruptedException: invalid stream header
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:737)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:253)
at TTT.main(TTT.java:24)

となってしまいます。

読み込むファイルをコメント部分の画像ファイルに
変えてみても同様です。

StreamCorruptedExceptionの内容はJAVAのAPIドキュメントでは読み込まれた制御情報が、内部整合検査に違反した場合にスローされると説明がありますがこれはどういう意味でしょうか?

また正しく実行させるにはどのようにすればいいのでしょうか?
よろしくお願いします。

A 回答 (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 …

> クライアントからの切断もサーバー側で感知させる
クライアント側でファイル末尾まで正常に出力が完了したら、完了通知のコード(数値や文字列)を送る。完了通知のコードが届かない場合は、サーバ側でクライアントから切断されたと判断する。という方法が考えられます。

参考URL:http://www.hellohiro.com/socket.htm

この回答への補足

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データにして
保持したいのです。そしてクライアントからの切断も判断させたいのです。

何度もご質問して申し訳ありませんが、何卒ご回答宜しくお願いいたします。

補足日時:2005/09/21 21:25
    • good
    • 0

>> UKYさん



間違いの指摘ありがとうございます。
完全に基本部分を忘れてました(笑)
intの255=(3バイト省略) 11111111はbyteでは-1ですよね。
おかげ様でこれからは間違えないように書けます。

for文で書いた方が変数宣言もできてスマートですね。
こちらも参考にさせて頂きます。
    • good
    • 0

>> 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);
}
    • good
    • 0

すいません。

No.4の訂正です。
[クライアント]の
while((b = (byte)fis.read()) != -1) {
os.write(b);
c++;
}

は、
while((b = (byte)fis.read()) != -1) {
os.write(b);
}
です。
    • good
    • 0

基本的には、バイナリデータを読み取るだけなら 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

補足日時:2005/09/21 15:46
    • good
    • 0

こんばんは。


> 内部整合検査に違反した場合にスローされる
おそらく読込もうとしたファイルが、オブジェクトをバイナリで書き出したファイルではない。ということだと思います。

指定ファイル(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等を使用します。
    • good
    • 0
この回答へのお礼

詳しい回答ありがとうございます。

バイナリデータを読み込むというのが目的でした。
でObjectInputStreamクラスを使用したのですがそもそも
使い方が間違っているのでしょうか?

お礼日時:2005/09/21 12:16

何だかとんちんかんなことをしているような……。



ObjectInputStream はどのような機能を持ったクラスでどのようにして使うかを知らずに、出任せでプログラムを書いていませんか?

とりあえず、このソースを読んだだけでは何をするためのプログラムを作ろうとしているのかよくわからないので、説明をお願いします。

この回答への補足

回答ありがとうございます

FileInputStreamクラスと
ObjectInputStreamクラスを使用して
バイナリファイルデータの読み込みをしたいと思っています。
読み込むデータのサイズは8Kで、Socketにてクライアントからデータの送信をさせようとしています。

補足日時:2005/09/20 20:44
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!