FTPサーバからのレスポンスメッセージが表示されない問題

このQ&Aのポイント
  • FTPサーバからのレスポンスメッセージを取得する際に、処理が進まなくなる問題が発生しています。
  • writeResponseMsgメソッド内で、すべてのレスポンスメッセージを表示し切った後に再びreadLine()を呼び出すが、処理が動かなくなります。
  • 利用環境はJava J2SE 1.5.0.14、FTPサーバはvsftpd 2.0.1、Eclipseは3.2.1です。解決方法やソースコードの誤りについてのご指導をお願いします。
回答を見る
  • ベストアンサー

ftpサーバからのレスポンスメッセージをすべて取得したところで処理が進まなくなる

下記のようなプライベートメソッドにおいて FTPサーバからのレスポンスメッセージをコンソールに表示し FTPのやりとりを確認しているのですが、 すべてのレスポンスメッセージを表示し切ったあと、 最後に再びreadLine()を呼び出すところでそのまま動かなくなります。 (Eclipseのデバッグからステップ実行で確認しております) private void writeResponseMsg(BufferedReader reader) throws Exception {  String strLine = new String();  while ((strLine = reader.readLine()) != null){ // ←この位置で止まる。   System.out.println(strLine);  } } 例えば、下記のようなFTPサーバにログインしたときのレスポンスの表示に上記メソッドを利用すると コンソールには正しくすべてのメッセージを表示するのですが 最後のメッセージを取得し終わったあとにreadLine()からnullが返ってくることはなく、 その位置から処理が動きません。 = = = = = メイン処理 = = = = = Socket socket = new Socket(hostName, portNo); PrintWriter writer = new PrintWriter(socket.getOutputStream()); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); writer.println("USER " + loginID); writer.flush(); writer.println("PASS " + password); writer.flush(); writeResponseMsg(reader); = = = = = コンソール = = = = = 220 (vsFTPd 2.0.1) 331 Please specify the password. 230 Login successful. 何かソースコードの誤りや解決方法をご存知の方がいらっしゃいましたら ご指導よろしくお願い申し上げます。 = = = = = 利用環境 = = = = = Java J2SE 1.5.0.14 FTPサーバ vsftpd 2.0.1 Eclipse 3.2.1 以上

  • Java
  • 回答数2
  • ありがとう数18

質問者が選んだベストアンサー

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

> 私的にはreadLineはBufferedReaderのメソッドなので > ソケットは関係ないように思えてしまいます。  まずBufferedReaderは、Readerをバッファリングするためのものです。逆に言えば、バッファリングする以外は単なるReaderです。これは良いですね?  次に、今回の話ではReaderはInputStreamReaderであり、「リードの大本となるのがInputStream」です。で、そのInputStreamはSocketInputStream。  ここまでくると分かると思うけど、BufferedReader.readLine()はSocketInputStream.read()に依存する。依存というか、ソケットのreadが返ってこないとバッファリングしようがしまいがリーダーのreadLineが返ってくる訳ないよね。ここで言いたいのは、BufferedReaderは本質的ではなく、今回の問題はInputStreamReaderとSocketInputStreamの関係によるものだという事。  SocketInputStreamのEOSについては後述。 > 同じストリームであろうとファイルに対するストリームの場合と > ソケットに対するストリームの場合とで動きが変わるものなのでしょうか?  Windows上の一テキストファイルからのFileInputStreamだと、ファイルが途中で改変されない限り確実にEOSがあってそれはEOFになる。だけど、Socket(というかプロセス間通信は全て)プロセス間通信が「切断された」時がEOSであって、「受信が途絶えた」はEOSじゃなくてひょっとしたら2秒後にまたデータが来るかも知れない(ready)し、単純にネットワークが遅延しているだけかも知れない。  そもそもですよ。FTPのレスポンスが3MiB/秒でトロトロ送られてくるとしましょう。そうすると、現行の一般的なパソコンでこのプログラムを走らせたとしても、FTPのレスポンスなんて1文字目からSocketInputStream.read()の時点でたどり着いてない訳ですよ。受信が途絶えた状態です。本当にそこでEOSにしちゃって良いんですか? 駄目だよね。「受信が途絶えた != EOS」の証明には十分なると思うけどどうでしょう。 > ストリームはそれを取り出すためのただのパイプ  パイプが分かっててなんでそこが分からないかな。パイプだってパイプ元のプロセスがPipeOutを閉じないと「パイプの終了(End Of Pipe)」にならないでしょ?  「閉じる」って事と「使わなくなる(送信側の場合はOutputしなくなる、受信側の場合はInputしなくなる)」事は違う。これはStreamがPipeなんだとしたらそれこそガチでしっくりくる所じゃないっすか。 > FTPとやりとりするためにわざわざ別スレッドを立てる理由  FTPがどうのこうのSocketがどうのこうと言うよりは、非同期入出力の部分を外出しにするのが普通という事かな。  逆に、FTP操作の完了を逐一待って次の処理に進むという作りじゃないといけないんだよ! という場合は、メインスレッドでストリーム操作を行っても良いと思う。  私の個人的な考えだとそれでもスレッド同期を使ってソケットスレッドの完了を待機しておく作りの方が良いとは思うんだけどね。非同期通信をずっと待っていると、メインスレッドがいわゆる「応答無し」の状態になるよね。それがマルチプロセスのOS上でお行儀が悪いように感じる。これは私のポリシーみたいなもんだからあなたなりの方法を取ってください。実際UNIX方面では「そんなのを管理するのはOSの責任だろ」っていう風潮があるよね。 > ブロック  今回の話でブロックと言っているのは、BufferedReader.readLine()はInputStreamReader.read()がブロックされる限りブロックされ、InputStreamReader.read()はSocketInputStream.read()がブロックされる限りブロックされる。  結果SocketInputStream.read()がブロックされずかつ'\n'が届くまでBufferedReader.readLine()はブロックされるのだが、それはそうなる「べきであり」、そこでEOSが返ってくるのはおかしい。これは今まで述べた通り。  繰り返しになるかも知れないが、1つのFTPレスポンスがごっそり返ってきたからと言って、そこでSocketが閉じられる訳ではない。でないと連続でコマンドを発行する事ができなくなる(LSしてBINしてPASVしてGETってのができなくなるよね)。  なので、1つのレスポンスブロックが全て届いたと判定するのはプログラム側の責任であり、そこで奴が勝手にEOSにしちゃってくれちゃってもかえって困るのでございやす。

その他の回答 (1)

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

> 最後に再びreadLine()を呼び出すところでそのまま動かなくなります。  そりゃそうだよ・・・・。レスポンスが全てかえってきた後、そのソケットは閉じられる(End Of Streamになる)んじゃなくてブロック(次のデータ待ちの)状態になるんだから。  reader.ready()を確認する、socketでタイムアウトを設定し、InterruptExceptionをキャッチする、などで場当たり的な対処はできると思うけど。  本則としては、FTPクライアントの機能、つまりサーバからのレスポンスを解析して「あ、コマンドが終わったな」と判断するのはプログラムが責任をもたなければならない。普通はFTPサーバとやりとりするだけの専用スレッドを作って、読んではどこかのメモリに吐き出させて、メインスレッドかレスポンス解析ポーリングスレッドでFTPコマンドの結果を解析するのでしょう。

kyonn2008
質問者

お礼

ご回答ありがとうございます。 >> レスポンスが全てかえってきた後、そのソケットは閉じられる(End Of Streamになる)んじゃなくて 私的にはreadLineはBufferedReaderのメソッドなので ソケットは関係ないように思えてしまいます。 JavaDocによるとreadLineメソッドは「ストリームの終わりに達している場合は null」とあるので 読み込むデータがなければnullを返してくるように思ってしまうのですが‥。 未熟なためによく分かっていないのですが 同じストリームであろうとファイルに対するストリームの場合と ソケットに対するストリームの場合とで動きが変わるものなのでしょうか? >> ブロック(次のデータ待ちの)状態になるんだから。 お教え頂いたreadyメソッドのJavaDocを見てみると、戻り値の解説にも 「次の read() が入力をブロックしないのが確実な場合は true、そうでない場合は false。false が返されても、次の読み込みが確実にブロックするというわけでない」 とあったのですが、ストリームには「ブロックの状態」と「ブロックしていない状態」というものがあるのでしょうか? anmochi様の回答によるとブロック状態とは「次のデータ待ちの状態」とあるのですが ストリームに「データ待ちの状態」が存在することがよくわかりません。 「ソケットにサーバからのレスポンスが書き込まれ、ストリームはそれを取り出すためのただのパイプ」 というのが私の理解ですが、ストリームがわざわざデータを待つのですか? どちらかと言えばそれはソケットの役割のように思えてならないのですが‥。 また「ブロックの状態」と「ブロックしていない状態」とは具体的にそれぞれどういう場合に起こりうるものなのでしょうか? >> 普通はFTPサーバとやりとりするだけの専用スレッドを作って すみません、FTPとやりとりするためにわざわざ別スレッドを立てる理由も私が無知なためによくわかりませんでした。 もしよろしければその辺りもご解説いただけると幸いでございます。 質問ばかりで大変恐縮ですが、よろしくお願い申し上げます。

関連するQ&A

  • この各行のプログラムの意味を教えてください!

    各行のプログラムがなにをしてるかちょっとわからないので誰か教えてください><お願いします! PrintWriter out=new PrintWriter(socket.getOutStream(),true); out.println("hello,World!"); BufferedReader in=new BufferedReader( new InputStreamReader(socket.getInputStream()); String result=in.readLine();

    • ベストアンサー
    • Java
  • 文字化けします。

    お世話になります。 ソケットでサーバとクライアントを接続しています。 コーディングの概略は次の通りです。 【クライアント】 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(),"EUC_JP")); out.println("かきくけこ"); System.out.println(in.readLine()); 【サーバ】 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(),"Shift_JIS")); String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println(inputLine); out.println(inputLine); //←これはクライアントでも文字化けしない。 String s1 = "あいうえお"; String u1 = new String(s1.getBytes("xxxxx"), "yyyyy"); out.println(u1); //←文字化けする。 } お伺いしたいのは文字コード変換についてです。 サーバ:RedhatLinux クライアント:Windowsでして、 サーバ側コーディングの String s1 = "あいうえお"; としている文字列を クライアント側の // 読み込んだデータを表示 System.out.println(in.readLine()); で表示したいのですが、文字が化けて(????←このようになります)困っています。 文字コード変換しなければいけないと思うのですが、 getBytesを使うのでしょうか? もしgetBytesを使うならどのように書けばいいのかがわかりません。 分かりにくい説明で申し訳ありませんが、 ご教授ください。宜しくお願いします。

    • ベストアンサー
    • Java
  • Vectorの使用法

    現在、課題の作成中なんですけどうまくいかないので教えてください。 ファイルに表示されているアクセスログファイルを別のファイルに表示させるコードなんですが、 import java.util.*; import java.io.*; public class AccessLog{   public static void main(String args[]){     if(args.length != 1){  System.exit(0);     }     String filename = args[0];     Vector vector = new Vector();     try{      String line ; BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filename))); while((line = reader.readLine()) != null){ vector.addElement(line); writer.println(vector); } }catch(IOException e){   System.out.println(e); }   } } Vectorの使い方がよくわからないので教えてください。宜しくお願いします。

  • javaサーバーとポートを入力してメッセージ

    こんにちは、java初心者なのですが、やり方がいまいちわかってないので教えてください。 このプログラムでは、プロンプトからホストナンバーを入力して、フレームが表示されるようになってるんですが、実行するときに、フレーム(swing)を先に表示させて、サーバーとポートを入力してから 数字などを入力できるようにしたいのですが、どうしたらいいのかわからないので教えてください。 サーバーのクラス public class ThreadedEchoServer { public static void main(String[] args) { int i = 1; try { ServerSocket s = new ServerSocket(7776); for (;;) { Socket incoming = s.accept(); System.out.println("Spawning " + i); new ThreadedEchoHandler(incoming, i).start(); i++; } } catch (Exception e) { System.out.println(e); } } } class ThreadedEchoHandler extends Thread { private Socket incoming; private int counter; public ThreadedEchoHandler(Socket i, int c) { incoming = i; counter = c; } public void run() { try { BufferedReader in = new BufferedReader(new InputStreamReader(incoming.getInputStream())); PrintWriter out = new PrintWriter(incoming.getOutputStream(), true); out.println("Hello! Please send me an number (0 to exit)."); boolean done = false; while (!done) { String str = in.readLine(); if (str == null) done = true; else { try { int i = Integer.parseInt(str); if (i == 0) { out.println("End !!!"); done = true; break; } String forClient = (i % 2) == 0 ? " You are wrong " : " You are write "; out.println(forClient); } catch (Exception e) { out.println("Please send a number!"); } } } incoming.close(); } catch (Exception e) { System.out.println(e); } } }

    • ベストアンサー
    • Java
  • HttpのResponseが文字化け

    以下のプログラムの結果が文字化けします。 正しい文字コード(EUC-JP)にしているのですが 文字化けしてしまいます。 解消方法を教えて下さい。 ----- import java.net.*; import java.io.*; public class HelloWorldSocketClient { public static void main(String[] args) throws Throwable{ Socket socket = new Socket("www.sumishinam.co.jp", 80); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out.println("GET / HTTP1.0\n"); String buff; while((buff=in.readLine())!=null){ System.out.println(new String(buff.getBytes(),"EUC-JP")); } out.close(); in.close(); socket.close(); } }

  • ソケットにおけるメッセージの送受信について

    いつもお世話になっています。 ソケットに関して質問します。 下記の手順でソケットを使用したいと考えています。 クラスAとクラスBが存在する。 1)クラスA:ソケット通信でメッセージ送信 2)クラスB:ソケット通信によるメッセージを取得 3)クラスB:ソケット通信で返信する 4)クラスA:ソケットによるメッセージを再取得する このとき、クラスAがクラスBによるメッセージ再信が、30秒以内にこなければ、ソケットを閉じる それにあたってソースを1クラスで実行できるよう書き変えたいのですが、どうも上手くいきません。 実行したいメソッド手順が ソケットを開く openSocket ソケットにメッセージ送信 sendMsg ・・・1と3はこのメソッドを使用 メッセージ取得 getMsg ・・・2と4はこのメソッドを使用 ソケットを閉じる closeSocket の順です。 自分で一度考えてみたソースは以下です。 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; public class Messaneger{ private Socket socket; private BufferedReader in; // ソケットを開く public void openSocket() { ServerSocket serverSocket; try { serverSocket = new ServerSocket(5555); System.out.println("クライアントからの接続をポート5555で待ちます"); // クライアントからの接続を待ちます Socket socket = serverSocket.accept(); System.out.println(socket.getInetAddress() + "から接続を受付ました"); // 出力ストリームを取得 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 入力ストリームを取得 in = new BufferedReader( new InputStreamReader( socket.getInputStream())); } catch (IOException e) {} } //ソケットを閉じる public void closeSocket() { try { socket.close(); } catch (IOException e) {} } /** * ソケット通信でメッセージを送信 * @param 送信するメッセージ */ public void setMsg(String sendMsg) { try { socket = new Socket("localhost", 5555); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // 入力ストリームを取得 in = new BufferedReader( new InputStreamReader( socket.getInputStream())); //サーバーにメッセージ送信 out.println( sendMsg ); //送信メッセージdata System.out.println(sendMsg); } catch (IOException e) {} } /** * ソケット通信でメッセージを取得 * @return msg サーバーに渡されたメッセージ */ public String getMsg() { String getMsg = ""; BufferedReader in; try { in = new BufferedReader( new InputStreamReader( socket.getInputStream())); System.out.println(in.readLine()); } catch (IOException e) {} return getMsg; } } 参考URL:http://www.hellohiro.com/socket.htm 宜しくお願いします。

  • ファイル出力時の

    以下のソースでファイル出力するのですが、 BufferedReader br = new BufferedReader(new FileReader(new File(args[0]))); PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(args[1]))); //一行読み込みを行った文字列を格納 String line; //一行読み込み while((line = br.readLine()) != null){ //一行書き込み pw.write(line); System.out.println(line); } コンソールへの出力は正常に出力されるのですが、 ファイルへの出力は改行されずに一行で出力されてしまいます。 どうすればファイル出力も正常になるか教えてください。

  • Socket通信でのデータの受け渡し

    サーバーが受けたデータを、クライアントに返す プログラムですが、 サーバーは、クライアントからのデータを、 while(true){ BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream())); str=br.readLine(); if(str!=null){ PrintWriter pr=new PrintWriter(s.getOutputStream()); pr.println(str); pr.flush(); }}で、返すようにしています。 クライアントも大体同じです(PrintWriterは、 Buttonを押した時に、取得しています)。 考え方としては、inputstreamは、無限ループで、 取得し、outputstreamは、必要におうじて、取得し データを送っています。 しかし、これだと、一度データを送ると、次から データの受け渡しが出来なくなります。 socketか、inputstreamかoutputstreamのどれかが、 閉じるか、使えなくなるのだと思いますが、 いつもここで、悩んでいます。 (また、無限ループで、BufferedReaderオブジェクト をつくり続けるというのも、ちょっと不安)。 どこに問題があるのか、分かる人教えてください。 お願いします。

    • ベストアンサー
    • Java
  • Java初心者です。どうぞお教えください。

    CSVファイルを読み込んで、ソートしてから、現在日時のファイル名でCSVで書き出します。以下のファイルでは、CSVで書き出せません。現在日時のファイル名をつける方法もよくわかりません。どうぞ教えてください。importは省略しています。 public class CSVdata { public static void main(String[] args) { String fname = " " ; ArrayList<String> list = new ArrayList<String>(); String save_filename = getFileName(); if(args.length>0) { fname = args[0]; } try { BufferedReader reader = new BufferedReader( new FileReader(fname) ); String Line = null; while ((Line = reader.readLine()) != null) { list.add(Line); } reader.close(); Collections.sort(list ); PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(save_filename + ".csv"))); for (String string : list) { writer.println(string); } writer.close(); } catch (FileNotFoundException e) { System.out.println("ファイルがありません"); } catch (IOException e) { System.out.println("ファイルがありません"); } } public static String getFileName(){ Date d = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); return sdf.format(d); } }

  • FileWrite()メソッド

    すいません、少しお時間を頂きたいと思います。今コマンドライン上でファイルを作ろうとしています。ソースは結城浩氏の著書で使われている、ネット上から無料でダウンロードできるソースを使っています。以下の通りです。 import java.io.*; public class FileWrite{ public static void main(String[] args) { if (args.length != 1) { System.out.println("使用法:java WriteFile1 作成ファイル"); System.out.println("例:java WriteFile1 output.txt < input.txt"); System.exit(0); } String filename = args[0]; try { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filename))); String line; while ((line = reader.readLine()) != null) { writer.println(line); } reader.close(); writer.close(); } catch (IOException e) { System.out.println(e); } } } ところが、これをコンパイルして実行までは出来るのですが、いざ因数(=ファイル名)を与えるとコマンドプロンプトがプロンプトのない状態で停止してしまいます。 javacのあるディレクトリ>java FileWrite abc.jsp と入力しています。(FileWriteはクラス名です。) また、拡張子の有無は関係ないようです。 何が悪いのでしょうか? よろしくお願いします。

    • ベストアンサー
    • Java

専門家に質問してみよう