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

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

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

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

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

Q&A

解決済

2回答

2736閲覧

JavaのNew IO(nio)のisWritableがtrueになる条件がわかりません。

inca1987

総合スコア20

Java

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

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

1グッド

0クリップ

投稿2016/09/16 09:29

概要

java.nioを用いてAndroidで動作するTCPクライアントを作成しています。
nioのセレクタ、チャンネルの仕組みについて理解できない部分があったため質問致しました。

質問

下記ソースにおける、key.isWritableはどのような場合にtrueになるのでしょうか。

ドキュメントも見たのですが、

対応するチャネルが「書き込み可能な状態」になっているか、次の書き込みができるようにリモートシャットダウンされているか、エラーによって一時停止していることを検出する

となっており、恥ずかしながらどういった状態が「書き込み可能な状態」であるのか理解できませんでした。

よろしくお願いいたします。

ソース

Java

1import java.io.IOException; 2import java.net.ConnectException; 3import java.net.InetSocketAddress; 4import java.nio.ByteBuffer; 5import java.nio.channels.SelectionKey; 6import java.nio.channels.Selector; 7import java.nio.channels.SocketChannel; 8import java.nio.charset.Charset; 9import java.util.Arrays; 10import java.util.Iterator; 11import java.util.concurrent.Executors; 12 13public class TCPClientForUnity{ 14 15 private SocketChannel mSocketChannel; 16 private Selector mSelector; 17 private ByteBuffer mReadBuffer; 18 private ByteBuffer mWriteBuffer; 19 private String mAddress; 20 private int mPort; 21 22 public TCPClientForUnity(final String adr, final int PORT){ 23 mAddress = adr; 24 mPort = PORT; 25 } 26 27 public void CreateBuffer(final int size){ 28 mReadBuffer = ByteBuffer.allocate(size); 29 mWriteBuffer = ByteBuffer.allocate(size); 30 } 31 32 public void CreateSocketChannel(final String adr, final int PORT){ 33 try { 34 mSelector = Selector.open(); 35 mSocketChannel = SocketChannel.open(); 36 mSocketChannel.configureBlocking(false); 37 mSocketChannel.connect(new InetSocketAddress(adr, PORT)); 38 mSocketChannel.register(mSelector, SelectionKey.OP_CONNECT); 39 }catch(IOException e){ 40 e.printStackTrace(); 41 } 42 } 43 44 private final Runnable mAcceptTask = new Runnable() { 45 public void run() { 46 System.out.println("start runnnable"); 47 CreateBuffer(1024); 48 49 try { //IOException 50 51 while (!Thread.interrupted()) { 52 try { //ConnectException 53 54 CreateSocketChannel(mAddress, mPort); 55 while (!Thread.interrupted() & mSocketChannel.isOpen()) { 56 mSelector.select(); 57 Iterator<SelectionKey> keys = mSelector.selectedKeys().iterator(); 58 59 while (keys.hasNext()) { 60 SelectionKey key = keys.next(); 61 keys.remove(); 62 63 if (key.isConnectable()) { 64 System.out.println("connecting..."); 65 mSocketChannel.finishConnect(); // if connection fail, throw ConnectException 66 System.out.println("CLIENT: Connected"); 67 mSocketChannel.register(mSelector, SelectionKey.OP_WRITE, mWriteBuffer); 68 mSocketChannel.register(mSelector, SelectionKey.OP_READ, mReadBuffer); 69 } 70 71 if (key.isWritable()) { 72 ByteBuffer writeBuf = (ByteBuffer) key.attachment(); 73 mSocketChannel.write(writeBuf); 74 if (!mWriteBuffer.hasRemaining()) { 75 System.out.println("CLIENT: Write " + Arrays.toString(mWriteBuffer.array())); 76 } 77 } 78 79 if (key.isReadable()) { 80 ByteBuffer readBuf = (ByteBuffer) key.attachment(); 81 mSocketChannel.read(readBuf); 82 if (!readBuf.hasRemaining()) { 83 String msg = Arrays.toString(readBuf.array()); 84 System.out.println("CLIENT: Read " + msg); 85 SendToRaspi(msg); 86 } 87 //mSocketChannel.close(); 88 //break loop; 89 } 90 } 91 } 92 } catch (ConnectException e) { 93 e.getStackTrace(); 94 try{ 95 Thread.sleep(100); 96 }catch (InterruptedException e2){ 97 e2.getStackTrace(); 98 } 99 } 100 } 101 }catch (IOException e) { 102 103 } 104 } 105 }; 106 107 public void SendToUnity(String msg){ 108 System.out.println("in TCPClientForUnity, msg: " + msg); 109 mWriteBuffer.put(msg.getBytes(Charset.forName("UTF-8"))); 110 } 111 112 public void SendToRaspi(String msg){ 113 System.out.println("plz over write"); 114 } 115 116 public void start() { 117 Executors.newSingleThreadExecutor().execute(mAcceptTask); 118 } 119 120}
GeeChiki👍を押しています

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

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

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

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

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

guest

回答2

0

自己解決

メインの質問については自己解決致しました。
registerの機能を理解できていなかった。というのが原因の全てです。

isWritableをtrueにするには、
毎回register(mSelector, SelectionKey.OP_WRITE)呼び出す必要があるようで、
自身のコードで言うと、SendToUnityを下記のように変更する必要がありました。

Java

1public void SendToUnity(String msg){ 2 mWriteBuffer.clear(); 3 mWriteBuffer.put(msg.getBytes(Charset.forName("UTF-8"))); 4 5 try{ 6 mSocketChannel.register(mSelector, SelectionKey.OP_WRITE); 7 }catch (IOException e){ 8 e.getStackTrace(); 9 } 10 }

register時にbufferを登録していたので、bufferを監視してisWritableをtrueにするんじゃないか?
と思い込んでしまったのですが、アレはただ用いるバッファを添付しているに過ぎないようです。
お目汚し失礼いたしました。

投稿2016/09/19 04:51

inca1987

総合スコア20

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

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

KSwordOfHaste

2016/09/19 08:50

お役には立てませんでしたが解決されたようでなによりです。
guest

0

質問に対する答えではないのですが・・・

コードを見る限り単一の接続に対するsend/receiveをしたいというのが目的に見えます。もしそうならnioを使うのはオーバースペックでは?サーバーを実装する際など複数の平行して発生するI/O(通信やファイルI/Oなど)の多重度などを制御したい場合はnioが必要になりますが、サーバーへの単一接続に対するI/O処理ならjava.net.Socketを利用する方がシンプルですのでそれを使うのが一般的と思います。(ただしSocketを使って接続したりsend/receiveする際に呼び出し元スレッドがブロックするのでUIスレッドとは別スレッドで通信することにはなります)

質問者さんが既にSocketでは問題ありと判断されてnioを調べておられるのでしたらすみませんがこのコメントはスルーしてください。

投稿2016/09/16 11:14

KSwordOfHaste

総合スコア18392

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

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

inca1987

2016/09/16 12:16

回答ありがとうございます。 Socketやノンブロッキング処理に対する知識が浅いので、是非ご教授いただけると幸いです。 通信はlocalhost内で行い、「AndroidNative」↔「Android上で動作するUnity」間の双方向通信を、同時並行で遅延を可能な限り少なく実現したいと考えております。 例えば、 1.Unity上でオブジェクトをタッチした情報をAndroidNativeへ送信 (2.AndroidNativeを経由してセンサを積んだマイコン上に送信) (3.センサの状況を加味して決定したUnity上のキャラクターのモーションをAndroidNativeに送信) 4.AndroidNativeからUnityに転送 という処理の流れを、他の通信と同時並行でなるべく遅延なく実現できる方法が必要です。 送受信データについては、たかだかサイズ1kB未満の文字列ですが、同時並行での送受信時ブロックが生じた際の遅延がどの程度になるか見積もることができなかったので、ノンブロッキング処理を行えるnioを選択しました。 この状況においても、やはりnioはオーバースペックでしょうか。ぜひご意見を伺いたいです。 追伸: 実は、以前の質問(https://teratail.com/questions/46856)で書いたUnity-AndroidNative間通信の問題が解消できず、行き詰まってしまったがための代替手段ですので、TCP通信を用いるのは少し不自然な場面かもしれません。
KSwordOfHaste

2016/09/16 13:54

Unityの仕様とAndroidNativeでやろうとしている処理がどういったものかわからないので残念ながら自分には現状の情報だけではお答えできません。UnityはゲームエンジンでAndroidNativeは要するにJNIを使ってハードウェアの機械語で直接動作するnativeコードとやりとりできるという理解であっているでしょうか。現状の情報では以下のような疑問が次々にわいてくるのでお答えできないのです。 1. UnityはAndroidのUIスレッド上で動作するものですか?それとも別のスレッドで実行されているものですか? 2. Unity上のオブジェクトをタッチした再、UIスレッドでハンドラーが実行されるのですか?それともUnity独自のスレッドでハンドラーが呼ばれるのですか?それとも色々な方法がある? 3. nativeコードはスレッドセーフに作られていますか?それとも単一のスレッドからのみ実行することが必要ですか? 4. センサーの状況を加味してUnityのキャラクターのモーションを決める処理はJavaで行う?nativeコードで行う?Javaで行うならセンサーの検出値をnativeコードを呼び出してJava側へ戻すことになりますしnativeコード側でやるならUnityのキャラクターの情報をnativeコードへいつ伝えるのかといった方針も関係してくると思います。
inca1987

2016/09/16 15:08

詳細なご指摘ありがとうございます。 自分にはKSwordOfHasteさんの質問に正確に答えるだけの知識が無いものと思われますが、 出来る限り調査したうえで、答えさせていただきます。 まずは「Unity」と「AndroidNative」という表現ですが、 「Unity」は「Unityプロジェクト上のコードおよびビルドされたAndroid上で実行可能なバイナリ」の意で、 「AndroidNative」は「上記以外のAndroidコードおよびビルドされたバイナリ」の意で言っていました。 勝手な表現で誤解を招いたことをお詫びいたします。 また明日調査した上で、ご指摘1〜4への回答をさせていただきたいと思います。
inca1987

2016/09/19 04:55

Android及びJavaへの理解が浅い自分にとって、KSwordOfHasteさんの質問に正確に答える事は少し荷が重いようです。 大変申し訳ございません。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問