質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.47%

  • Java

    14104questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

java.nioで他のチャネルを正しく発火させる方法は?

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,281

tchofu

score 79

キー入力とソケット通信を非同期に取り扱いたく、java.nioを使ってお試しプログラムを作ってみました。キー入力する側をstdinPipe、ソケット通信する側をSocketChannelとしています。

stdinPipeでキー入力を受け取った時、SocketChannelにそのバッファを書き込み、送信タイミングを作るためにSelectionKey.interestOps(SelectionKey.OP_WRITE);するようにコーディングしています。

実行した結果、SocketChannel.write()は成功(送信バイト数が返る)しているように見えるのですが、ネットワーク上にデータが流れず、又、SelectionKey.OP_WRITEが無限ループしてしまいます。

あるチャネル(stdinPipe)から、他のチャネル(SocketChannel)を発火させる正しい方法は、どのようなコードになるのでしょうか?何か良い例があればご教授いただけないでしょうか。

以下、期待通り動かないプログラムを載せておきます。

Main:
        public static void main(String[] args) throws Exception {
        Selector selector = Selector.open();
        
        final Socket socket = new Socket();
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(true);
        socketChannel.connect(new InetSocketAddress("localhost", 50000));
        System.out.println("connected.");
        socketChannel.configureBlocking(false);
        IOHandler ioHandler = new IOHandler();
        SelectionKey sckey = socketChannel.register(
            selector, 
            SelectionKey.OP_READ, 
            ioHandler
        );

        SystemInPipe stdinPipe = new SystemInPipe();
        SelectableChannel stdinChannel = stdinPipe.getStdinChannel();
        stdinChannel.register(
            selector,
            SelectionKey.OP_READ,
            new StdinHandler(sckey)
        );
        stdinPipe.start();
        
        System.out.println("selecting...");
        while(selector.select() > 0) {
            Set keys = selector.selectedKeys();

            for (Iterator it = keys.iterator(); it.hasNext(); ) {
                SelectionKey key = (SelectionKey)it.next();
                it.remove();
                Handler handler = (Handler)key.attachment();
                handler.handle(key);
            }
        }

    }

stdinPipe:
package example;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;

public class StdinHandler implements Handler {
    private SelectionKey selectionKey = null;

    public StdinHandler(SelectionKey selectionKey) {
        this.selectionKey = selectionKey;
    }

    @Override
    public void handle(SelectionKey key) throws ClosedChannelException, IOException {
        ByteBuffer buffer = ByteBuffer.allocate(32);
        ReadableByteChannel channel = (ReadableByteChannel) key.channel();
        if (channel.read(buffer) <= 1) {
            System.out.println("program completed.");
            channel.close();
            System.exit(-1); // @TODO 段取りを踏んだ終わり方に変更する
        }
        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        System.out.println();        
        
        buffer.flip();
        if (selectionKey != null) {
            IOHandler handler = (IOHandler)selectionKey.attachment();
            handler.setBuffer(buffer);
            selectionKey.interestOps(SelectionKey.OP_WRITE);
        }
        
        buffer.clear();
    }

}

socketChannel:
package example;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.DatatypeConverter;

/*
 * @see 出典: http://itpro.nikkeibp.co.jp/article/COLUMN/20060515/237871/
 */
public class IOHandler implements Handler {
    private final static int BUFFER_SIZE = 4096;

    private List<ByteBuffer> buffers;

    public IOHandler() {
        // 読み込んだデータを格納するためのリストの初期化
        buffers = new ArrayList<ByteBuffer>();
    }
    
    public void setBuffer(ByteBuffer buffer) {
        buffers.add(buffer);
    }
    
    public void handle(SelectionKey key)
                    throws ClosedChannelException, IOException {
        SocketChannel channel = (SocketChannel)key.channel();

        // 読みこみ可であれば、読みこみを行う
        if (key.isReadable()) {
            read(key);
        }

        // 書きこみ可であれば、書きこみを行う
        if (key.isWritable() && key.isValid()) {
            write(key);
        }
    }

    private void read(SelectionKey key)
                    throws ClosedChannelException, IOException {
        SocketChannel channel = (SocketChannel)key.channel();

        // 読みこみ用のバッファの生成
        ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
 
        // 読みこみ
        channel.read(buffer);

        // フリップしてからリストに追加
        buffer.flip();
        buffers.add(buffer);
        
        if (key.interestOps()
            != (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) {
            // 読みこみ操作に対する監視を行う
            key.interestOps(SelectionKey.OP_READ 
                            | SelectionKey.OP_WRITE);
        }
    }

    private void write(SelectionKey key)
                    throws ClosedChannelException, IOException {
        SocketChannel channel = (SocketChannel)key.channel();

        if (!buffers.isEmpty()) {
            // リストが空でなければ、先頭のバッファを取り出し
            // 書きこみを行う
            ByteBuffer buffer = buffers.get(0);
            int size = channel.write(buffer);
            System.out.println("write size= " + size);
            String hexString = DatatypeConverter.printHexBinary(buffer.array());
            System.out.println(hexString);
            
            key.interestOps(SelectionKey.OP_READ);
            
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            // 書きこみが終わったバッファは削除する
            buffers.remove(0);
        } else {
            // 書きこむデータがなければ、書きこみ操作の監視をやめる
            key.interestOps(SelectionKey.OP_READ);
        }
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

check解決した方法

0

自己解決しました。ただ期待通りに動かなかったのは私の設計ミスが原因でした。
申し訳ありません。
java.nioの使い方はまずくなかったようです。

私の設計では、サーバクライアントともport=50000で待つことになります。
実行環境はサーバ、クライアントともlocalhostだったためです。
(クライアント側も上記ポートでreadしていました)

相手(サーバ)に送ったはずのメッセージが、自分自身(クライアント)で受信されていました。

まだ詳細は掴み切れていないため、
詳しい状況が判明した上でご報告すべきだと思うのですが、
見てくださっている方々に無駄なお手数をかけていただくまいと思い、
中途半端な状況ではありますが、
自身で解決すべきと思い、クローズさせていただきます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/08/05 08:07 編集

    了解です。
    クライアントでも50000ポートでlistenしている箇所があったと言うことでしょうか?

    できれば、解決した後でいいので、
    結果を教えていただけると嬉しいです。

    一応、現時点で気になっていることは、

    1.そう言えばWireSharkはローカルの通信は
    キャプチャー出来なかったような。未確認です。

    2.クライアントでもサーバから受信したデータを
    そのままサーバに送信するようなコードがあるが、
    これだとデータをエコーし合ってしまうのではないか?

    です。関係無さそうなら無視してください。

    キャンセル

0

StdinHandlerのーhandleメソッドの最後でbuffer.clear();していますが、
これがあると余計な0がwriteされてしまいそうですが、
意図通りでしょうか?

何故動作しないのかについては、
まだ分からないので、追加で情報をいただきたいです。
私が自分で試せればよいのですが、ちょっと今は使える環境がないもので、
できればお願いします。

ネットワークにパケットが流れないというのは、
何を持って判断していますか?
また、無限ループとは、どこからどこまでの処理がどの様に無限ループするのでしょうか?
通信先のプログラムはどの様なものですか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/08/04 12:51

    実行した結果のコンソール出力内容も、できれば欲しいです。

    キャンセル

  • 2015/08/05 00:25 編集

    追加情報をご報告いたします。
    サーバは、末尾に引用したサーバ(ネットで見つけたシンプルなEchoServer)を使っています。パケットはWireSharkでモニタしています。

    ```python:
    #!/usr/bin/env python

    """
    A simple echo server

    http://ilab.cs.byu.edu/python/socket/echoserver.html

    """

    import socket

    host = ''
    port = 50000
    backlog = 5
    size = 1024
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((host,port))
    s.listen(backlog)
    while 1:
    client, address = s.accept()
    data = client.recv(size)
    if data:
    client.send(data)

    ```

    キャンセル

  • 2015/08/05 07:39

    コメントありがとうございます。

    WireSharkで、接続で3way handshakeが終わった後、何もパケットが流れないということで認識あっていますでしょうか?
    また、貼っていただいたEchoServerは、recv、sendした後、
    またacceptしてclientを上書きしていますが、これは意図通りでしょうか?
    whileの前でacceptした方が良いのかなと思います。

    それと、他の質問については、
    追加情報いただけないでしょうか?

    キャンセル

  • 2015/08/05 07:42

    お詫びさせてください。
    Java.nioの使い方ではなく、私のプログラムの設計が間違えていました。
    親身になっていただき、ありがとうございました。
    詳細は自己解決欄にて

    キャンセル

関連した質問

  • 解決済

    【Java】System.in.read()とSystem.in.skip()について

    失礼致します。 初歩的な質問をさせて頂きます。 Javaで、以下のようなプログラムを書きました。 package java; import java.io.IOExcepti

  • 解決済

    単語ごとの出現回数を出現順に表示するには、HashMapとArrayListが必要か

    単語ごとの出現回数を出現順に表示するには、HashMapとArryaListが必要でしょうか。 下記の例よりももっとスマートかつコード量が少なく使用するメモリの量が少ない方法がある

  • 受付中

    JUnitテストについて質問です

    今、以下のクラスについてのJUnitテストを書こうとしているのですが、どういう風にテストメソッドを設定すればよいか分かりません。 import java.io.BufferedR

  • 解決済

    java でしりとり

    java でひとりで遊べるしりとりを作りたいと思っているのですが コンピューターが自動的に返事してくれるようなものを作りたいと考えているのですが なにか参考になるようなサイトがあれ

  • 解決済

    MAILTO設定をレジストリで確認することは可能であるか

    既定プログラムのMAILTOで例えばWindowsLiveメールを設定した場合、 レジストリでその設定を確認することは可能でしょうか? また、レジストリの値はJAVAで取得する

  • 受付中

    ファイルのコピー

    Java開発初心者です。 ファイルのコピーを行うコードを書いています。 FileInputStreamとFileOutputStreamを使って、 コピーしたいファイ

  • 解決済

    java 監視サービス 複数フォルダ監視時の疑問

    いつも参考にさせていただいています。 JavaのWatchServiceを用いてフォルダの監視を行おうとしています。 色々なサンプルを参考にしていますが、下記の内容が少し理

  • 解決済

    会員情報システム(Java)での作り方

    javaで会員情報システムのようなものを作りたいです。 下記の実行結果(コマンドプロンプトで実行)になるような、登録プログラムを作成したいのですが、作成方法が分かりませんので、教

同じタグがついた質問を見る

  • Java

    14104questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。