ionic
PeerJS
Ionic3

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

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

PeerJS とは

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

PeerJS - GitHub

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

Ionicでピアを作成する

いつもの

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

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

$ npm install --save @types/peerjs

PeerJSの読み込み

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

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

Talkクラスを定義する

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

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

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

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

<!-- 自分の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から接続を受けた場合の処理の流し先を設定します。

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関数が実行されます。

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を使いました。

<!-- トーク履歴表示 履歴が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

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

準備

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

$ npm install --save peer

コード

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

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

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

実行する

ターミナルを2つ立ち上げて、1つ目で

$ node peer.server.js

2つ目のターミナルで

$ ionic serve

こんな感じで動きます。

スクリーンショット 2018-05-04 1.11.35.png

終わりに

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

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

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

実コードはここ GitHubです。

参考書籍

HTML5/WebSocket/WebRTCによる
TypeScriptネットワークプログラミング