![【JavaScript 応用講座】: [WebRTC/P2P] チャット&ファイル転送ができる双方向アプリを実装](https://humanxai.info/images/uploads/javascript-p2p-05.webp)
🎯 目的と背景
前回の記事で、シグナリングサーバを作成しましたが、今回は、そのサーバを活用して、クライアント同士を接続して、チャットと、ファイル送信が出来るまで、実装していきます。
前回の記事。

【JavaScript 応用講座】:P2P通信の基盤!Node.js製シグナリングサーバをKoyebで構築
Node.js と WebSocket を使ってシンプルなシグナリングサーバを構築し、無料のクラウドサービス Koyeb にデプロイする手順をまとめました。GitHub連携から起動確認、実際の接続テストまで、P2P通信の基盤となるシステムを最短で構築できます。
https://humanxai.info/posts/javascript-p2p-04/🔧 技術スタック
- JavaScript + HTML + CSS
- simple-peer(WebRTCラッパー)
- Node.js + WebSocket(Signaling Server)
- ローカル環境(LiveServer)で2画面起動
- Blobによるファイル受信
Signalingサーバ構築
- Node.js + ws
- Renderで無料ホスティング
- on(“message”) で offer/answer を中継
- ログ確認で送信内容を明確に出力
以下は通信ログのスクショ・サンプルです。

1. WebSocket通信でsignalを中継する
const signalingSocket = new WebSocket("wss://your-app.koyeb.app");
signalingSocket.onopen = () => {
console.log("🌐 WebSocket接続完了!");
};
signalingSocket.onmessage = (event) => {
const remoteSignal = JSON.parse(event.data);
console.log("📥 signal受信:", remoteSignal);
peer.signal(remoteSignal); // 相手のsignalを反映
};
🌐 サーバに2つのクライアントが繋がる流れ(イメージ)
P2P通信の流れ(signaling-server使用):
[Client A] initiator: true
└─── signal(オファー)──→
\
[signaling-server]
/
←─── signal(アンサー)──┘
[Client B] initiator: false
- Aが initiator: true でsignal(オファー)を発行
- signaling-server を通じて B に送信
- Bが peer.signal() で受信&応答(アンサー)
- Aが応答を受け取って P2P接続確立
signal発火時に WebSocket で送信
peer.on("signal", (data) => {
if (signalingSocket.readyState === WebSocket.OPEN) {
signalingSocket.send(JSON.stringify(data));
console.log("📤 signal送信:", data);
} else {
console.warn("WebSocketがまだ開いていません");
}
});
ERROR: “[object Blob]” is not valid JSON
Koyeb上のNode.js WebSocketサーバは ws.send(JSON.stringify(…)) で送信していても、 ブラウザ側の WebSocket 実装が MIME を検出して Blob として扱うことがある。
このようなケースでは .text() で明示的に文字列化が必要になります。
const text = await event.data.text(); // Blob → string
const signal = JSON.parse(text); // string → object
WebSocket接続と、peer.signal(…) の遅延問題
🔁 解決策:一時保存して、接続完了後に送信
let pendingSignals = [];
peer.on("signal", (data) => {
if (signalingSocket.readyState === WebSocket.OPEN) {
signalingSocket.send(JSON.stringify(data));
console.log("signal送信2");
} else {
pendingSignals.push(data); // 一時保存
console.log("🕒 signalingSocket未接続、後で送信予定");
}
});
signalingSocket.onopen = () => {
console.log("🌐 signaling server へ接続成功");
// 一時保存されていたシグナルを送る
pendingSignals.forEach((data) => {
signalingSocket.send(JSON.stringify(data));
console.log("📤 遅延送信:", data);
});
pendingSignals = [];
};
🧠 なぜこの問題が起こるのか?
- peer.signal(…) の呼び出しは早い段階で発火する。
- でも WebSocket の接続は非同期で、ちょっと時間がかかる。
- そのため「接続前に送信しようとして失敗」が起こる。
Uncaught TypeError: Cannot read properties of undefined (reading ‘readyState’)
🔍 原因
if (signalingSocket.readyState === WebSocket.OPEN)
この行で signalingSocket が未定義(undefined)なのに .readyState を読もうとして…
Uncaught TypeError: Cannot read properties of undefined (reading 'readyState')
という典型的なエラーになります。
🧠 つまり
- まだ connectSignalingServer() が呼ばれていない
- だから signalingSocket は 存在していない
- peer.on(“signal”, …) は即時に走ることがあるため、順番が重要
✅ 対策(1行の修正)
if (signalingSocket && signalingSocket.readyState === WebSocket.OPEN)
✅ 理想的な順序
- ユーザーが initiator / responder を決定
- connectSignalingServer() を呼んで WebSocket を開く
- そのあと createPeer(true or false) を呼び出す
この順序が最も安全です。
WebSocketが確実に開通してから peer を作るのがベスト。
📌 注意点
- 同一Koyebサーバに複数ブラウザ(もしくは別端末)から接続しておくと、相互にsignalが届きP2P接続が成立します。
- trickle: false にしているので、1回のsignal交換だけで完了します(ICE候補をまとめて送信)。
成功後のスクリーンショット

2. P2Pチャット通信
- createPeer(true) / createPeer(false) でinitiator/receiver分離
- signalingSocket未接続時にはsignal一時保存
- P2P接続成功後に signal(data) → send
- チャットは “あなた”, “相手” として表示切替
チャットに関しては、昨日の実装内容でそのまま動いた為、徳にコードは書いてないです。
![【JavaScript 応用講座】:[P2P通信入門] P2P通信でファイル送受信【簡易チャット付き】](https://humanxai.info/images/uploads/javascript-p2p-03.webp)
【JavaScript 応用講座】:[P2P通信入門] P2P通信でファイル送受信【簡易チャット付き】
JavaScriptだけで、ブラウザ間でZIPファイルの送受信ができる!? チャンク送信+Blob再構築+チャット対応のフル実装。
https://humanxai.info/posts/javascript-p2p-03/
3. ZIPファイル転送
<input type="file">
から読み込み →readAsArrayBuffer
- DataChannelで送信(Uint8Arrayで分割)
- 受信側で連結 → Blob化 →
createObjectURL
- ZIP受信成功ログとダウンロードリンク生成
これも、昨日の実装のまま動いたため特に変更は無いです。

🧪 難しかった点と解決策
- signalingSocket未接続時の readyState エラー → 一時保存&後送信で対応
- peer.on(“signal”) タイミングが難しい → ページ読み込み順を工夫
- blob受信→再構成のロジックは先に簡単なテキストで検証してから導入
💬 感想とまとめ
サーバに繋がるまでに少し苦労しましたが、その後は、先日の実装コードがそのまま動いたため楽に実装出来ました。
手動で接続していた際は、signal情報を双方にコピペする必要がありとても面倒でしたが、サーバを経由する事で、その負担が無くなり凄く楽になりました。
それを「代わりにやってくれている」のが、あなたの立てた Node.js+WebSocket のシグナリングサーバです。
🔁 サーバ側で何が行われているのか?
クライアント(ブラウザ)は peer.signal(...)
により、「自分の接続情報」(SDPやICE候補など)を signal
オブジェクトとして生成します。
それを WebSocket を通じてシグナリングサーバに送信。 サーバはそれを **「他の接続中のクライアントに中継」**します。
🧠 もっと具体的に言うと…
サーバコードの中でこんな流れになっています:
const peers = [];
wss.on('connection', (ws) => {
peers.push(ws);
ws.on('message', (data) => {
// 受け取った signal 情報を他の全クライアントへ転送
peers.forEach(client => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
ws.on('close', () => {
// 切断時にクライアントリストから削除
const index = peers.indexOf(ws);
if (index !== -1) peers.splice(index, 1);
});
});
☑ サーバがしていること(要点)
処理 | 内容 |
---|---|
接続管理 | 接続してきたクライアントを peers 配列で管理 |
signal中継 | 一方のクライアントから受信した signal(JSON)を、他のクライアントに送信 |
切断処理 | クライアントが切断したら、配列から除外 |
🔄 なぜこれで signal の貼り付けが不要になるのか?
以前の「手動方式」では:
peer1.signal(...)
で生成された JSON を- 手動でコピー&ペーストして
- 相手側で
peer2.signal(...)
に渡す
という流れでした。
今は:
peer1.signal(...)
で生成された JSON を- WebSocket 経由でサーバに送信
- サーバがそれを
peer2
側へ中継 →peer.signal(...)
で自動的に処理!
という流れなので、signal の交換は相変わらず必須ですが、手間が自動化されただけなんです。
以前は signal 情報をコピペして相手に渡していたが、今はサーバが勝手に橋渡ししてくれる。
つまり、“P2Pの仲人役”をしてくれているのがシグナリングサーバです。
📎 参考・使用ライブラリ
関連リンク

【JavaScript 応用講座】:P2P通信の基盤!Node.js製シグナリングサーバをKoyebで構築
Node.js と WebSocket を使ってシンプルなシグナリングサーバを構築し、無料のクラウドサービス Koyeb にデプロイする手順をまとめました。GitHub連携から起動確認、実際の接続テストまで、P2P通信の基盤となるシステムを最短で構築できます。
https://humanxai.info/posts/javascript-p2p-04/![【JavaScript 応用講座】:[P2P通信入門] P2P通信でファイル送受信【簡易チャット付き】](https://humanxai.info/images/uploads/javascript-p2p-03.webp)
【JavaScript 応用講座】:[P2P通信入門] P2P通信でファイル送受信【簡易チャット付き】
JavaScriptだけで、ブラウザ間でZIPファイルの送受信ができる!? チャンク送信+Blob再構築+チャット対応のフル実装。
https://humanxai.info/posts/javascript-p2p-03/![【JavaScript 応用講座】:[P2P通信入門] simple-peerでシグナリングと接続を理解する](https://humanxai.info/images/uploads/javascript-p2p-02.webp)
【JavaScript 応用講座】:[P2P通信入門] simple-peerでシグナリングと接続を理解する
Webアプリ上でのP2P通信にチャレンジ!「WebRTCは難しそう…」と思っていたけれど、simple-peerを使えば意外とシンプルに実装できました。この記事では、実際の接続手順、initiator問題、シグナリングの中身までを丁寧に解説し、将来的な分散アプ …
https://humanxai.info/posts/javascript-p2p-02/
【JavaScript 応用講座】🛰️P2P通信に関する技術検証メモ
JavaScriptでのゲーム開発やWebアプリ内で P2P通信を用いたアセット共有・同期 を行う構想のメモ
https://humanxai.info/posts/javascript-p2p/
💬 コメント