【Ionic】PeerJSを使ってP2Pチャットアプリを作る

**実行環境はホームネットワーク内を想定しています。**

## PeerJS　とは

PeerJSは、WebRTCを普通に使うよりも簡単に処理に組み込めるように開発されたJavaScriptライブラリです。

[PeerJS - GitHub](https://github.com/peers/peerjs)

今回はNode.jsを使って簡単なシグナリングサーバーを自前で用意し、PeerJSを使ってそのサーバーにアクセスし、ブラウザ間のシグナリングをおこないます。

## Ionicでピアを作成する

### いつもの

```bash
$ ionic start p2p-text-chat blank
$ cd p2p-text-chat
```

### PeerJSの型定義をインストール

```bash
$ npm install --save @types/peerjs
```

### PeerJSの読み込み

「src/index.html」に下記を追加

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/peerjs/0.3.14/peer.js"></script>
```

### Talkクラスを定義する

今回は１回のメッセージをTalkインスタンスとして管理します。

```typescript
class Talk {
  constructor(
    public srcId: string,
    public destId: string,
    public message: string
  ) {}
}
```

### メッセージ送信フォームを作成

Angularでいう[Template-driven Forms](https://angular.io/guide/forms)に従って作成します。
「接続先のPeerID」と「送信メッセージ」のどちらかでも空になっている場合、「Send」ボタンは非活性となります。

```html

<!-- 自分のPeerID -->
<h3>Your PeerID is <span ion-text color="danger">{{ talk.srcId }}</span></h3>

<!-- 投稿フォーム -->
<form (ngSubmit)="onSend()" #talkForm="ngForm">

  <!-- 接続先PeerID入力 -->
  <ion-item>
      <ion-label>Connect to</ion-label>
      <ion-input name="destPeerId" type="number" required [(ngModel)]="talk.destId" #destPeerId="ngModel"></ion-input>
  </ion-item>

  <!-- 送信メッセージ入力 -->
  <ion-item>
      <ion-label>Message</ion-label>
      <ion-input name="message" type="text" required [(ngModel)]="talk.message" #message="ngModel"></ion-input>
  </ion-item>

  <!-- 送信ボタン Required設定されているInputがある場合は非活性 -->
  <ion-row>
    <ion-col center text-center>
      <button ion-button type="submit" [disabled]="talkForm.form.invalid">Send</button>
    </ion-col>
  </ion-row>
</form>

```

Talkインスタンスを初期化します。
また、ランダムなPeerIDを発行し、それを元にPeerインスタンスを作成します。
Peerインスタンスが無事に作成され、Listen状態となった場合、
生成したPeerIDをTalkインスタンスの設定します。
最後に他のPeerから接続を受けた場合の処理の流し先を設定します。

```typescript

talk: Talk = new Talk('', '', '');
talkHistory: Talk[] = [];
peer: PeerJs.Peer;

ionViewDidLoad() {
  this.start();
}

public start() {
  const peerId = String(Math.floor(Math.random() * 900) + 100);
  const options = {
    host: location.hostname,
    port: 9000
  };

  this.peer = new Peer(peerId, options);

  this.peer.on('open', id => this.talk.srcId = id);

  this.peer.on('connection', conn => this.receive(conn));
}

private receive(conn: PeerJs.DataConnection) {
  conn.on('open', () => {
    conn.on('data', data => this.showMessage(data));
  });
}

showMessage(talk: Talk) {
  this.talkHistory.push(talk);
}
```

### 入力したメッセージを送信する

Submitアクションを受けてonSend関数が実行されます。

```typescript
onSend() {
  const conn = this.peer.connect(this.talk.destId, { serialization: 'json' });

  conn.on('open', () => {
    conn.send(this.talk);
    this.showMessage(this.talk);
  });
}
```

### Talk履歴を表示する

今回はion-cardを使いました。

```html
<!-- トーク履歴表示 履歴が0の場合は表示しない -->
<ion-card *ngIf="talkHistory.length > 0">
  <ion-card-content>
    <ion-card-title>Talk</ion-card-title>
      <ion-list>
        <ion-item *ngFor="let talk of talkHistory;">
          <h3>[{{talk.srcId}} -> {{talk.destId}}]</h3>
          <p>{{ talk.message }}</p>
        </ion-item>
      </ion-list>
  </ion-card-content>
</ion-card>
```
これでピアの準備ができました。
実行イメージは以下になります。

![スクリーンショット 2018-05-04 1.02.05.png](https://qiita-image-store.s3.amazonaws.com/0/164245/e9408923-7c5f-43d9-b1b2-5f0883dde402.png)


## シグナリングサーバーを立てる

### 準備

Peerパッケージをインストール

```bash
$ npm install --save peer
```

### コード

「peer.server.js」というファイルを作成し、以下を書きました。

```javascript
var PeerServer = require('peer').PeerServer;
var server = new PeerServer({port: 9000});
```

以上。びっくりするぐらい簡単でした。

## 実行する

ターミナルを２つ立ち上げて、１つ目で

```bash
$ node peer.server.js
```

２つ目のターミナルで

```bash
$ ionic serve
```

こんな感じで動きます。

![スクリーンショット 2018-05-04 1.11.35.png](https://qiita-image-store.s3.amazonaws.com/0/164245/e904e447-0452-ebf5-f689-0dfe6658e6fb.png)


## 終わりに

基本中の基本ですがサクッと動きを確認することができました。
参考書籍にリンクをつけた本の内容の復習といった感じですね。

もっと流行らせるためには「Ionic使うとこんな感じでサクッとプロトタイプ作れますよー」って実例を増やしていった方がいいのかな・・・

結構やりこんだから流行って欲しいなー。

実コードは[ここ GitHub](https://github.com/yosshitaku067/Ionic-Laboratory/tree/master/src/pages/p2p-text-chat)です。

## 参考書籍

[HTML5/WebSocket/WebRTCによる 
TypeScriptネットワークプログラミング](http://www.cutt.co.jp/book/978-4-87783-354-1.html)