【JavaScript 応用講座】🛰️P2P通信に関する技術検証メモ

🔧 現在の目的

  • ゲームやWebアプリ内で P2P通信を用いたアセット共有・同期 を行う。
  • 特に、ZIPファイル(画像・カードデータなど)を安全にやり取りする手段 として、サーバを介さない通信の可能性を検討中。

🔍 検討・調査したポイント

1. P2Pに必要な基本構成

  • シグナリングサーバ:WebRTCの初期接続(ICE candidateやSDPのやりとり)に必須。

    • 代替案として:

      • Firebase Realtime Database
      • WebSocketサーバ(Node.js + ws)
      • GitHub Gist / Pastebin / Discord などを一時的な手段として使うことも可能?
  • STUN/TURNサーバ:NAT越えの支援

    • GoogleのSTUNサーバ(stun:stun.l.google.com:19302)をテスト候補に。

2. 検討したライブラリ・ツール

ツール名 特徴 懸念点
SkyWay 日本語対応・WebRTCラッパー サービス依存・商用利用で制限あり
PeerJS シンプルなP2P構築が可能 シグナリングサーバを別途建てる必要あり
simple-peer WebRTCの薄いラッパーで柔軟性高い 設定や周辺構成は自前で用意する必要あり
socket.io P2Pというよりクライアント-サーバ型 TURNサーバの役割には不向き

3. 現時点での構成案(暫定)

  • simple-peer + Node.js製のシグナリングサーバ(もしくは Firebase)で、純粋なP2P通信を試す。
  • ZIPファイルは Blob 化し、DataChannel 経由で送信。
  • 通信経路は暗号化されるが、通信内容の暗号化(ファイルレベル)も別途検討中

4. 課題・今後の検証ポイント

  • 通信量制限(DataChannel で送れるファイルサイズの上限)
  • 送受信時のZIPファイルの整合性チェック(MD5やSHA-1など)
  • シグナリング手段の安定化(無料かつ軽量な選定)
  • モバイル対応とブラウザ制限(Safariなどの挙動)
  • 通信の確立時間(接続までのタイムラグ)

PeerJS 開発終了?


user icon

PeerJSについて調べると開発が止まってる、メンテナンス、更新されてない。 なので、今のブラウザ環境では動かない可能性が高いなど。


ai icon
おっしゃる通り、PeerJSは現在「実質的にメンテナンスされていない」状態 にあり、以下の理由で新規導入にはリスクが高い
です。

🚨 PeerJS の現状(2025年時点)

項目 内容
開発状況 GitHubでの更新が数年止まっている
公式サーバー peerjs.com のクラウドホスティングも不安定/終了
利用ブラウザ 古い仕様に依存している可能性あり(特に adapter.js 周り)
替えの推奨 よりモダンなライブラリや素の WebRTC が推奨されつつある

🛠 代替案:現実的な選択肢

✅ ① 素の WebRTC + 自作シグナリング

  • Socket.ioやWebSocketを使って、接続の交換(SDP/ICE)だけやる
  • データ送受信は純粋な RTCDataChannel

🔧 学習コストは高いが自由度&将来性あり


✅ ② libp2p

  • IPFSや分散型通信の標準プロトコル(WebRTCにも対応)
  • モダンで活発に開発されており、将来性が高い

🔧 導入はやや複雑(ノードID管理、プロトコル設計などが必要)


✅ ③ Simple-Peer

  • PeerJSと同じように抽象化された、モダンなWebRTCラッパー
  • 軽量で人気があり、npmでも現役
npm install simple-peer
import Peer from "simple-peer";

// 例:送信側
const peer = new Peer({ initiator: true, trickle: false });
peer.on('signal', data => {
  // このdataを相手へ渡す(WebSocketやチャットで)
});
peer.on('connect', () => {
  peer.send("hello");
});

🔧 シグナリングは別途自前で用意する必要あり(ここだけSocket.ioなどで補完)


✅ 結論:PeerJSは避けて、Simple-Peer + WebSocket が現実的!

あなたのように「技術を遊びながら学びたい/でも安全な道を選びたい」タイプには:

🔷 Simple-Peer + WebSocketチャットによるシグナリング → P2PでBlob共有

という構成が最も良いと思います。

SkyWayとはなんなのか?

SkyWay(スカイウェイ)は、NTTコミュニケーションズが開発・提供する国産のWebRTCプラットフォーム/APIサービス です。


🌐 SkyWayとは?

WebRTCを使った音声・映像・データ通信を、簡単に実装できるP2P通信プラットフォーム


✅ 主な特徴

項目 説明
📦 提供元 NTTコミュニケーションズ
🌐 ベース技術 WebRTC(音声・映像・データ通信)
🧰 機能 P2P接続、メッシュ/サーバ型ルーティング、データチャネル
📦 提供形態 JavaScript SDK、iOS/Android SDKもあり
💡 補足 自前のシグナリングサーバやTURNサーバを用意せずに済む
🔐 セキュリティ 国内運用、商用利用向けにも安心感あり

🔧 SkyWayが便利な理由

  • PeerJSよりもモダンで安定(NTTが商用向けに運用)
  • WebRTCの面倒な部分(ICE候補、NAT越え)を全部やってくれる
  • サーバ不要でP2P通信/多人数接続/ファイル共有が実装可能
  • 日本語ドキュメント&開発者コミュニティも存在

📦 SkyWayとPeerJSの関係

実は、SkyWayは元々PeerJSのフォーク(改良版)としてスタート しています。

🔽 過去には SkyWay-PeerJS という互換ライブラリがありました
つまり「PeerJSの問題点を解消した日本製ライブラリ」でもあるのです。


✍️ どう使うの?

npm install @skyway-sdk/room

最小サンプル(データ通信)も数行で動きます:

import { SkyWayContext, SkyWayRoom } from '@skyway-sdk/room';

const context = await SkyWayContext.Create(token);
const room = await SkyWayRoom.FindOrCreate(context, { name: 'my-room' });
const me = await room.join({ name: 'user1' });

me.onStreamPublished.addHandler((stream) => {
  // 映像やデータの受信処理
});

※トークン発行にはSkyWayの管理画面(無料登録)を使用。


🧭 結論:SkyWayは…

🔹「PeerJSの弱点を埋めた、日本発のWebRTCプラットフォーム」
🔹「初心者でもP2P通信を扱えるようにした商用レベルのAPI」
🔹「ファイル共有にも応用できる強力なP2P土台」

「WebRTC」 と 「simple-peer」

✔ なぜ simple-peer?

特徴 内容
🧬 軽量 わずか数kBでP2P接続が可能(WebRTCのラッパー)
🧠 分かりやすいAPI PeerJSよりもモダンで制御しやすい
🔧 柔軟性 WebSocketと組み合わせて独自シグナリングも可能
🧑‍💻 メンテ継続中 現在もGitHubで開発・Issue対応あり
📦 CDNも可 npmなしでもscriptタグで使用できる

⚠️ WebRTCを「生」で扱うと?

まさに「地獄を見る」とよく言われます。

  • ICE候補
  • STUN/TURNサーバー
  • SDP交換
  • ネットワーク環境による失敗
  • NAT越えのトラブル

など、通信以前の“儀式”が多すぎる んです。


✅ おすすめの現実的ルート

段階 内容
🔰 ① simple-peer を使った最小のファイル送受信アプリ作成
🌐 ② シグナリングに WebSocket(自前 or サービス)を使う
🧪 ③ ファイル転送に Blob や ArrayBuffer の分割転送を試す
🔐 ④ メタデータ共有+「相手が持ってるZIPの取得」まで拡張

💡 simple-peerの導入イメージ

npm install simple-peer

JavaScriptサンプル(Sender側)

const peer = new SimplePeer({ initiator: true, trickle: false });

peer.on('signal', data => {
  // data を WebSocketなどで送る
});

peer.signal(remoteSignalData); // 相手のsignal情報

peer.send("こんにちは、世界!");

受信側では:

peer.on('data', data => {
  console.log("受信:", data.toString());
});

🎯 まとめ

  • simple-peer がシンプル・現実的・自分で制御できる
  • WebRTCネイティブは沼。必要になるまで避けるのが吉


user icon

> npm install simple-peer

これって、node.js では?
サーバどうする?


ai icon
はい、鋭い!その通りです。
simple-peer は確かに Node.js 環境でも使えますが、ブラウザ用にも動くように設計されたライブラリです。
だから、実際にはこういう選択肢があります:

simplepeerは Node.js 必須?

A. 必須じゃない。CDNで読み込めばブラウザだけでも動く。

🧩 CDN使用の方法(完全クライアントサイドでも可能)

<script src="https://unpkg.com/simple-peer@9.11.1/simplepeer.min.js"></script>
<script>
  const peer = new SimplePeer({ initiator: location.hash === '#1', trickle: false });

  peer.on('signal', data => {
    console.log("Signalデータをコピーして相手に送ってください:", JSON.stringify(data));
  });

  peer.on('data', data => {
    console.log("受信データ:", data.toString());
  });

  // 相手から受け取った signal をここに貼って送信
  function receiveSignal(signal) {
    peer.signal(signal);
  }
</script>

🔗 つまり:

  • Node.js環境がなくても動く
  • HTMLだけでP2P接続可能(ただし、signalのやりとりは手動 or 別手段で)

🌐 Q. サーバどうするの?

A. 「WebRTC=P2P」でも最初の"仲介"(シグナリング)は必要

方法は大きく2つ:

シグナリング方法 説明 難易度
📝 手動 Signal(JSON文字列)をコピペで交換 超カンタン
📡 WebSocket 小さなシグナリングサーバを立てて自動化 中級者向け
🛰 SkyWayなど サービスに任せる(制限あり) 簡単だけど将来性×

✅ 最初はこれでOK!

🧪 まずは手動でSignal交換するバージョンで試すのが鉄則!

  • ブラウザだけで完結
  • ページを開いて、表示された「Signalデータ」を相手に送る
  • 相手がそれを peer.signal() に渡す

🔧 もし将来的に WebSocketサーバを用意するなら…

  • Node.js + ws モジュールで数十行で作れます
  • Netlify FunctionsやRenderの無料枠でも十分
  • でもまずはローカル or 手動交換で構造を理解すべき!

🚀 まとめ

やること 要Node.js? 備考
simple-peer動かすだけ ❌ いらない(CDNでOK)
Signalの交換を自動化 ✅ WebSocketサーバ必要
ZIP共有P2P化 ✅ WebRTCのdatachannel使用

🔌 WebRTCの「シグナリング」とは?

P2Pで通信を開始する前に、お互いの情報(IPアドレス、ポート、暗号化情報など)を交換する必要があります。 これが「シグナリング(signal exchange) 」。


🌐 Winnyの場合

項目 内容
ノード発見 最初の「ノードリスト」をWebから取得
接続試行 既知のノードとP2P接続を確立
拡散 接続に成功したノードから、さらに他のノード情報を入手して分岐的に拡張

📡 WebRTC(simple-peer)の場合

フェーズ 役割 方法
シグナリング 初期情報の交換 JSONで signal を送受信(Winnyでいう「初期ノードリスト」)
P2P接続確立 NAT越えなどを処理 STUN/TURNを自動で使う
datachannel通信 ファイルやチャットのやり取り P2P接続後に自由な通信が可能

✅ つまり…

WebRTCの signal 情報 = Winnyでいう「ノードリストの1人分の情報」

  • 自分の signal を相手に渡すことで「繋いでいいよ」の合図を送る
  • 相手も自分の signal を返してくると、P2Pが確立

🧠 重要な違い

項目 Winny WebRTC
ノード数 数百〜数千の多対多ネット 通常は1対1(グループも可能)
ネット構造 再帰的ネットワーク 明示的なペア
シグナリング方法 専用ノード or Web配布 自作・外部サービス・手動など自由

🛠 どうするのが良いか?

あなたが目指す方向が:

  • 🔹 ゲームアセット共有やチャット用途
  • 🔹 匿名性よりシンプル動作優先
  • 🔹 構造理解も目的

なら、

✅ まずは「手動シグナリング」から!

  • Winnyの最初期ノード配布のように「Signal情報をコピペで共有」するだけでOK
  • 動作が分かってから、必要に応じて「簡易WebSocketサーバ」を追加すると良い



user icon

やっぱり、サーバが1つはいりますね。
ゲームアプリでアセットをP2Pで探す場合、サーバの index.php?list=node.jsonとかを参照して、ノードリストを取得して接続を試みる。
サーバ側は、参照したユーザーの情報を元にnode.jsonを更新。
これで、最新情報が保たれて、少ないユーザーでもなんとか接続が出来る?

そこまでするならP2Pでなくてもいいかもだけど…。


ai icon
おっしゃる通りです。
そして、その構成はP2Pの“クラシックだけど実用的な”ハイブリッド型 です。
以下に整理しますね:

サーバは「必要」。でも“最低限”でいい。

🎯 最低限のサーバだけ用意して、
🎯 あとは「ユーザー同士で直接データをやり取り(P2P)」

➤ この思想こそが Winny 以降の「P2Pハイブリッド構成」です。


項目 P2Pオンリー ハイブリッド構成(あなたの案)
初期接続のしやすさ 難しい(事前情報が必要) サーバで仲介しやすい
負荷 0 低(リスト配布のみ)
信頼性 低(誰とも繋がらない可能性) 中(最小限の保証あり)
匿名性 中程度
実装の難易度 高い 比較的やさしい

💡 「P2Pでなくてもいいのでは?」に対して

これはとても本質的な問いです。以下の観点で検討できます:

条件 選択肢
少人数・同期性重視 P2Pが向いている
多人数・非同期(いつでもDL) サーバ配布の方が安定
ストレージ負荷を分散したい P2Pが有利
著作権・セキュリティを制御したい サーバ配信が管理しやすい

あなたが考えている「プレイヤー間のアセット共有 」のような用途は、“ある程度信頼できる小規模ネットワーク”において、P2Pは最適 です。


🔧 実装ヒント(サーバ側)

  • index.phpREMOTE_ADDRsignalInfo を受け取って node.json に追記
  • GET されたら最新20件くらいをランダムに返す
  • node.json の形式は以下のように:
[
  {
    "id": "user01",
    "ip": "203.0.113.10",
    "signal": "..."
  },
  ...
]

🚦今後の実験ステップ提案:

  1. simple-peer を使った 手動シグナリングの実験(signalコピペで接続)
  2. それを自動化するための 最小限の index.php サーバを用意
  3. 成功したら、 アセットの送受信(Blob→ZIP) を載せていく
user icon
IPが記録&共有されるのが嫌という人もいるので、Winnyみたいに暗号化するとか?
でも、JSコード解析されると直ぐバレると思いますが…。

ハイブリッド型の場合、PHPファイルで公式認定のZIPを参照出来る他、P2P共有で非公式アセットZIPを自己責任でユーザー間で共有できるというメリットはありますね。

PHPサーバがあれば、両方対応できる。

ai icon
まさにその通りです。
あなたが描いている構想は、安全性・自由度・現実的な実装負荷のバランスをとった理想的な設計です。

✅ ハイブリッド構成の利点(改めて整理)

機能 P2P共有 サーバ共有(PHP)
🔓 非公式アセット ◎(自由に共有) ×(登録制限)
✅ 公式アセット △(複製の保証なし) ◎(常にDL可能)
💬 柔軟性 ◎(誰でも共有) △(管理者判断)
🛡️ 安全性 △(スクリーニング困難) ◎(チェック済み)
⚙ 拡張性 ◎(配布型に近い) △(ストレージ必要)

🧠 「IPアドレス共有が嫌」への対応策

完全匿名のP2Pは現代JSではほぼ不可能です。ですが、以下のような**“現実的な妥協策”**はあります:

1. ✅ IPを直接は公開しない

  • node.json には signalデータ(SDP + ICE) だけを記録
  • ip:port は signal 交換時に WebRTC が裏で解決
  • WebRTCではSTUN/TURNによってIPが経由されるが、ユーザーに直接は見えない

2. 🔐 ZIPの整合性チェックを導入

  • SHA256 のチェックサムをつけて、破損や改ざんを検知
  • サーバが提供する「公式SHA256リスト」と照合すれば信頼性も担保できる
{
  "title": "非公式アセットA",
  "author": "匿名うさぎ",
  "hash": "bd50d7a5f51bcb0e...",
  "filename": "assetA.zip"
}

🔐 暗号化は意味あるのか?

❗️ JSはフロントで動くため、暗号鍵もバレる=暗号化は難しい

でも、整合性チェック(ハッシュ)とUIでの警告表示で十分抑止力はあります。

たとえば:

if (zipHash !== knownHash) {
  alert("このZIPファイルは改変されている可能性があります。\n公式サイトで確認してください。");
}

🗂 想定UI(例)

▼ 公式アセット一覧(PHPサーバ配信)
[✔] アセットA.zip(SHA256一致) [ダウンロード]

▼ ユーザー共有アセット(P2P)
[⚠] アセットX.zip(ハッシュ不一致) [受け取る] [拒否]

▼ 検証済み非公式アセット(サーバに自動登録済)
[✔] アセットY.zip(署名済) [ダウンロード]

🌐 サーバの役割は?

  • ノード一覧 (node.json)
  • アセット署名・検証済みリスト
  • 公式アセットの配布(+画像/サムネ)

“信頼の起点” としての存在です (あとはユーザーの自由)


🔄 実験の優先順(次ステップ)

  1. ✅ IndexedDB → ZIP(済)
  2. ✅ ZIP生成 → Blob送信(仮完了)
  3. 🔄 signal交換 → WebRTC接続(simple-peer or 手動で)
  4. 🔄 Blob送信(ZIPをdatachannelで送信)
  5. ✅ ZIPハッシュ生成 → チェック処理
  6. ✅ PHPサーバ構築(公式ZIP + node.json管理)
user icon
>node.json には signalデータ(SDP + ICE) だけを記録

これがよくわからないのですが、どういう情報でしょうか?
各端末ごとに、固有IDを作成して、それで判別?

ai icon
とても鋭い質問です!
「SDP + ICE」とは、WebRTCでピア同士が直接通信するための“名刺交換”にあたる情報です。
順を追ってわかりやすく説明しますね。

💡 WebRTCにおける signal 情報とは?

WebRTC はピアツーピア接続を行うために、以下の「接続手順」を踏みます:

🧭 ステップ1:Signal情報の交換

要素 内容
SDP(Session Description Protocol) お互いの音声/映像/通信能力の自己紹介。例えば「私はZIPファイルを送れる」など
ICE(Interactive Connectivity Establishment) 実際にどうやって接続すればいいかの候補一覧(IPアドレス、ポートなど)

🧪 例:

{
  "sdp": "...長い文字列...",
  "ice": [
    { "candidate": "candidate:...", "sdpMid": "0", "sdpMLineIndex": 0 },
    ...
  ]
}

📌 補足:SignalはJSコードで生成される

このデータは、WebRTCの RTCPeerConnection を使って以下のように生成します。

const peer = new RTCPeerConnection();

peer.createOffer().then(offer => {
  return peer.setLocalDescription(offer);
}).then(() => {
  // peer.localDescription にSDPが入る
  console.log("SDP:", peer.localDescription);
});

ICE候補も以下で取得します:

peer.onicecandidate = (event) => {
  if (event.candidate) {
    console.log("ICE Candidate:", event.candidate);
  }
};

🧾 node.json に記録する内容(例)

各ピアがサーバに以下のようなJSONを送って共有する、というイメージです:

[
  {
    "peerId": "abc123",               // 固有ID(手動生成でも可)
    "timestamp": 1721382181,          // 最終更新時刻
    "assets": ["main.zip", "cute.zip"],
    "signal": {
      "sdp": "v=0\r\no=- ...",        // offer SDP
      "ice": [ ... ICE候補リスト ... ]
    }
  },
  ...
]

🎯 固有ID(peerId)とは?

PeerJSのようなライブラリでは、ピアごとに peerId を自動生成しますが、今回は自作なので以下でもOKです:

  • ランダム文字列(Math.random().toString(36).slice(2)
  • UUID
  • 名前付きハンドル
  • 端末情報に基づく一意キー(ただしプライバシー注意)

この peerId によって、誰がどんなアセットを持っていて、signal情報は何か、を識別できるというわけです。


🔄 接続の流れ(イメージ)

1. USER01  サーバに自分の signal 情報をアップロード(node.json に登録)

2. USER02  node.json を読み取り、「あ、この人が 'main.zip' を持ってる」と判明

3. USER02  USER01  signal 情報を使って接続を試みる(= WebRTC開始

4. USER01  USER02  direct connection 成立(ZIPデータ送受信

📦 なぜSDPとICEが必要?

これは WebRTC の仕様で、「直接通信(P2P)を可能にするための必須情報」だからです。 サーバを介して最初にこの情報を交換することで、その後はP2P通信だけで済むようになります。


✨ まとめ

項目 内容
signalとは? WebRTCの接続に必要な自己紹介データ(SDP + ICE)
なぜ必要? ブラウザは最初にsignalを交換しないと通信できないため
node.jsonに入れるもの peerId, signal情報, 持ってるアセット名, 最終更新時刻 など
固有IDとは? 自作でもOK(ランダム文字列 or UUIDなど)

🔚 次のステップ

  • simple-peer を使った基本的なP2P通信のテスト
  • ZIPファイル送信のプロトタイプ実装
  • シグナリングサーバの準備(Node.js or Firebase)