LAN内通信アプリを作ろう その４ JavaFXのコントロールに情報を設定する/スレッドからJavaFXの画面を呼び出す

仮眠（5時間）も取ったので作業を再開しましょう。

LAN内通信アプリは今日で終わりなのでササッと（出来ても出来なくても）


* メッセージ部分の受信
* 受信したメッセージの表示（受信ウィンドウの作成）

受信処理の改修からやっていきます。

# 受信処理の改修

* 受信メッセージの表示ウィンドウを作成する。
* メッセージを安定して受け取れるようにする。


## 受信メッセージの表示ウィンドウを作成する。

まずはこれ。
JavaFXを利用して受信したメッセージを表示する画面を作成。

送信ウィンドウを流用して作ったのがこちら。

![2018y06m15d_224036934.jpg](https://qiita-image-store.s3.amazonaws.com/0/259679/19c43a8b-62f0-b221-0e94-841957b7d204.jpeg)

## 受信時にウィンドウを呼び出す。

ここで詰まった。

今回のアプリケーションでは「メッセージを受信したときに受信メッセージを新規ウィンドウで表示する」というイメージで作成していて
「受信用の待ち受けスレッドを用意してポートを監視している」作りになっている。

最初は「受信用の待ち受けスレッドで画面を作成して表示すればいいか」と思っていたものの
javafx.stage.Stageのインスタンスを作ってshow()メソッドを呼ぶとIlegalStateExceptionを出してくる。

↓こんな感じ

```RxThread.java
    (受信処理)

	Stage stage = new Stage();
	stage.show();

```



↓結果がこちら

![2018y06m16d_001717192.jpg](https://qiita-image-store.s3.amazonaws.com/0/259679/d07b5e2b-6867-bb34-b506-8d7b581e327a.jpeg)

このExceptionはなんぞや？と思って調べてみると、こんな事実に行き当たった。
曰く
「JavaFXの指定したスレッド(JavaFX application thread)以外からJavaFXの要素を操作しようとすると上記例外が投げられる」

で、回避方法を調べてみたところ、javaFX.platformなるクラスが存在することが判明。

[Oracle - JavaFXドキュメンテーション クラスplatform
 (日本語)](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/application/Platform.html
)

このクラスのrunLater()メソッドにrunnnableなスレッドを登録してやることでJavaFX applicationが代替実行してくれるそうな。

あ、formみたいにnewしてshow()!とは行かないのね。
さっそく調べた内容を元にStageを表示する処理を
書き換えて動作確認。

↓こんな感じに変更

```RxThread.java
    (受信処理)

	Platform.runLater( () -> {
		Stage stage = new Stage();
		stage.show();
	});

```

↓結果がこちら（真っ白なウィンドウがnewしたStage。サイズ変更してるけど最初はかなり大きい）
![2018y06m16d_004712408.jpg](https://qiita-image-store.s3.amazonaws.com/0/259679/0d991ae9-4eee-fdbd-62a2-db59397f8ddc.jpeg)

…えー、この事実を受けて
画面描画の構成を変える必要が鎌首をもたげたわけですね。（ため息）
今の作りだとあまりにも汚いので･･･

直しました（１時間使って）。

ざっくりこんな感じの作りに修正

<dl>
<dt>Stage関連処理</dt>
<dd>画面毎のFXMLファイル</dd>
<dd>画面毎のStage取得用クラス</dd>
<dd>画面毎のControllerクラス</dd>
<dt>送受信処理</dt>
<dd>ソケット通信を利用してデータを送信するスレッド継承クラス</dd?
<dd>受信ポートを監視し、データを受信するスレッド継承クラス</dd>
<dt>通常処理</dt?
<dd>Platform.runLator()にStageの描画処理を行わせるための汎用スレッド継承クラス</dd?
<dd>エントリポイントを含むクラス</dd>

上記作り変えを行ったことで、
[メッセージの受信を確認] => [受信メッセージStageを取得] => [Applicationスレッドに描画タスクを依頼] => [Stageの描画]
という処理が出来るようになりました。

![2018y06m16d_035944134.jpg](https://qiita-image-store.s3.amazonaws.com/0/259679/29719ea2-9eb4-39e3-1a37-e8ca13372298.jpeg)

次はこのStageに受信したメッセージを表示させたいと思います。

## JavaFxを用いて表示したGUIのコントローラに値を設定する

ここもがっつり詰まりました。
しかもnull参照で。

時間が・・時間が・・・（焦燥）

設定の仕方はこんな手順で実現できます。

1. GUIに紐付けているコントローラークラスに、コントロールを操作するための処理を用意する
1. コントローラのインスタンスを取得して用意した操作メソッドを呼び出す

書いてしまえばなんと簡単に見えることか

### GUIに紐付けているコントローラークラスに、コントロールを操作するための処理を用意する

これは簡単。普通のセッターを作ってあげればOK。
コントロールをオブジェクトとして扱う方法は[LAN内通信アプリを作ろう その３](https://qiita.com/Shiratori/items/35b0c488f6adbb034cd4)の「IDを設定したコントロールと同一の型、名前を持つオブジェクトをクラス内で宣言する。」の項目を参照されたし。

### コントローラのインスタンスを取得して用意した操作メソッドを呼び出す

これ。これでガッツリはまりました。
ポイントは以下の通り。

* コントローラのインスタンスはFXMLLoaderクラスのgetController()メソッドを利用する。
(Object型で返却されるので、コントローラに指定してるクラスでキャストすること)
* 上記インスタンスはFXMLLoader.load()メソッドを実行した段階で生成される。
(これより早いタイミングで触ろうとするとnull参照で怒られる。ｶﾞｯ!)

で、この処理のためにfxmlLoaderの実態を持っているコントローラでgetControl()の値を返却するゲッターを作り
それをStage描画用スレッドで受け取ってそのまま返却するラッパーを作って･･･と細かいことを延々と･･･

その甲斐あって、取得したメッセージを表示するまでは成功

![2018y06m16d_043800899.jpg](https://qiita-image-store.s3.amazonaws.com/0/259679/465eb47c-4f98-ac5f-0f51-930255fd2803.jpeg)

## メッセージを安定して受け取れるようにする。

ここで大変悲しいお知らせです。
えー、昨日の自分はなんか意識が朦朧としてたので（言い訳）
無駄な処理を入れてメッセージの待ち受けを行ってました。

ので、無駄な処理を省いてこんな感じに修正

```RxThread.java
			while (length >= 0) {
				byte[] buffer = new byte[1024];
				length = dis.read(buffer);
				sBuffer.append(new String(buffer));
				
			}

```

1. バッファ領域とって
2. 取得したデータ量を更新して
3. 取得したデータは文字列バッファに格納していく

って処理ですね。
バッファサイズはlengthの値でもよかったかなー。

ソケット通信の場合、取得する情報が無くなると-1を返却してくるので
取る情報が無くなったら抜けてくれるわけですね。

今のところあんま問題ないのでこれ放置で。
本当は最初に送信するデータの量とかを相手方に伝えたりするんですが（伝送中の損失に備えるため）
まあ、今回はLAN内だしね。
暗号化もナシ。

# 端末間で通信してみる。

さて、とりあえず一通り実装した（ハズ）なので、LAN内の端末間で通信してみましょ。

ひとまず開発マシン(windows 7)と営業用のノートパソコン(windows 10)での結果がこんな感じ

（携帯のカメラで撮影したので非常に重い写真。開発した本体（ライブラリ込み）の70倍超）
![P_20180616_060205_vHDR_On.jpg](https://qiita-image-store.s3.amazonaws.com/0/259679/3dbd2707-0c9b-13c9-2afe-cb09c00e0740.jpeg)


で、悲しいかなLinuxで起動しようとしたら怒られました。
軽く調べてみたところ、なんかsun.*系のライブラリがどーたらこーたらなのでそこはもうちょっと調査が必要か。
![Screenshot from 2018-06-16 06-32-05.png](https://qiita-image-store.s3.amazonaws.com/0/259679/509941f5-4e0d-673c-38aa-883d2ac61990.png)


せっかくJavaで組んだんだから多種のOSに対応したいものね。
とりあえず目的は達成？したので今回はこれまで。
直接IP打ってたりで少し使いづらいのはアレだけど。そこは改善点として･･･別機能だしね（開き直り）
取得したアドレスに/とか入ってるのが気になるところ。

追記：JavaFXのライブラリが入ってなかったっぽい･･･あれ？1.8だともう標準じゃなかったっけ･･･?
openjfxを導入したら普通に起動しました。

![Screenshot from 2018-06-16 07-58-48.png](https://qiita-image-store.s3.amazonaws.com/0/259679/1cf616b1-a00a-497c-3f26-76872164b18b.png)

通信結果はこんな感じ。（VNCで表示してます。）

![2018y06m16d_080211838.jpg](https://qiita-image-store.s3.amazonaws.com/0/259679/592336a0-811f-e0a5-801a-4c8249249c22.jpeg)


---

最終日の稼動は07:41:54で終了。
全体で23:03:51。

来週はどうなるかなー？

あ、挙げ忘れてたけど、GitHubはこちら

GitHub - PrivateMessenger
https://github.com/Shiratori1218/PrivateMessenger