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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Java

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

Q&A

解決済

2回答

4292閲覧

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

tchofu

総合スコア87

Java

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

0グッド

0クリップ

投稿2015/08/03 23:41

編集2015/08/03 23:42

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

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

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

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

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

Main:

java:

1 public static void main(String[] args) throws Exception { 2 Selector selector = Selector.open(); 3 4 final Socket socket = new Socket(); 5 SocketChannel socketChannel = SocketChannel.open(); 6 socketChannel.configureBlocking(true); 7 socketChannel.connect(new InetSocketAddress("localhost", 50000)); 8 System.out.println("connected."); 9 socketChannel.configureBlocking(false); 10 IOHandler ioHandler = new IOHandler(); 11 SelectionKey sckey = socketChannel.register( 12 selector, 13 SelectionKey.OP_READ, 14 ioHandler 15 ); 16 17 SystemInPipe stdinPipe = new SystemInPipe(); 18 SelectableChannel stdinChannel = stdinPipe.getStdinChannel(); 19 stdinChannel.register( 20 selector, 21 SelectionKey.OP_READ, 22 new StdinHandler(sckey) 23 ); 24 stdinPipe.start(); 25 26 System.out.println("selecting..."); 27 while(selector.select() > 0) { 28 Set keys = selector.selectedKeys(); 29 30 for (Iterator it = keys.iterator(); it.hasNext(); ) { 31 SelectionKey key = (SelectionKey)it.next(); 32 it.remove(); 33 Handler handler = (Handler)key.attachment(); 34 handler.handle(key); 35 } 36 } 37 38 }

stdinPipe:

java:

1 2package example; 3 4import java.io.DataOutputStream; 5import java.io.IOException; 6import java.net.Socket; 7import java.nio.ByteBuffer; 8import java.nio.channels.ClosedChannelException; 9import java.nio.channels.ReadableByteChannel; 10import java.nio.channels.SelectionKey; 11 12public class StdinHandler implements Handler { 13 private SelectionKey selectionKey = null; 14 15 public StdinHandler(SelectionKey selectionKey) { 16 this.selectionKey = selectionKey; 17 } 18 19 @Override 20 public void handle(SelectionKey key) throws ClosedChannelException, IOException { 21 ByteBuffer buffer = ByteBuffer.allocate(32); 22 ReadableByteChannel channel = (ReadableByteChannel) key.channel(); 23 if (channel.read(buffer) <= 1) { 24 System.out.println("program completed."); 25 channel.close(); 26 System.exit(-1); // @TODO 段取りを踏んだ終わり方に変更する 27 } 28 buffer.flip(); 29 while (buffer.hasRemaining()) { 30 System.out.print((char) buffer.get()); 31 } 32 System.out.println(); 33 34 buffer.flip(); 35 if (selectionKey != null) { 36 IOHandler handler = (IOHandler)selectionKey.attachment(); 37 handler.setBuffer(buffer); 38 selectionKey.interestOps(SelectionKey.OP_WRITE); 39 } 40 41 buffer.clear(); 42 } 43 44}

socketChannel:

java

1package example; 2 3import java.io.IOException; 4import java.nio.ByteBuffer; 5import java.nio.channels.ClosedChannelException; 6import java.nio.channels.SelectionKey; 7import java.nio.channels.SocketChannel; 8import java.util.ArrayList; 9import java.util.List; 10 11import javax.xml.bind.DatatypeConverter; 12 13/* 14 * @see 出典: http://itpro.nikkeibp.co.jp/article/COLUMN/20060515/237871/ 15 */ 16public class IOHandler implements Handler { 17 private final static int BUFFER_SIZE = 4096; 18 19 private List<ByteBuffer> buffers; 20 21 public IOHandler() { 22 // 読み込んだデータを格納するためのリストの初期化 23 buffers = new ArrayList<ByteBuffer>(); 24 } 25 26 public void setBuffer(ByteBuffer buffer) { 27 buffers.add(buffer); 28 } 29 30 public void handle(SelectionKey key) 31 throws ClosedChannelException, IOException { 32 SocketChannel channel = (SocketChannel)key.channel(); 33 34 // 読みこみ可であれば、読みこみを行う 35 if (key.isReadable()) { 36 read(key); 37 } 38 39 // 書きこみ可であれば、書きこみを行う 40 if (key.isWritable() && key.isValid()) { 41 write(key); 42 } 43 } 44 45 private void read(SelectionKey key) 46 throws ClosedChannelException, IOException { 47 SocketChannel channel = (SocketChannel)key.channel(); 48 49 // 読みこみ用のバッファの生成 50 ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); 51 52 // 読みこみ 53 channel.read(buffer); 54 55 // フリップしてからリストに追加 56 buffer.flip(); 57 buffers.add(buffer); 58 59 if (key.interestOps() 60 != (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) { 61 // 読みこみ操作に対する監視を行う 62 key.interestOps(SelectionKey.OP_READ 63 | SelectionKey.OP_WRITE); 64 } 65 } 66 67 private void write(SelectionKey key) 68 throws ClosedChannelException, IOException { 69 SocketChannel channel = (SocketChannel)key.channel(); 70 71 if (!buffers.isEmpty()) { 72 // リストが空でなければ、先頭のバッファを取り出し 73 // 書きこみを行う 74 ByteBuffer buffer = buffers.get(0); 75 int size = channel.write(buffer); 76 System.out.println("write size= " + size); 77 String hexString = DatatypeConverter.printHexBinary(buffer.array()); 78 System.out.println(hexString); 79 80 key.interestOps(SelectionKey.OP_READ); 81 82 try { 83 Thread.sleep(1000); 84 } catch (InterruptedException e) { 85 // TODO Auto-generated catch block 86 e.printStackTrace(); 87 } 88 89 // 書きこみが終わったバッファは削除する 90 buffers.remove(0); 91 } else { 92 // 書きこむデータがなければ、書きこみ操作の監視をやめる 93 key.interestOps(SelectionKey.OP_READ); 94 } 95 } 96}

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

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

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

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

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

投稿2015/08/04 22:49

tchofu

総合スコア87

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

eripong

2015/08/04 23:08 編集

了解です。 クライアントでも50000ポートでlistenしている箇所があったと言うことでしょうか? できれば、解決した後でいいので、 結果を教えていただけると嬉しいです。 一応、現時点で気になっていることは、 1.そう言えばWireSharkはローカルの通信は キャプチャー出来なかったような。未確認です。 2.クライアントでもサーバから受信したデータを そのままサーバに送信するようなコードがあるが、 これだとデータをエコーし合ってしまうのではないか? です。関係無さそうなら無視してください。
guest

0

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

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

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

投稿2015/08/04 03:36

eripong

総合スコア1546

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

eripong

2015/08/04 03:51

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

2015/08/04 15: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) ```
eripong

2015/08/04 22:39

コメントありがとうございます。 WireSharkで、接続で3way handshakeが終わった後、何もパケットが流れないということで認識あっていますでしょうか? また、貼っていただいたEchoServerは、recv、sendした後、 またacceptしてclientを上書きしていますが、これは意図通りでしょうか? whileの前でacceptした方が良いのかなと思います。 それと、他の質問については、 追加情報いただけないでしょうか?
tchofu

2015/08/04 22:42

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問