- PR -

タイマーを使ったメッセージの再送

投稿者投稿内容
fuzuki
常連さん
会議室デビュー日: 2003/08/23
投稿数: 48
投稿日時: 2003-10-03 17:47
こんにちは。
ソケットプログラミングで、相手にメッセージを送って、相手からの返事を待ち、返事が来たらその返事を表示し、一定時間待っても返事が来なければ最初のメッセージを再送してまた相手からの返事を待つということをしたいのですが、ここの返事待ちと再送の処理をどのように書けばよいか分からず困っています。
タイマーを使って一定時間待ってまた再送を行うというのは想像が付くのですが、いざ試そうとすると、どこからどこまでをタイマーのスケジュールに入れれば良いのかが分かりません。スケジュール用にTimerTaskをextendしたクラスを書いたりしてみたのですが、その中でmainで定義しているsocketなどを使う必要が出てきてしまい、うまくできませんでした。やり方が分かる方いらっしゃいましたらよろしくお願いします。

以下のソースはメッセージを送る側の簡単な例です。

-------------------------------
import java.io.*;
import java.net.*;

public class Send
{
 public static void main(String[] args)
 {
  try
  {
   String host = "127.0.0.1";
   Socket socket = new Socket(host, 8080);
   BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
   BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));

   while(true)
   {
    System.out.print("Enter string:"); // 標準入力からメッセージを入力
    String tmp = stdin.readLine();

    out.println(tmp); // 相手にメッセージを送る
    out.flush();

    String str = in.readLine(); // 相手からの返事を待つ。もし返事が来なければ一定時間後に何度か同じメッセージの再送を試みたい

    System.out.println(message); // 相手からの返事を出力
   }
  }
  catch(Exception e)
  {
   System.out.println(e.getMessage());
  }
 }
}
-------------------------------

[ メッセージ編集済み 編集者: fuzuki 編集日時 2003-10-03 17:55 ]
Kiriko
常連さん
会議室デビュー日: 2003/09/25
投稿数: 25
投稿日時: 2003-10-03 18:48
こんばんは。

このような非同期通信プログラムでは、
スレッド(java.lang.Thread)を利用するのが常套手段です。
もし、スレッドプログラミングについての知識がないならば、
書籍、Webサイトなどで学習してください。
もし、Javaでのスレッドプログラミングを理解されているの
でしたら、
相手からの返事を受信する部分、タイマーの部分を
スレッド化し、何かイベント(相手からのメッセージを受信した、
タイマーがタイムアップした)が発生した場合、
そのイベントをどこかのオブジェクト(イベントリスナー)に
通知するようにすれば可能です。
イベントを通知されたオブジェクト(イベントリスナー)では、
イベントがタイムアップならば通信相手に再度メッセージを送信し、
イベントが返事の受信であればタイマーをキャンセルするといった
プログラミングになります。
fuzukiさんのバックグラウンドが分からないので、
これだけの情報でイメージできるかどうかはかなり疑問ですが、
キーワードはスレッド(&イベントリスナー)になるでしょうか。
fuzuki
常連さん
会議室デビュー日: 2003/08/23
投稿数: 48
投稿日時: 2003-10-04 00:56
Kirikoさん
ご返事ありがとうございました。
Javaは使い始めてまだ3ヶ月なので深いところは全然分かってません。スレッドは基本的なことは知っています。
今回質問したものでは、特に非同期ではなくて良いので、タイマーだけでいけるかなと思ったのです。つまり、相手から返事が返ってくるまでは待ち続けて、何度か再送してもダメな場合はコネクションを切って終わりにしてしまっていいのです。
もう少しスレッドとタイマーについて調べてみます。
Kissinger
ぬし
会議室デビュー日: 2002/04/30
投稿数: 428
お住まい・勤務地: 愛知県
投稿日時: 2003-10-04 02:35
fuzukiさん、こんにちは。

私もスレッドプログラミングをお勧めしますが、
Socketの場合、read()の前に setSoTimeout() で
タイムアウト時間を指定する方法も良く使われます。

処理系によっては、時間の監視を行う別スレッド
からブロック中のスレッドに interruptしても
復帰しないので、タイムアウトと併用する方法が
有効なのです。

Kirikoさんの書いた方法は、同期プリミティブの
基本ですが、read()のようにIOでブロックする処理
と組み合わせるには一工夫必要で、いろいろと制約
もあります。
Kiriko
常連さん
会議室デビュー日: 2003/09/25
投稿数: 25
投稿日時: 2003-10-04 08:48
おはようございます。

引用:

Kissingerさんの書き込み (2003-10-04 02:35) より:

私もスレッドプログラミングをお勧めしますが、
Socketの場合、read()の前に setSoTimeout() で
タイムアウト時間を指定する方法も良く使われます。


おっしゃる通り、setSoTimeoutを使えば
非同期プログラミングをする必要などなく、
簡単に機能を実現できますね。

引用:

処理系によっては、時間の監視を行う別スレッド
からブロック中のスレッドに interruptしても
復帰しないので、タイムアウトと併用する方法が
有効なのです。


私は次のように認識していたのですが、
これは正しいでしょうか。
ちょっと不安になったものですから^^;

1.Socketからのread待ちをしているスレッドに
  他のスレッドからThread#interruptで
  割り込みを掛けるのはよくない。
  なぜならば、read()メソッドの中で
  さらに別のスレッドが実際にソケットからのデータ受信
  待ちをしている可能性があり、そのスレッドに対して
  割り込めるかどうかわからないからである。
  この認識は正しいでしょうか?
  また、Kissingerさんが上記でおっしゃっていることは
  これと同じことでしょうか。

2.Socketをクローズすることで、Socketからのread待ちを
  していたスレッドを復帰させることは問題ない。

3.スレッドで実現したTimerをキャンセルしたい場合、
  wait, notify(All)によって、眠っていたTimerスレッドを
  起こすようにすれば、処理系に依存したコードを書く必要がなく、
  問題ない。

引用:

Kirikoさんの書いた方法は、同期プリミティブの
基本ですが、read()のようにIOでブロックする処理
と組み合わせるには一工夫必要で、いろいろと制約
もあります。


いくつか教えていただけないでしょうか
このような書籍からは得られない、経験に基づく情報は
私の知識欲をくすぐります(笑)。
Kissinger
ぬし
会議室デビュー日: 2002/04/30
投稿数: 428
お住まい・勤務地: 愛知県
投稿日時: 2003-10-04 10:48
Kirikoさん、とても詳しいみたいなので説明は
要らないかも知れませんが、
引用:

なぜならば、read()メソッドの中で
さらに別のスレッドが実際にソケットからのデータ受信
待ちをしている可能性があり、そのスレッドに対して
割り込めるかどうかわからないからである。
この認識は正しいでしょうか?
また、Kissingerさんが上記でおっしゃっていることは
これと同じことでしょうか。


そうです。

引用:

2.Socketをクローズすることで、Socketからのread待ちを
  していたスレッドを復帰させることは問題ない。

3.スレッドで実現したTimerをキャンセルしたい場合、
  wait, notify(All)によって、眠っていたTimerスレッドを
  起こすようにすれば、処理系に依存したコードを書く必要がなく、
  問題ない。


私も、そう思います。

無応答タイムアウトと判断された状況で、他スレッドから closeする
ことは禁止されていないはずなので、有効な方法と思います。

あと、Socketの read(), ServerSocketの accept()ではタイムアウト時に
他スレッドが立てた中断フラグをチェックするのは簡単で、安全です。

ただし、read()の中断の場合、中断直後に遅れてやってきたデータが
その後のシーケンスを乱すことがあるので、データの文脈を
状態遷移処理で確認しながら処理するか、それが出来ない場合は一度
ソケットを closeするとか、… 提供するサービスの仕様によるので
どれが良いか一概に言えませんが、あると思います。

# 私の少ない経験からで、参考にならないかも知れませんが、
# 間違いなどありましたら、指摘してください。
Kiriko
常連さん
会議室デビュー日: 2003/09/25
投稿数: 25
投稿日時: 2003-10-04 19:42
こんばんは。

Kissingerさん
返信ありがとうございます。

引用:

あと、Socketの read(), ServerSocketの accept()ではタイムアウト時に
他スレッドが立てた中断フラグをチェックするのは簡単で、安全です。


「簡単で、安全です。」というところ、なるほどと思いました。

引用:

ただし、read()の中断の場合、中断直後に遅れてやってきたデータが
その後のシーケンスを乱すことがあるので、データの文脈を
状態遷移処理で確認しながら処理するか、それが出来ない場合は一度
ソケットを closeするとか、… 提供するサービスの仕様によるので
どれが良いか一概に言えませんが、あると思います。


そうですね。
この辺りの微妙なケースは、言語にかかわらず、
どうするべきか事前に検討するのが重要ですね。

このトピックのおかげで、
通信制御プログラムをJavaで実現する場合のクラス設計を
イメージすることができました。

ありがとうございました。
fuzuki
常連さん
会議室デビュー日: 2003/08/23
投稿数: 48
投稿日時: 2003-10-04 22:51
fuzukiです。

結局、アドバイスをいただいたとおりsetSoTimeoutを使ってみたところ思うような機能を実現することが出来ました。散々悩んだのが嘘のように簡単にできました。
スレッドも考えたのですが、まだ基本的なことしか理解しておらず、考えていると頭がこんがらかってくるので今回はこれでよしとしました。
お二人が話している内容は分かったような分からないような・・・

Kirikoさん、Kissingerさん、ありがとうございました。

スキルアップ/キャリアアップ(JOB@IT)