はじめに
これは Node.jsアドベントカレンダー2020の記事です。実用性皆無のネタ記事ですが、リラックスしてお楽しみください。
twitterで「iPadでLinuxが動く」というツイートを見たことがきっかけで、iPad/iPhone上でNode.jsを動かしてみました。内容は次の通りです。
- iSH Shell で Node.jsを動かす
- Node.jsでWebサーバーを動かし、Safariでアクセスする
- オマケ:WebRTCのシグナリングサーバーを動かして、iPadとiPhoneで通信する
iSH Shell
iSH Shellとは
利用するアプリは、「iSH Shell」(App Store)というアプリです。私は知りませんでいたが、以前からあるアプリのようです。
名前は「Shell」とありますが、実際にはアプリケーションレイヤーでlinuxをエミュレーションしています。利用されているディストリビューションは、Alpine Linuxです。
ちなみに使用するには、Bluetoothキーボードがあった方が便利です。
iSH Shellのインストール
普通にApp Storeからインストールします。
中身の確認
- デフォルトのユーザーは root
- /etc/issue をみると、Alpine Linux 3.12
- CPUアーキテクチャーは、i686(インテル系32ビット)
~# whoami
root
~# cat /etc/issue
Welcome to Alpine Linux 3.12
Kernel \r on an \m (\l)
~# uname -m
i686
パッケージマネージャーの追加
アプリをインストールした状態では、Alpineのパッケージマネージャー apk はインストールされていません。どうやらApp Storeの規約上、取り除く必要があったようです。
(※2020.12.15現在、App Store版でもapkがインストール済みになったようです)
こちらの手順に従い、apkをインストールします。
- GitHub ... https://github.com/ish-app/ish/
- Wiki ... https://github.com/ish-app/ish/wiki
- Tutorial:Installing apk ... https://github.com/ish-app/ish/wiki/Installing-apk
- ※手順はこのページに記載
~# wget -qO- http://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86/apk-tools-static-2.10.5-r1.apk | tar -xz sbin/apk.static && ./sbin/apk.static add apk-tools && rm sbin/apk.static && rmdir sbin 2> /dev/null
~# apk --version
apk-tools 2.10.5, compiled for x86.
Node.js を動かす
Node.jsのインストール
iSHとapkが用意できたら、念願のNode.jsをインストールします。
~# apk add nodejs
~# apk add npm
~# node -v
v12.18.4
~# npm -v
6.14.6
2020/12/5現在、node v12.18と、npm 6.14がインストールされました。(※npmは応答が返ってくるまで、時間がかかります。何かのタイムアウトが発生している?)
REPLの利用
さっそくREPL(対話モード)を動かしてみます。
~# node
Welcome to Node.js v12.18.4.
Type ".help" for more information.
> 1+2+3
6
> console.log('Hello Node.js')
Hello Node.js
undefined
> .exit
~#
Webサーバーを動かす
次はNode.jsを使って、簡単なWebサーバーを動かしてみます。
const http = require('http');
const server = http.createServer((req, res) => {
res.end('<html><h1>Hello World!</h1></html>');
});
server.listen(8000);
シェルから起動します。
~# node server.js
iPadの場合
iSHがバックグラウンドに回ってしまうと、内部のプロセスの実行が停止してしまいます。そのためiPadの場合はSplit View(Appleの説明)を使って、iSHとSafariを横に並べて使います。
- iSH上で、サーバーを起動
- Split Viewで、Safariを同時に表示
- Safariで、http://localhost:8000/にアクセス
- 「Hello World!」と表示されればOK
iPadなら、そこそこ実用的にNode.jsを動かすことができます。
iPhoneの場合
iPhoneでは、Split Viewが使えず、同時に2つのアプリを動かすことができません。そこで次の手順で無理やり実行します。
- iSH上で、サーバーを起動
- Safarを起動し、http://localhost:8000/にアクセス
- ロード待ちになる
- iSHに切り替えると、サーバーがリクエストを処理する
- 再びSafariに切り替えると、Safari上でページが表示される
リクエストが飛ぶたびに、iSHとSafariを切り替えてやる必要があります。iPhoneではかなり厳しいです。
iSH Shellの制約
iSH Shellではx86をエミュレーとしてLinuxが動いていますが、一部デバイスに近い部分では使えない機能があるようです。具体的には、ネットワーク周りの機能では動かないものがありました。
例えば、Node.jsで次のコードを実行しようとすると、エラーが発生します。
const os = require('os');
const interfaces = os.networkInterfaces();
Uncaught:
SystemError [ERR_SYSTEM_ERROR]: A system error occurred: uv_interface_addresses returned Unknown system error 22 (Unknown system error 22)
ネットワークスインターフェイスの情報が取れていないようです。
オマケ:WebRTCのシグナリングサーバーを動かす
ここからは、完全に個人の興味によるオマケです。
WebRTC とシグナリング
Node.js が動くなら、WebRTCのシグナリングを動かしたくなるのが性分です。
- WebRTC ... Web Realtime Communitaion
- Web上で、音声/映像/データをやり取りするための仕組み
- P2Pでのユースケースを想定して規格が作られたが、いまはサーバー経由の利用が増えている
- シグナリング
- これから通信を始める2者が、通信開始に必要な情報を交換するための手続き
- WebRTCではやり方は規定されていない
- サーバー経由で、WebSocket等の双方向通信の仕組みでやり取りすることが多い
ここでは説明は省略します。興味がある方は(ちょっと古いですが)こちらをご参照ください。
- WebRTCハンズオン 概要編 ... https://qiita.com/massie_g/items/916694413353a3293f73
シグナリングサーバーとWebサーバー
よくあるケース
通常はシグナリングのサーバーはローカルネットワーク内のPC上か、インターネット上で動かし、通信したい2つの端末で接続します。
iOSデバイスだけで動かしたい
今回はPC(Mac)やインターネット上のサーバーを利用せず、2台のiOSデバイスだけでシグナリングサーバーを動かし、WebRTC通信を行ってみます。
- iPad と iPad
- または、iPad と iPhone
(不自然ではありますが、そういう縛りのある状況でデモしなければならない、という設定をご想像ください汗)
また、ブラウザでメディア通信で使うカメラ/マイクにアクセスするには、セキュリティ上の制限により次の場所にあるWebページの必要があります。
そのため、2台のデバイスそれぞれでWebサーバーは動かします。
iSH でWebサーバー/シグナリングサーバーを動かしてみる
Webサーバー/シグナリングサーバは、Node.js + express + ws(WebSocket) で実装しています。
準備(セットアップ)
- 2台のiOSデバイスを用意 (iPad + iPad, or iPad + iPhone)
- それぞれに、iSH Shell をインストール、apk, node.js もインストール
- それぞれに、gitもインストール
- apk add git
- それぞれに、コードを取得(ブランチを指定してください)
~# apk add git
~# git clone https://github.com/mganeko/webrtc_1to1.git
~# cd webrtc_1to1
~# npm install
準備(情報収集)
今回シグナリングサーバー役のデバイス1は、iPadを利用する必要があります。(iSHをバックグラウンドに回さないように)。一方、デバイス2側からは、デバイス1のIPアドレスを知っておく必要があります。
- (A) 両方が、同じWiFi環境にいる場合
- デバイス1側で、割り振られているIPアドレスを確認
- 「設定」-「Wi-Fi」- 詳細情報(iボタン)で、ネットワークの情報を確認
- IPV4アドレスの表から、自分のIPアドレスを取得
- 192.168.0.xxx など
- (B) それ以外の場合
- デバイス1側で、「インターネット共有」(テザリング)をオンに
- デバイス2側で、デバイス1で共有したWiFiに接続
- デバイス2側で、接続情報を確認
- IPV4アドレスの表から、「ルーター」のアドレスを確認
- 172.20.xxx.1 など
- ※テザリングしているデバイス1でなく、そこのぶら下がるデバイス2側で確認します
(A),(B)それぞれに応じた方法で取得したシグナリングサーバー(デバイス1)のIPアドレスを、次のステップで利用します。
実行
(1) サーバーの起動
デバイス1、2の両方で、サーバーを起動します。
~# npm start
> webrtc_1to1@1.0.0 start /root/work/webrtc_1to1
> node server_1to1.js
websocket server start. port=8080
Web server start. http://localhost:8080/
(2) SafariでWebサーバーにアクセス
起動したらそれぞれのデバイスでWebサーバーにつなぎますが、デバイスごとにURLが異なります。
- デバイス1(ローカルのシグナリングサーバーに接続する)
- デバイス2(シグナリングサーバーのURLを別途指定する)
- http://localhost:8080/sub.html
- ※デバイス2がiPhoneの場合は、SahariとiSHを交互に切り替えながら、Webページをロードしてください
※iOSデバイスがスリープしてしまうと、サーバーの実行が止まります。再度起動し直してください。
(3) カメラ映像の取得
- デバイス1で、[Start Video]ボタンをタップ
- アクセス許可を求められたら、[許可]をタップしてください
- 同様にデバイス2でも、[Start Video]ボタンをタップ
- アクセス許可を求められたら、[許可]をタップしてください
(4) 接続
接続はデバイス2 (sub.html)側から行います
- デバイス2側で、テキストエリアにシグナリングサーバーのURLを入力
- 例えば、「ws://192.168.0.10:8080」
- ※ポート番号は 8080 です
- デバイス2側で[Connect]ボタンをタップ
- 接続情報が交換され、通信が開始します。
(5) 切断
- デバイス1,2 どちらでも良いので、[Hang Up]ボタンをタップします
- ※この状態で[Connect]ボタンをタップすると、再接続できます
コード
https://github.com/mganeko/webrtc_1to1.git にあります。
おわりに
「iOSでNode.jsを動かしたい」という何の実用性もない記事でした。もし不幸にも(?)そういう状況に陥ったら思い出してみてください。