I am trying to create a P2P chat file in Firefox using WebRTC, but I have so far been unsuccessful in finding any working examples or in modifying any of the examples that I have found to work between different computers on different networks. The examples that I have found work only on a single computer, or on two computers within the same network. I am looking for an example where I can do the following:
User 1 initiates the connection by pressing the start button, which generates a session description and gives it to them. They then email this session description to User 2.
User 2 pastes the session description from User 1 into a textarea and receives a second session description in response. User 2 then emails this second session description back to User 1.
User 1 pastes the second session description from User 2 into a textarea, which completes the connection.
The session now completed, both users can chat back and forth.
Eventually, my aim is to accomplish the session description transfer through a centralized server, but I want to establish this method as a proof-of-concept first, because if I cannot get this to work, then I would rather not have spent the time on the server bit.
These two examples are pretty much what I want to do, but they only appear to work on a single computer:
https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Simple_RTCDataChannel_sample
https://webrtc.github.io/samples/src/content/datachannel/basic/
This example is exactly what I want to do, but it is 7 years old and does not appear to work anymore:
https://cjb.github.io/serverless-webrtc/serverless-webrtc.html
Here is my adaptation of Mozilla's example file above that I am testing out:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body {
font-family: "Lucida Grande", "Arial", sans-serif;
font-size: 16px;
}
.messagebox {
border: 1px solid black;
padding: 5px;
width: 450px;
}
.buttonright {
float: right;
}
.buttonleft {
float: left;
}
.controlbox {
padding: 5px;
width: 450px;
height: 28px;
}
</style>
<script>
var initiateConnectionButton = null;
var getAnswerButton = null;
var completeConnectionButton = null;
var disconnectButton = null;
var sendButton = null;
var messageInputBox = null;
var receiveBox = null;
var localConnection = null;
var sendChannel = null;
var receiveChannel = null;
var constraints = null;
var configuration = {
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
};
function handleSendChannelStatusChange(event) {
return;
}
function handleReceiveChannelStatusChange(event) {
return;
}
window.addEventListener('load', function () {
initiateConnectionButton = document.getElementById('initiate-connection-button');
getAnswerButton = document.getElementById('get-answer-button');
completeConnectionButton = document.getElementById('complete-connection-button');
disconnectButton = document.getElementById('disconnect-button');
sendButton = document.getElementById('sendButton');
messageInputBox = document.getElementById('message');
receiveBox = document.getElementById('receivebox');
initiateConnectionButton.addEventListener('click', function () {
localConnection = new RTCPeerConnection();
sendChannel = localConnection.createDataChannel("sendChannel");
sendChannel.onopen = handleSendChannelStatusChange;
sendChannel.onclose = handleSendChannelStatusChange;
localConnection.createOffer()
.then(function(offer) {
console.log(JSON.stringify(offer));
localConnection.setLocalDescription(offer);
})
.catch(function(error) {
console.log("Unable to create an offer: " + error.toString());
});
}, false);
getAnswerButton.addEventListener('click', function () {
localConnection = new RTCPeerConnection(configuration);
sendChannel = localConnection.createDataChannel("sendChannel");
sendChannel.onopen = handleSendChannelStatusChange;
sendChannel.onclose = handleSendChannelStatusChange;
localConnection.onicecandidate = function(e) {
localConnection.addIceCandidate(e.candidate);
// if (e.candidate && e.candidate['candidate'].includes("TCP")) {
// localConnection.addIceCandidate(e.candidate);
// }
}
let sessionDescription = new RTCSessionDescription(JSON.parse(document.getElementById("local-description-input").value));
localConnection.setRemoteDescription(sessionDescription)
.then(function() {
answer = localConnection.createAnswer();
})
.then(function(answer) {
console.log(answer);
let result = localConnection.setLocalDescription(answer);
return result;
})
.catch(function(error) {
console.log("Error: " + error.toString());
});
}, false);
completeConnectionButton.addEventListener('click', function () {
localConnection.ondatachannel = function (event) {
receiveChannel = event.channel;
receiveChannel.onmessage = function(event) {
var el = document.createElement("p");
var txtNode = document.createTextNode(event.data);
el.appendChild(txtNode);
receiveBox.appendChild(el);
};
receiveChannel.onopen = handleReceiveChannelStatusChange;
receiveChannel.onclose = handleReceiveChannelStatusChange;
};
localConnection.onicecandidate = function(e) {
if (e.candidate && e.candidate['candidate'].includes("tcp")) {
console.log(e.candidate);
localConnection.addIceCandidate(e.candidate);
}
}
let sessionDescription = new RTCSessionDescription({
type: "answer",
sdp: atob(document.getElementById("remote-description-input").value)
});
localConnection.setRemoteDescription(sessionDescription);
console.log(localConnection);
}, false);
disconnectButton.addEventListener('click', function () {
// Close the RTCDataChannels if they're open.
sendChannel.close();
receiveChannel.close();
// Close the RTCPeerConnections
localConnection.close();
sendChannel = null;
receiveChannel = null;
localConnection = null;
}, false);
sendButton.addEventListener('click', function() {
var message = messageInputBox.value;
sendChannel.send(message);
messageInputBox.value = "";
messageInputBox.focus();
}, false);
}, false);
</script>
</head>
<body>
<div class="controlbox">
<button id="disconnect-button">
Disconnect
</button>
</div>
<div class="messagebox">
<label for="message">Enter a message:
<input type="text" name="message" id="message" placeholder="Message text"
inputmode="latin" size=60 maxlength=120>
</label>
<button id="sendButton" name="sendButton" class="buttonright">
Send
</button>
</div>
<div class="messagebox" id="receivebox">
<p>Messages received:</p>
</div>
<h2>Initiate Connection</h2>
<button id="initiate-connection-button">
Initiate Connection
</button>
<h2>Get Answer</h2>
<textarea id="local-description-input"></textarea>
<button id="get-answer-button">Get Answer</button>
<h2>Complete Connection</h2>
<textarea id="remote-description-input"></textarea>
<button id="complete-connection-button">Submit</button>
</body>
</html>
Here is the about:webrtc error log that I get after I attempt to connect:
(generic/EMERG) Exit UDP socket connected
(generic/ERR) UDP socket error:Internal error at /builds/worker/checkouts/gecko/dom/network/UDPSocketParent.cpp:268 this=00000258866EA000
(ice/INFO) /builds/worker/checkouts/gecko/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c:173 function nr_socket_multi_tcp_create_stun_server_socket skipping UDP STUN server(addr:)
(ice/WARNING) /builds/worker/checkouts/gecko/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c:617 function nr_socket_multi_tcp_listen failed with error 3
(ice/WARNING) ICE(PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html)): failed to create passive TCP host candidate: 3
(ice/WARNING) ICE(PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html)): peer (PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html):default) has no stream matching stream PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html) transport-id=transport_0 - 18a9322f:167fe60f30b26ec3c9fd8ab65febec08
(ice/NOTICE) ICE(PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html)): peer (PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html):default) no streams with non-empty check lists
(ice/NOTICE) ICE(PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html)): peer (PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html):default) no streams with pre-answer requests
(ice/NOTICE) ICE(PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html)): peer (PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html):default) no checks to start
(ice/ERR) ICE(PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html)): peer (PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html):default) pairing local trickle ICE candidate host(IP4:173.17.64.254:58401/UDP)
(ice/ERR) ICE(PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html)): peer (PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html):default) pairing local trickle ICE candidate host(IP4:173.17.64.254:59370/TCP) active
(stun/INFO) STUN-CLIENT(srflx(IP4:173.17.64.254:58401/UDP|stun.l.google.com:19302)): Received response; processing
(ice/INFO) ICE(PC:1586652270128000 (id=2147483682 url=file:///C:/Users/root/Desktop/webrtc/index2.html)): All candidates initialized
+++++++ END ++++++++