パソコンで立ち上げたWebサーバにスマートフォンで接続するとき、パソコンのIPをいちいち調べなくてもよい方法

# 0. 概要

### 状況

パソコンで立ち上げたWebサーバにスマートフォンで接続したい。

### なにが問題か

スマートフォンからではlocalhostで接続できない。パソコンのIPを調べるのは面倒である。

### 解決方法

スマートフォンから接続可能なURLをQRコードで表示する。

### ダウンロード

https://github.com/toyohisa2nakada/local_qrcode/tree/main

プログラムは local_qrcode.js です。そのファイルを使う例が index.html です。

# 1. プログラムの使い方

```javascript
// ライブラリをロードします。
const { local_qrcode } = await import("./local_qrcode.js");
// idがqr_codeのcanvas要素にQRコードを描画します。
await local_qrcode.inject(document.getElementById("qr_code"));
// idがown_urlの要素のテキストに、URLの文字列を表示します。
document.getElementById("own_url").innerText = await local_qrcode.url();
```

scriptは、type=moduleで定義してください。ライブラリを読み込んで、canvasにQRコード、divやspanにURLの文字を表示する例です。

# 2. 処理の流れ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275020/3a7299e0-f4da-7675-12aa-d6fd0599ef96.png)

### URLの取得

window.location.href によって取得する。

### ホスト名部分をIPアドレスに変える

通常は localhost になっているホスト名を、外部のPCやスマートフォンから接続できるようにIPアドレスの数値標記に変える。

### QRコードを作成する

JavaScriptでQRコードを作成できるライブラリ(qrious)を使ってIPアドレスに変換したURLをQRコードで表示する。

https://github.com/neocotic/qrious

# 3. HTML部分

```index.html
<div>
    <!-- urlの表示用 -->
    <span id="own_url"></span>
</div>
<div>
    <!-- QRコード, サイズはライブラリ(qrious)が設定してくれる -->
    <canvas id="qr_code"></canvas>
</div>
```
bodyタグの中に記述してください。

# 4. QRコード作成ライブラリの読み込み

```local_qrcode.js
const _load_script = function (fname) {
    return new Promise((resolve, reject) => {
        const sc = document.createElement("script");
        sc.type = "text/javascript";
        sc.src = fname;
        sc.onload = () => resolve();
        sc.onerror = (e) => reject(e);
        const s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(sc, s);
    });
};
// QRコード作成ライブラリ
await _load_script("https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js");
```
local_qrcode.jsの最初で実行されます。

# 5. JavaScript部分

```javascript
// URL、localhostの部分がipアドレスの数値になるものの元
const testurl = window.location.href;

// プロトコル部分の抽出
const proto = /^([a-zA-Z][a-zA-Z\d+\-.]*):\/\//.exec(testurl)?.[1];
// ポート番号の抽出、デフォルトのポート番号の場合、空文字になる。
const port = /(:\d+)/.exec(testurl)?.[1] ?? "";
// パス部分の抽出
const path = /^(?:[^:]+:\/\/)?[^\/]+(\/.*)/.exec(testurl)?.[1];
// ipは、WebRTCを使って取得する。参考: https://stackoverflow.com/questions/20194722/can-you-get-a-users-local-lan-ip-address-via-javascript
const ip = await new Promise((resolve, reject) => {
    const pc = new RTCPeerConnection({ iceServers: [] });
    pc.createDataChannel("");
    pc.createOffer(pc.setLocalDescription.bind(pc), () => { });
    pc.onicecandidate = (ice) => {
        if (ice?.candidate && ice.candidate.candidate) {
            const ip = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}|([a-f0-9-]+\.local))/
                .exec(ice.candidate.candidate)[1];
            pc.onicecandidate = () => { };
            resolve(ip);
        }
    }
});

// localhostがipの数値になったURLを再構成する。
const own_url = proto + "://" + ip + port + path;
// URLの表示
document.getElementById("own_url").innerText = own_url;
// QRコードの作成
const qr = new QRious({
    element: document.getElementById("qr_code"),
    value: own_url,
});
```
scriptタグの中で記述してください。scriptタグは、type="module"となっていることを想定しています。

IPアドレスはWebRTCというリアルタイムで通信することを想定したライブラリによって数値を取得しています。URLのそれ以外の部分は正規表現によって抜き出して再構成しています。

GitHubで公開されているlocal_qrcode.jsは、エラー処理などが含まれているため、実際のコードは少し異なります。ここでは説明用に少し簡略化したコードを示しています。

# 6. 実行結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275020/5ce63c45-eb54-52b1-3e92-e1e14f016e17.png)


パソコンでWebサーバに接続した画面です。パソコンからlocalhostで接続しても、10.xx.xx.xxのようにURLがIPアドレスの数値になってQRコードが作成されます。

このQRコードをスマートフォンで読み込むと、パソコンで立ち上げたWebサーバに接続することができます。


# 7. 制限

+ スマートフォンは、Webサーバのパソコンと同じネットワークに入っていないと通常は接続できません
+ パソコンでHTMLファイルをダブルクリックして表示するときには、スマートフォンからは接続できません
+ パソコンのブラウザのアドレスに https:\/\/localhost/ としたhttpsプロトコルを使用するとJavaScriptは、mDNSというサーバを用いないローカルネットワーク用のホスト名を取得してQRコードを作成します。そのホスト名は、.local というトップレベルドメインで終わるホスト名です。ただこのホスト名は、2024年10月13日時点では、Android 12から接続することはできません。

# 8. パソコンで立ち上げるWebサーバについて

pythonをインストールしているなら、python -m http.serverで実行したフォルダをルートとしたWebサーバを起動することができます。