NodeJS WebRTC app using DataChannel isn't working in production server
I do not see the gathering of ice candidates in your code - so it is no surprise your peers cannot establish a connection with each other. Here is the working sample of what your code should look like.
streamer.js:
async function createPeer(configuration) {
const localCandidates = [];
// Step 1. Create new RTCPeerConnection
const peer = new RTCPeerConnection(configuration);
peer.onconnectionstatechange = (event) => {
console.log('Connection state:', peer.connectionState);
};
peer.onsignalingstatechange = (event) => {
console.log('Signaling state:', peer.signalingState);
};
peer.oniceconnectionstatechange = (event) => {
console.log('ICE connection state:', peer.iceConnectionState);
};
peer.onicegatheringstatechange = (event) => {
console.log('ICE gathering state:', peer.iceGatheringState);
};
// Step 5. Gathering local ICE candidates
peer.onicecandidate = async (event) => {
if (event.candidate) {
localCandidates.push(event.candidate);
return;
}
// Step 6. Send Offer and client candidates to server
const response = await fetch('/broadcast', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
offer: offer,
candidates: localCandidates,
}),
});
const {answer, candidates} = await response.json();
// Step 7. Set remote description with Answer from server
await peer.setRemoteDescription(answer);
// Step 8. Add ICE candidates from server
for (let candidate of candidates) {
await peer.addIceCandidate(candidate);
}
};
// Step 2. Create new Data channel
const dataChannel = peer.createDataChannel('host-server');
dataChannel.onopen = (event) => {
dataChannel.send('Hello from client!');
};
dataChannel.onclose = (event) => {
console.log('Data channel closed');
};
dataChannel.onmessage = (event) => {
console.log('Data channel message:', event.data);
};
// Step 3. Create Offer
const offer = await peer.createOffer();
// Step 4. Set local description with Offer from step 3
await peer.setLocalDescription(offer);
return peer;
}
const configuration = {
iceServers: [
{
urls: 'stun:global.stun.twilio.com:3478?transport=udp',
},
],
};
// Add turn server to `configuration.iceServers` if needed.
// See more at https://www.twilio.com/docs/stun-turn
createPeer(configuration);
server.js:
const express = require('express');
const bodyParser = require('body-parser');
const webrtc = require('wrtc');
const port = process.env.PORT || 80;
const configuration = {
iceServers: [
{
urls: 'stun:global.stun.twilio.com:3478?transport=udp',
},
],
};
// Add turn server to `configuration.iceServers` if needed.
const app = express();
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.post('/broadcast', async (req, res) => {
const {offer, candidates} = req.body;
const localCandidates = [];
let dataChannel;
// Step 1. Create new RTCPeerConnection
const peer = new webrtc.RTCPeerConnection(configuration);
peer.ondatachannel = (event) => {
dataChannel = event.channel;
dataChannel.onopen = (event) => {
dataChannel.send('Hello from server!');
};
dataChannel.onclose = (event) => {
console.log('Data channel closed');
};
dataChannel.onmessage = (event) => {
console.log('Data channel message:', event.data);
};
};
peer.onconnectionstatechange = (event) => {
console.log('Connection state:', peer.connectionState);
};
peer.onsignalingstatechange = (event) => {
console.log('Signaling state:', peer.signalingState);
};
peer.oniceconnectionstatechange = (event) => {
console.log('ICE connection state:', peer.iceConnectionState);
};
peer.onicegatheringstatechange = (event) => {
console.log('ICE gathering state:', peer.iceGatheringState);
};
peer.onicecandidate = (event) => {
// Step 6. Gathering local ICE candidates
if (event.candidate) {
localCandidates.push(event.candidate);
return;
}
// Step 7. Response with Answer and server candidates
let payload = {
answer: peer.localDescription,
candidates: localCandidates,
};
res.json(payload);
};
// Step 2. Set remote description with Offer from client
await peer.setRemoteDescription(offer);
// Step 3. Create Answer
let answer = await peer.createAnswer();
// Step 4. Set local description with Answer from step 3
await peer.setLocalDescription(answer);
// Step 5. Add ICE candidates from client
for (let candidate of candidates) {
await peer.addIceCandidate(candidate);
}
});
app.listen(port, () => console.log('Server started on port ' + port));
I found your stun server not fully functional, so I replaced it with another one from Twillio. Also, I added event handlers with which it is easy to track the state of the WebRTC session. You would do well to learn more about WebRTC connection flow, really.