• 締切済み

SelectorとSocketChannelの使い方について分からない

SelectorとSocketChannelの使い方について分からない事があり困っております。 JavaのSelectorを利用した多重I/Oの色々なHPのサンプルプログラムを拝見したのですが全て「特定のサーバーに接続してそこにあるSelectorにSochetChannelをregisterして登録し、そこでそのまま送受信を行う」タイプのものでした。 私が今考えているのは「特定のサーバーに最初は接続するが、そこから複数のチャンネルがあり、その各チャンネルにはSelectorがあり、最初に接続したサーバーでSocketChannelを抽出するところまで行ってそれを各チャンネルのSelectorに渡してそこでregisterをする」というタイプです。こうするとデータの受信は最初に接続したサーバーが反応するのではなく、登録したSelectorのチャンネルが反応します。具体例を挙げると無作為で誰かとチャットが出来るようなサービスで、ただし回線切断からの再接続の場合は自動的に切断前に接続していたチャンネルへ再接続をしてくれる、というタイプのプログラムです。 そこで困っているのが「切断された後の再接続処理」についてです。 例えばチャンネル5番に接続されたとして何らかの原因で切断されるとします。その後再接続を行うと自動的にチャンネル5にログインするようにしたいのです。 しかし、接続して「再接続かどうか、また再接続ならどこのチャンネルに飛ばせばいいか」という情報をサーバーが受信するためにはまずSelectorにSocketChannelを登録しなければなりません。なので動作としては「サーバーのSelectorにSocketChannelを登録→データ受信→再接続先へ再接続、という処理になるかと思うのですが、こうすると「サーバーのSelectorからいったんSocketChannelを除いて特定のチャンネルのSelectorに登録しなおす」事になり、サーバーのSelectorから抜けた瞬間にサーバーからの接続が切断されてしまいます。 そこで「データを受信する前、つまり接続検知がありSelectorに登録するまでに再接続かどうかを判断しようと試みたのですが、接続検知で入手できるものはSelectionKeyとSocketChannelぐらいで、この二つをtoString()で文字列出力するとIP情報などが出てきたのですが再接続であるかどうかを判断するデータはありませんでした。 上記のように「サーバーに接続→初回接続ならサーバーから適当なチャンネルに飛ぶ→そのチャンネルとデータの送受信→接続が切断される→サーバーに再接続→サーバーが切断されたチャンネルに自動的に飛ばしてくれる」というプログラムは不可能なのでしょうか? 以上宜しくお願い致します。

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

みんなの回答

  • askaaska
  • ベストアンサー率35% (1455/4149)
回答No.2

こういうのはどうかしら。 仮ログイン先:各クライアントは最初ここに接続する。 その接続を維持したまま各チャンネルに接続する。 ムリかな?

to_dairi3
質問者

補足

お返事ありがとうございます。 その方法を現在模索しているのですが上手い方法が見つかっていない状態です。 ただし普通に考えますと「接続を維持したまま他のチャンネルにも接続する」と言う事は二重に接続していることになり、例えば「おはよう」とかデータを送信した場合どちらが受信するのか、または両方が受信するのかという不具合が発生します。また「端末から接続を2回試みて接続が二重になる」のならともかく、「1回の接続でセッションを二重に確立する」というのは恐らく理論的に無理な気がします。

  • askaaska
  • ベストアンサー率35% (1455/4149)
回答No.1

ちょっとやったことのない分野なので アイデア的な回答でゴメンね。 自分がこれから行う接続が再接続かどうかって クライアント側は知っているんじゃないかしら? つまり前回接続したかどうか?とか チャットならユーザが「再接続を要求する」とか もしくは前回切断時から120秒以内の接続とか。 判断できる材料はあるじゃないかなと思うの。 どうかしら。

to_dairi3
質問者

補足

お返事ありがとうございます。 私も現時点で、クライアント側は「さっき切断されたから次は再接続」というのは分かるのですが、それを受信するサーバーからすると「また新しいユーザーが接続してきた」という理解でしかありません。 サーバー側が「こいつはさっきチャンネル5に居て切断されたユーザーだ、チャンネル5に接続しなおし」という処理をしたいと考えております。しかし「チャンネル5に居たユーザーだ」という情報を得るために既にどこかのチャンネルに仮ログインをしなければいけません。そうなると「接続先の切り替え」をする必要が出てきてそれをしようとすると仮ログイン先から一時切断して新しいところへ接続するので切断した瞬間に端末とのセッションが切れてしまう、という状況です。

関連するQ&A

  • TCP通信の切断の検出について

    現在javaでTCP通信でサーバーに接続するタイプの大富豪を製作しております。 そこでTCP接続の切断の検出についてお聞きしたいです。 例えば既にあるオンラインのテーブルゲームなどをプレイしていると、誰かの通信が不安定になるor切断された場合、20~30秒程画面の間、動きが止まり、切断されたプレイヤー以外とのチャット等は可能で20~30秒後に誰かが「落ち」てプレイが続行されます。 この時、バックグラウンドではどのような処理が行われているのでしょうか? (1)TCP接続でデータの送信(受信)を行っているがとあるユーザーは切断されているのでそこが何度も送信(受信)のリトライ→反応なし(ACKを受信できず)→タイムアウトが20~30秒に指定してあり、時間経過後にタイムアウトが呼び出され切断されて続行される。 (2)通信が不安定なユーザーが居て切断/接続を何度も繰り返している。プログラム的に20~30秒不安定な状況が続けば切断するようなプログラムを組んでいる。 (3)その他 また、接続の切断の検出はサーバープログラムのどこで行われるのでしょうか。 (1)各接続クラスのInputStream.readもしくはOutputStream.write (2)Socket接続が切断されればどこかで例外が発生する? (3)その他 詳しい方居られましたら教えて頂けると幸いです。

  • ホームページのデータをFFFTPやDreamWeaverでアップロード中に接続が切断されてしまいます

    DreamWeaverでホームページ作成をしております。 サーバーへデータをアップデートする際、頻繁に接続が切断されてしまいます。 ・DreamWeaverから直接アップデート ・フリーソフトFFFTPからアップデート を両方試しましたが、どちらも同じ状況で、 「サーバーからの応答が無くなりました」という旨のメッセージが出てアップデート未完了のまま接続自体が切断してしまいます。 特定のデータをアップデートしている時にいつも切断が起きるのですが、 そのデータ数が1つ2つのレベルではないので、 (具体的にはhtmlデータ30個中、特定の5個、  イメージデータ100個中、特定の20個程度で切断が起こります) そんなにたくさんのデータが壊れているとも疑いにくい状況です。 念のためサーバにも問い合わせてみましたが、 サーバでは異常切断されている履歴も残っていない、とのことでした。 このような場合、原因はどこにあると考えられるでしょうか? ご意見がいただけたら嬉しいです。

  • 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関数で 切断を検知できるようにできないでしょうか。 よろしくお願いします。 以上

  • ダイアルアップ接続時のSMTP(メール送信)コマンドハンドリングで困っています。

    ダイアルアップ接続時のSMTP(メール送信)コマンドハンドリングで困っています。 プログラム中でダイアルアップによりインターネットに接続しSMPTコマンドを発行してメールを送信しようとしています。 DATA送信後のサーバー応答コード250を受信しQUITコマンドを送信、その後にサーバーの応答として221が戻ってきた時点でソケットを切断し電話線を切断しています。にもかかわらずなぜかまだメール送信中で送信中に切断されたとのことでメールが送れません。 何が問題でどう対処すればよろしいのでしょうか? アドバイスをよろしくお願いいたします。

  • TCP通信のブロッキングについて

    初めて投稿させて頂きます。 javaでTCP接続での4人で遊ぶ大富豪を作っております。 サーバーを用意し、各ユーザーがサーバーに接続して遊ぶタイプです。 サーバーの構造は次のようになっております。 ・クライアントから接続を受け付ける待機クラス(Server.java)を常時走らせておく ・待機クラスに接続があれば、1ユーザーごとに送受信のスレッド(client.java)を立ち上げる。各ユーザーからのデータはこのクラスが受け取る ・人数が4人集まれば、テーブルスレッド(table.java)を立ち上げる。各ユーザーと接続されているclient.java(*4)がデータを受け取るとここが計算を行う 各ユーザーがサーバーに接続し、データのやり取りをするところまでは成功したのですが、以下の事について色々調べたのですが分からず、教えて頂けると幸いです。 (質問)ユーザーの接続が切断されたときのTCPの動作について 例えばABCDの4人のプレイヤーが居て、A→B→C→Dの順番だとします。今Aが◇3を切ったとします。するとAから「◇3を切った」というデータをサーバー(サーバーが発行したAとの送受信クラス)に送信し、サーバーが受信、それをABCD全員に送信して手番がBに移ります。この時、仮にCが切断されていてABCDにデータを送信したけどCだけデータが届かない、というケースの場合について、全員のデータの整合性を取るために「サーバーが、"Aが◇3を切った送信"に対するACKパケットを全ユーザーから受信したのを確認してから手番をBに移す」という手順を踏みたいのです。 各クライアントとの送受信のスレッドは余計なところは省略すると以下のようになっております。 class client implements Runnable{ (変数宣言) (コンストラクタ) public void run(){ while(true){ try{ ※(1) in.read(b); // inはSocket.getInputStreamの戻り値 (データを受け取ったことで色々な処理) out.write(b); // outはSocket.getOutputStreamの戻り値 out.flush(); ※(2) }catch(Exception e){} } } } Aと接続されているclientクラスのin.readで「◇3を切った」を受信し、色々な処理を行った後、ABCDと接続している各clientクラスのout.writeで「◇3を切った」を送信します。 ここで質問なのですが、接続が途切れているCの場合、out.write(b)の部分ACKパケットを受信するまで何度もリトライをすると思うのですが、プログラムの動作としてはリトライをしながらもout.write(b)以下の処理に進んでしまうのでしょうか?それともACKを受信するまでここでプログラムはブロックされるorタイムアウト等が発生するまでリトライorサーバーが即切断を認識してExceptionに移行する のでしょうか?私の希望としては例えば上記プログラムの※(1)にf=false;(fはboolean)、※(2)にf=trueなどを置いて「全スレッドのf==trueになれば、次の作業をしてもOK」という風に出来ればプログラム的に楽なのですが、接続が途切れているプレイヤーCのスレッドはout.writeの結果に関わらず(リトライされようが)下の命令を実行してしまうのでしょうか?「ACKパケットを受け取ったか否か」を判断したいのですが、プログラムの動作としては パケットを受け取った/受け取ってない で、どのように差が出るのでしょうか。また、out.writeの内容に関わらずプログラム処理が進んでしまう場合、どのようなプログラムが最適でしょうか。 このサンプルの場合だと、Cとの接続クラスはin.readのところで確実にブロックされますが、もしout.writeでブロックされなければACDのプログラムはBを放っておいてどんどん先に進んでしまい、データの整合性が怪しくなってきます。 詳しい方居られましたらお教え頂ければ幸いです。

  • データベースに接続したままの負荷について

    データベースに一度接続したら、最後に切断するのが普通(?)だと思います。しかし接続する際にサーバにかかる負担が大きいとどこかのサイトで見ました。 もし、1日のアクセスが30万件あったとして、毎回接続と切断をするとかなり負荷がかかると思うのですが、その際に一番最初にデータベースを接続して、切断しないままでいることは可能なのでしょうか?また、もし接続したままでいるといくらか時間が経った後に自動的に切断されてしまうということはあるのでしょうか? よろしくお願いします。

    • ベストアンサー
    • MySQL
  • RSSのオートディスカバリーが…

    ブログのHTMLを変更して、読者さんがRSSリーダー登録してくれるときに指定したRSSをリーダーが自動検知するようにしてみたのですが、ためしにリンクバーを使ってワンクリックでリーダーに登録するタイプのものでやってみたら、指定したRSSでなく、ブログサービスのもともとのRSSが表示されていました。 これは自動検知の設定に失敗しているのか、そのタイプの登録の仕方ではオートディスカバリー設定はきかないのかどちらなのでしょうか。 どのタイプの自動登録でも指定したRSSが検知されるようにすることはできませんか? どなたか教えて下さい、よろしくお願いします。

  • ダイアルアップがすぐに切断されてしまいます

     こんばんは。kurehaと申します。  会社の課内サーバーとしてLinuxでサーバーを構築していますが、ISDN回線のためにすぐにダイアルアップが切断され、翌日に手動で再接続をしています。 質問として、以下3点ご教授願います。 (1)接続が切れたことをイベントとして検知し、自動で再接続をさせること はできますか? (2)cronを使って毎朝同じ時刻に自動で接続にいくようにしたいが、コマンドラインからダイアルアップするにはどうすればいいでしょうか。 (3)ダイアルアップにkpppを使用していますが、kpppはコマンドラインから接続を指示できますか? 以上、よろしくお願いいたします。

  • HYBRID W-ZERO3を買ったのですが、メールの受信設定で悩んでいます

    WSO27SHに機種変更したので、メールの受信を一定時間毎に確認するように設定したのですが、WSO27SHは接続の切断が自動的にされないので、私が手動で切断するまで受信メールサーバーに接続しっぱなしになってしまいます 回線は3Gでは無くPHS(club air edge)を利用してますが、この接続時間も利用料がかかってしまうのでしょうか? 既に膨大な接続時間になっているので不安でたまりません!

  • サーバーにおいて自動でプログラムを起動する仕組みについて

    サーバーを使って自動でプログラムを実行するやりかたが知りたいです サーバーから自動で一日一回特定のページにアクセスしてデータを 持ってきてSQLサーバーに取り込む仕組みを作りたいと思います これにはどういったやり方がありますか? JAVAでできるのでしょうか? やりかたがわかる本やページ またそれに関係しそうなキーワードでもいいのでおしえてください