• 締切済み

closeされたsocketへの動作について

現在、javaを使用してプログラムを作成しているのですが、closeされたsocketに対しての動作について質問があります。 クライアント側のプログラムに ObjectOutputStream.writeObject(send); ObjectOutputStream.flush(); ObjectInputStream.readObject(); というものがあったとします。 サーバ側でsocketをcloseし、上記のプログラムを走らせた場合writeObjectにてsocketExceptionを検出する場合とreadObjectでEOFExceptionを検出する場合の2パターンが起こりうるのですが、これはなぜでしょうか? なお、上記のwriteObjectの引数のsendはSerializeを継承して作成した自作クラスのオブジェクトです。

  • Java
  • 回答数1
  • ありがとう数0

みんなの回答

  • anmochi
  • ベストアンサー率65% (1332/2045)
回答No.1

 はい、ではTCPがどういう風に開始、終了するのかと合わせて確認してみましょう。  まず、 1.Socketはサーバ側が待ち受け(Listen)しており、 2.クライアント側が接続(connect)して、 3.サーバ側が受け入れ(Accept)して通信が可能になる。 この時、TCP層では2のタイミングでクライアントからサーバにSYNパケットというものが送信される。その後、3でサーバからクライアントにSYN+ACKパケットが返送され、最後にクライアントがACKパケットをサーバに送りつけて完了だ。これで双方は「相手と現在つながっている」という認識を行う。  そして終了だが、ソケットアプリケーション(JavaのSocketクラスも例外ではない)は、ソケットを閉じる時に以下の手順に従わなくてはならない。以下はJavaのSocketOutputStreamとSoketInputStreamでイメージしやすいように記述する。 1.相手に届けたいものがあれば気の済むまで届ける。 2.ライト側すなわちSocketOutputStreamをclose()する。 3.EOFになるまでSocketInputStreamから読み出す。EOFになったのに読むとEOFException。 4.EOFになったSocketInputStreamをclose()する。 これを双方が行わなくてはいけない。一方がそれこそ一方的に切断してしまうと、もう片方はConnection Reset by peerになってしまう。 分かるだろうか。お互いがOutputを切る事で相手に通信の終了を伝え、相手はInputがもうない事を確認した上で切断する。この時、TCPでは双方とも相手にFINというパケットを投げる事で「こっちはもう出力側をクローズしたぜ。」と伝える。  前置きが長くなったが、サーバが勝手に接続を切った時、クライアント側でWriteのタイミングで例外が起きたりReadのタイミングで例外が起きたりする、とは、Writeのタイミングで例外が起きたり起きなかったりする、と言い換える事ができる。  Write側は相手のRead側とつながっているけど、前述の通りWriteが先に切るのがお作法というかもう不文律レベルの話だ(いや、どっかで明文化されているのかも知れんけど)。なので、Write側は「相手が勝手に切っちゃう」なんて状況を想定していない。  で、相手が勝手に切っちゃってた時の動きだけど、スローされない場合があるのは、サーバ側のプログラムでSocketをclose()した後、実際にTCPを切断するタイミングを制御するのはOSであり、OSはしばらく待っているからだ(OSから見ればクライアントからFINパケットを受け取っていないので待つしかない)。クライアント側のプログラムはWriteしてサーバには届いたけど、サーバ側のプログラムはもうSocketInputStreamを閉じてる認識なので読み出す事はないという状態だ。サーバ側のOSとしては自分は相手にFINを送ったのに相手からはそれに対するACKも送られてこないし、もう通信は終わらなきゃいけないのに相手からはFINが送られてこない、という状態になっている。しばらくは待ってみるけど、OSが待つと決めた時間が過ぎる(タイムアウト)と、それも切ってしまう。切られてなけりゃクライアント側のWriteは成功するし、切られてたら失敗する。このタイミングはOSによりけりだ。  ついでに、ReadのタイミングでEOFExceptionなのは、前後のプログラムをみてみないと分からないな。EOFか確認せずにReadしたらそれは当然EOFExceptionになる。

関連するQ&A

  • javaのObjectOutputStreamのエラーが消えません

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt")); oos.writeObject(text); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("text.txt")); text = (LinkedList<String>)ois.readObject(); 最後の分のois.readObject()のなぜかClassNotFoundExceptionになってしまって先に進めないです。 これではあまり意味がないんですが、わかる方がいらっしゃいましたら教えてください

  • SocketのSend関数でのCLOSEの検知 [Linux]

    Linux環境でSocket(dm:PF_INET,type:SOCK_STREAM)を使用しての、 Client&ServerプログラムをCで作成しているのですが、 そこでのSend関数の使い方についてご助力ください。 Client&Serverプログラムは下記のような動きをします。 [Client] ServerへConnectした後、複数のDataを数秒間隔でServerへ 送信(send関数使用)します。受信(recvやread関数等)は、 一切行いません。 [Server] ClientからのConnectを受け付けた後、Clientから受信(recv関数 使用)したDataを標準出力へ表示する。送信(sendやwrite関数 等)は、一切行いません。 さて、ここでもしClientプログラムがCloseを発行したり、マシン DOWN等の理由でConnectionが切断され、Server側のSocketが CLOSE_WAIT状態になった場合、Bufferに溜まっていたDataを すべて受けきった後、recv関数が0を返してくれるので 相手が終了したことがわかります。 ここからが質問のMainです。 では、もしServerプログラムがCloseを発行したり、マシン DOWN等の理由でConnectionが切断され、Client側のSocketが CLOSE_WAIT状態になっても、CLOSE_WAIT直後のsend関数が なぜか正常に処理されてしまいます。無論このDataは、 Server側は受け取りません。この次のsend関数実行時に EPIPEが返ってくるので、ここでようやくSocketが切断された ことが判ります。 これを何とかCLOSE_WAIT状態になった直後から、send関数で 切断を検知できるようにできないでしょうか。 よろしくお願いします。 以上

  • "try{}catch(){}"文で"close()"はどのように書けばよいのでしょうか。

    こんにちは、片岡と言います。 プログラム1は、Java言語で学ぶデザインパターン入門(結城浩さん著)の 433ページを参考にして書きました。 私は、プログラム1のclose()の書き方よりもプログラム2のようなclose()の書き方が、 良いと思っています。 なぜならば、プログラム1では、out.writeObject(memento)行の例外によって、 close()が実行されないからです。 私のこの考え方は正しいのでしょうか。 もっと良いclose()の書き方はあるのでしょうか。 ご存知の方はいらっしゃいませんか。 ●プログラム1 public class Main { public static void saveMemento(Memento memento) { try { ObjectOutput out = new ObjectOutputStream(new FileOutputStream("game.dat")); out.writeObject(memento); out.close(); } catch (IOException e) { e.printStackTrace(); } } … //以下略 } public class Memento implements Serializable { … //以下略 } ●プログラム2 public class Main { public static void saveMemento(Memento memento) { try { ObjectOutput out = new ObjectOutputStream(new FileOutputStream("game.dat")); out.writeObject(memento); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException ex) { ex.printStackTrace(); } } } } … //以下略 } public class Memento implements Serializable { … //以下略 } なお、私の環境は以下の通りです。 OS: Microsoft Windows XP Professional SP2 開発環境:Eclipse 3.2.2 java: java version "1.4.2_13"

    • ベストアンサー
    • Java
  • javaのsocketインスタンスについて

    javaでソケット接続するプログラムを作成しています。 socketを.closeした場合、このsocketインスタンスのシステムリソースは開放されるのでしょうか? お詳しい方おられましたらよろしくお願いします。

  • Socket通信での通信異常について

    JavaでSocket通信している状態で、物理LANを抜いたりして通信異常状態を起こしても、すぐには通信異常と検知できないようで、一定期間してからSocketExceptionなどを検知します。 現在、クライアントとサーバで常に受信し続け、各々で1秒毎にSocketでメッセージを相互に送っております。 その間で物理LANを抜いても、一定期間は送信しつづけるように見え(実際受信はされていない)、 一定時間を超えると、上記のようにSocketExceptionで例外検知します。 上記のようにSocket通信で通信異常状態の場合には即検知することは無理なのでしょうか?

    • ベストアンサー
    • Java
  • JavaのSocketを使い、チャットシステムを作っています。

    JavaのSocketを使い、チャットシステムを作っています。 2ちゃんねるの掲示板やニコニコ動画のコメントの真似のようなものです。 一つのサーバーに、複数のクライアントがアクセスしてコメントを書いていくというものです。 しかしオブジェクトの送受信で困ったことになりました。 //読み込み BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); ObjectInputStream ois = new ObjectInputStream(bis); //書き込み BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); ObjectOutputStream oos = new ObjectOutputStream(bos); こうするとBufferedOutputStreamの実体化でプログラムがフリーズしてしまいます。 oisで一度なにかを読み込んだあとに実体化すればできるのですが、 マルチスレッドプログラミングを使うため、 接続があった瞬間に両方実体化しなくてはいけません。 socketからStreamを手に入れることが、一度入出力をするまで片方しかできないようです。 socketのcloneを実体化することも考えたのですが、サーバー側は serverSocket.accept()を使うためかうまくできません。 どうすればいいのでしょうか。 デバッガの領域からはずれた部分(接続、送受信等)のバグ探しで非常に滅入っています。 助けてください。お願いします。

  • Socketの使用方法について

    Socketの使用方法について サーバ側クラスA クライアント側クラスB とあり、Aは常駐しておりBから接続が合った場合に処理を行い、 処理後には待機状態に再び戻ります。 上記の場合に Aのクラスは以下のように作成しましたが、★の部分でCloseではなく、このままこのソケットを使用して待機したいです。 (ほぼ同時刻に複数のアクセスがあるため、資源の事を考えて使いまわしたいです。) どのような手段があるのかご指導お願い致します。 又、そもそもソケットに関しての理解が足りないとも思いますので、参考サイトを教えていただけると幸いです。 クラスA ServerSocket svsock = new ServerSocket(port); while (true) { Socket socket = svsock.accept(); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedWriter out = new BufferedWriter(new PrintWriter(socket.getOutputStream(), true)); // 処理結果を受信 String line; if ((line = in.readLine()) != null) { System.out.println("受け取ったメッセージ : " + line); out.write("サーバで表示。"); out.newLine(); out.flush(); } socket.close(); // ★ }

    • ベストアンサー
    • Java
  • パッケージ管理に変更されたデータObjectのリカバリー方法

    今までパッケージ管理されていなかったデータオブジェクトをパッケージ管理するよう変更したのですが、クラス名が変更されたことにより旧データオブジェクトの復旧が行えなくなりました。 前データ) class Data implements Serializable { public Data(){} } (1)オブジェクトストリームとしてExport。 Data target = new Data(); java.io.FileOutputStream ostream = new java.io.FileOutputStream("C:\\test.data"); java.io.ObjectOutputStream p = new java.io.ObjectOutputStream(ostream); p.writeObject(target); p.flush(); ostream.close(); (2)Dataオブジェクトをパッケージ管理に変更 新データ) package com.corp.etc class Data implements Serializable { public Data(){} } (3)(1)でExportしたデータをImport java.io.FileInputStream istream = new java.io.FileInputStream("C:\\test.data"); java.io.ObjectInputStream p = new java.io.ObjectInputStream(istream); Object obj = p.readObject(); istream.close(); Data data = (Data)obj; (3)の実行結果) java.lang.ClassNotFoundException: Data クラス名が変わったので、当たり前の動きとは思うのですが、旧データを今までと同じようにインスタンス化するために何か良い方法はありませんでしょうか?

  • Socket + XML

    ・クライアントはサーバへXML形式のクエリを送信し、 ・サーバからXML形式のデータを受け取る という単純なプログラムを実装しています。 しかしサーバ側の、InputStreamを引数とするXMLパースメソッドでwaitしてしまって困っています。 かといって、クライアント側のOutputStreamを閉じると、クライアントのソケット自体も閉じてしまいます。 なるべく一度のコネクションで送受信を終了させたいのですが、よい方法はないでしょうか? 一応、BufferedReaderを利用してXML文章を文字列に落としてから StringReaderをbuilder.parseの引数に与えることで解決出来てはいるのですがスマートではない気がしまして。 [Server側]  ServerSocket server_sock = new ServerSocket(12345);  Socket sock = server_sock.accept();  /* XMLパーサビルダ生成 */  DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();  DocumentBuilder builder = fact.newDocumentBuilder();  /* ドキュメント docIn の解析 */  Document docIn = builder.parse(sock.getInputStream());//ここから動かない(clientがoutputを終了するまで待っている?)  (略)  /* 結果ドキュメント docOut の構築*/  Document docOut = builder.newDocument();  (略)  /* docOut 送信 */  TransformerFactory tfactory = TransformerFactory.newInstance();  Transformer transformer = tfactory.newTransformer();  transformer.transform(new DOMSource(docOut), new StreamResult(sock.getOutputStream()));  sock.getInputStream().close();  sock.getOutputStream().close();  sock.close(); [Client側]  Socket sock = new Socket("localhost", 12345);  /* XMLパーサビルダ生成 */  DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();  DocumentBuilder builder = fact.newDocumentBuilder();  /* クエリドキュメント docOut の構築*/  Document docOut = builder.newDocument();  (略)  /* docOut 送信 */  TransformerFactory tfactory = TransformerFactory.newInstance();  Transformer transformer = tfactory.newTransformer();  transformer.transform(new DOMSource(docOut), new StreamResult(sock.getOutputStream()));  sock.getOutputStream().flush();  //sock.getOutputSream().close();//これにすると、socketが閉じてしまう  /* 結果ドキュメント docIn の解析 */  Document docIn = builder.parse(sock.getInputStream());//サーバから結果が送信されないので、クライアントはここで停止  (略)

    • ベストアンサー
    • Java
  • Cliant から Server の Socket 管理

    サーバアプリを作成しています。 ServerSocket を生成してから 基本的に read() でクライアントからの データを待機しています。 その状態でクライアント側から close 処理を行いたいのですが どういう形にすれば正しく終了できるのでしょうか? クライアントから Socket の close() を行うと 空のデータが2回送られてきます。 例外が発生しているので finally で close させようと 思ったのですがビルドエラーになります。 どうかご教授お願いします。

    • ベストアンサー
    • Java

専門家に質問してみよう