I have some test code running that I'm using to try to learn the basics of WebRTC. This test code works on a LAN, but not over the internet, even if I use a TURN server (one side shows a status "checking" and the other "failed"). I can see that there are ice candidates in the SDPs, so I don't need to send them explicitly (right?).
This writes a lot of debug info to the console, so I can tell my signalling server is working. I'm stuck - what do I need to do differently in my code to enable it to work over the internet?
BTW, I have run other WebRTC demo scripts between my test computers, and they do work (like opentokrtc.ocom)
<html>
<head>
<title>test</title>
<script type="text/javascript">
var curInvite = null;
//create an invitation to connect and post to signalling server
function CreateInvite(){
//function to run upon receiving a response
var postRespFunc = function(txt){
console.log("Posted offer and received " + txt);
var invite = txt;
curInvite = invite;
document.getElementById("inviteId").innerHTML = invite;
//then poll for answer...
var pollFunc = function(){
GetRequest("answered?"+invite,function(txt){
if(txt){
//assume it's the answer
handleAnswer(txt);
}else{
//poll more
setTimeout(pollFunc,1000);
}
});
};
//start polling for answer
setTimeout(pollFunc,1000);
};
//function to run after creating the WebRTC offer
var postFunc = function(offer){
PostRequest('offer','offer='+encodeURIComponent(offer), postRespFunc);
}
//create the offer
createLocalOffer(postFunc);
}
function AnswerInvite(){
var invite = document.getElementById("invitation").value;
//can we create our local description BEFORE we get the remote desc?
//reduce to one ajax call?
GetRequest("accept?"+invite,function(txt){
var answerPostedCallback = function(txt){
console.log("answerPostedCallback",txt);
}
var answerCallback = function(answer){
PostRequest("answer?"+invite,'answer='+encodeURIComponent(answer), answerPostedCallback);
}
handleOffer(txt, answerCallback);
//then we're waiting for a data channel to be open...
});
}
function PostRequest(postUrl, reqStr, callback){
var req=new XMLHttpRequest();
req.onload = function(){
var strResp = req.responseText;
if(callback) callback(strResp);
}
//var namevalue=encodeURIComponent(document.getElementById("test").value);
//var parameters="name="+namevalue;
req.open("POST", postUrl, true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.send(reqStr);
}
function GetRequest(getUrl, callback){
var req=new XMLHttpRequest();
req.onload = function(){
var strResp = req.responseText;
if(callback) callback(strResp);
}
//var namevalue=encodeURIComponent(document.getElementById("test").value);
//var parameters="name="+namevalue;
req.open("GET", getUrl, true);
req.send();
}
/************ WebRTC stuff ****************/
var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection || window.msRTCPeerConnection;
var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription ||
window.webkitRTCSessionDescription || window.msRTCSessionDescription;
navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia ||
navigator.webkitGetUserMedia || navigator.msGetUserMedia;
//SEE http://olegh.ftp.sh/public-stun.txt
var cfg = {"iceServers":[
{url:'stun:stun.12connect.com:3478'},
{url:'stun:stun.12voip.com:3478'}
]};
cfg.iceServers = [{
url: 'turn:numb.viagenie.ca',
credential: 'muazkh',
username: 'webrtc#live.com'
}]
var con = { 'optional': [{'DtlsSrtpKeyAgreement': true}] };
var peerConnection = new RTCPeerConnection(cfg,con);
var dataChannel = null;
function initDataChannel(){
dataChannel.onerror = function (error) {
console.log("Data Channel Error:", error);
};
dataChannel.onmessage = function (event) {
console.log("Got Data Channel Message:", event.data);
var data = JSON.parse(event.data);
document.getElementById("chat").innerHTML+= "RECD: " + data + "<br />";
};
dataChannel.onopen = function () {
console.log('data channel open');
alert("data channel open, ready to connect!");
};
dataChannel.onclose = function () {
console.log("The Data Channel is Closed");
peerConnection.close();
alert("Disconnected!");
};
}
//used when peerConnection is an answerer
peerConnection.ondatachannel = function (e) {
dataChannel = e.channel || e; // Chrome sends event, FF sends raw channel
initDataChannel();
console.log("Received datachannel", arguments);
}
//to initiate a connection
function createLocalOffer(callback) {
//create datachannel
try {
dataChannel = peerConnection.createDataChannel('test', {reliable:true});
initDataChannel();
console.log("Created datachannel (peerConnection)");
} catch (e) { console.warn("No data channel (peerConnection)", e); }
//set event handler
peerConnection.onicecandidate = function (e) {
console.log("ICE candidate (peerConnection)", e);
if (e.candidate == null) {
console.log("ice candidate",peerConnection.localDescription);
callback(JSON.stringify(peerConnection.localDescription));
}
};
peerConnection.createOffer(function (desc) {
peerConnection.setLocalDescription(desc);
console.log("created local offer", desc);
}, function () {console.warn("Couldn't create offer");});
}
peerConnection.onconnection = function(e){
console.log("peerConnection connected",e);
};
function onsignalingstatechange(state) {
console.info('signaling state change:', state);
}
function oniceconnectionstatechange(state) {
console.info('ice connection state change:', state);
console.info('iceConnectionState: ', peerConnection.iceConnectionState);
}
function onicegatheringstatechange(state) {
console.info('ice gathering state change:', state);
}
peerConnection.onsignalingstatechange = onsignalingstatechange;
peerConnection.oniceconnectionstatechange = oniceconnectionstatechange;
peerConnection.onicegatheringstatechange = onicegatheringstatechange;
//local handles answer from remote
function handleAnswer(answerJson) {
var obj = JSON.parse(answerJson);
var answerDesc = new RTCSessionDescription(obj);
peerConnection.setRemoteDescription(answerDesc);
}
/* functions for remote side */
//handle offer from the initiator
function handleOffer(offerJson, callback) {
var obj = JSON.parse(offerJson);
var offerDesc = new RTCSessionDescription(obj);
peerConnection.setRemoteDescription(offerDesc);
//set event handler
peerConnection.onicecandidate = function (e) {
console.log("ICE candidate (peerConnection)", e);
if (e.candidate == null) {
console.log("ice candidate",peerConnection.localDescription);
}
};
peerConnection.createAnswer(function (answerDesc) {
console.log("Created local answer: ", answerDesc);
peerConnection.setLocalDescription(answerDesc);
callback(JSON.stringify(answerDesc));
}, function () { console.warn("No create answer"); });
}
function sendMessage() {
var msg = document.getElementById("msg").value;
document.getElementById("msg").value = null;
document.getElementById("chat").innerHTML+= "SENT: " + msg + "<br />";
var obj = {message: msg};
dataChannel.send(JSON.stringify(msg));
return false;
};
</script>
</script>
</head>
<body>
<p>test</p>
<p>
<div id="createWrapper">
<h4>create an invitiation</h4>
<button type="button" onclick="CreateInvite();">create invitation</button>
<h3 id="inviteId"></h3>
</div>
<div id="acceptWrapper">
<h4>or accept an inviation</h4>
<input id="invitation" type="text" name="invitation" />
<button type="button" onclick="AnswerInvite()">answer invitation</button>
</div>
<p>Once the data channel is open type your messages below</p>
<input type="text" id="msg" /><button type="button" onclick="sendMessage()">send</button>
<div id="chat"></div>
</body>
</html>
[EDIT: HERE IS WORKING CODE, IN CASE IT'S USEFUL TO ANYONE ELSE. You will still need your own signalling server and working STUN/TURN server(s), but this was helpful to me to understand the basics]
<html>
<head>
<title>test</title>
<script type="text/javascript">
var curInvite = null;
//create an invitation to connect and post to signalling server
function CreateInvite(){
//function to run upon receiving a response
var postRespFunc = function(txt){
console.log("Posted offer and received " + txt);
var invite = txt;
curInvite = invite;
document.getElementById("inviteId").innerHTML = invite;
//then poll for answer...
var pollFunc = function(){
GetRequest("answered?"+invite,function(txt){
if(txt){
//assume it's the answer
handleAnswer(txt);
}else{
//poll more
setTimeout(pollFunc,1000);
}
});
};
//start polling for answer
setTimeout(pollFunc,100);
};
//function to run after creating the WebRTC offer
var postFunc = function(offer){
PostRequest('offer','offer='+encodeURIComponent(offer), postRespFunc);
}
//create the offer
createLocalOffer(postFunc);
}
function AnswerInvite(){
var invite = document.getElementById("invitation").value;
//can we create our local description BEFORE we get the remote desc?
//reduce to one ajax call?
GetRequest("accept?"+invite,function(txt){
var answerPostedCallback = function(txt){
console.log("answerPostedCallback",txt);
}
var answerCallback = function(answer){
PostRequest("answer?"+invite,'answer='+encodeURIComponent(answer), answerPostedCallback);
}
handleOffer(txt, answerCallback);
//then we're waiting for a data channel to be open...
});
}
function PostRequest(postUrl, reqStr, callback){
var req=new XMLHttpRequest();
req.onload = function(){
var strResp = req.responseText;
if(callback) callback(strResp);
}
//var namevalue=encodeURIComponent(document.getElementById("test").value);
//var parameters="name="+namevalue;
req.open("POST", postUrl, true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.send(reqStr);
}
function GetRequest(getUrl, callback){
var req=new XMLHttpRequest();
req.onload = function(){
var strResp = req.responseText;
if(callback) callback(strResp);
}
//var namevalue=encodeURIComponent(document.getElementById("test").value);
//var parameters="name="+namevalue;
req.open("GET", getUrl, true);
req.send();
}
/************ WebRTC stuff ****************/
var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection || window.msRTCPeerConnection;
var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription ||
window.webkitRTCSessionDescription || window.msRTCSessionDescription;
navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia ||
navigator.webkitGetUserMedia || navigator.msGetUserMedia;
//SEE http://olegh.ftp.sh/public-stun.txt
var cfg = {"iceServers":[
{url: 'turn:numb.viagenie.ca',
credential: 'muazkh',
username: 'webrtc#live.com'
}
]};
var con = { 'optional': [{'DtlsSrtpKeyAgreement': true}] };
var peerConnection = null;
function createPeer(){
peerConnection = new RTCPeerConnection(cfg,con);
//used when peerConnection is an answerer
peerConnection.ondatachannel = function (e) {
dataChannel = e.channel || e; // Chrome sends event, FF sends raw channel
initDataChannel();
console.log("Received datachannel", arguments);
}
peerConnection.onsignalingstatechange = onsignalingstatechange;
peerConnection.oniceconnectionstatechange = oniceconnectionstatechange;
peerConnection.onicegatheringstatechange = onicegatheringstatechange;
peerConnection.onconnection = function(e){
console.log("peerConnection connected",e);
};
}
var dataChannel = null;
function initDataChannel(){
dataChannel.onerror = function (error) {
console.log("Data Channel Error:", error);
};
dataChannel.onmessage = function (event) {
console.log("Got Data Channel Message:", event.data);
var data = JSON.parse(event.data);
document.getElementById("chat").innerHTML+= "RECD: " + data + "<br />";
};
dataChannel.onopen = function () {
console.log('data channel open');
alert("data channel open, ready to connect!");
};
dataChannel.onclose = function () {
console.log("The Data Channel is Closed");
peerConnection.close();
alert("Disconnected!");
};
}
//to initiate a connection
function createLocalOffer(callback) {
createPeer();
//create datachannel
try {
dataChannel = peerConnection.createDataChannel('test', {reliable:true});
initDataChannel();
console.log("Created datachannel (peerConnection)");
} catch (e) { console.warn("No data channel (peerConnection)", e); }
//set event handler
peerConnection.onicecandidate = function (e) {
console.log("ICE candidate (peerConnection)", e);
if (e.candidate == null) {
console.log("ice candidate",peerConnection.localDescription);
callback(JSON.stringify(peerConnection.localDescription));
}
};
peerConnection.createOffer(function (desc) {
peerConnection.setLocalDescription(desc);
console.log("created local offer", desc);
}, function () {console.warn("Couldn't create offer");});
}
function onsignalingstatechange(state) {
console.info('signaling state change:', state);
}
function oniceconnectionstatechange(state) {
console.info('ice connection state change:', state);
console.info('iceConnectionState: ', peerConnection.iceConnectionState);
}
function onicegatheringstatechange(state) {
console.info('ice gathering state change:', state);
}
//local handles answer from remote
function handleAnswer(answerJson) {
var obj = JSON.parse(answerJson);
var answerDesc = new RTCSessionDescription(obj);
peerConnection.setRemoteDescription(answerDesc);
}
/* functions for remote side */
//handle offer from the initiator
function handleOffer(offerJson, callback) {
createPeer();
var obj = JSON.parse(offerJson);
var offerDesc = new RTCSessionDescription(obj);
//set event handler
peerConnection.onicecandidate = function (e) {
console.log("ICE candidate (peerConnection)", e);
if (e.candidate == null) {
console.log("ice candidate",peerConnection.localDescription);
callback(JSON.stringify(peerConnection.localDescription));
}
};
peerConnection.setRemoteDescription(offerDesc);
peerConnection.createAnswer(function (answerDesc) {
console.log("Created local answer: ", answerDesc);
peerConnection.setLocalDescription(answerDesc);
}, function () { console.warn("No create answer"); });
}
function sendMessage() {
var msg = document.getElementById("msg").value;
document.getElementById("msg").value = null;
document.getElementById("chat").innerHTML+= "SENT: " + msg + "<br />";
var obj = {message: msg};
dataChannel.send(JSON.stringify(msg));
return false;
};
</script>
</script>
</head>
<body>
<p>test</p>
<p>
<div id="createWrapper">
<h4>create an invitiation</h4>
<button type="button" onclick="CreateInvite();">create invitation</button>
<h3 id="inviteId"></h3>
</div>
<div id="acceptWrapper">
<h4>or accept an inviation</h4>
<input id="invitation" type="text" name="invitation" />
<button type="button" onclick="AnswerInvite()">answer invitation</button>
</div>
<p>Once the data channel is open type your messages below</p>
<input type="text" id="msg" /><button type="button" onclick="sendMessage()">send</button>
<div id="chat"></div>
</body>
</html>
The SDP will only contain the candidates that have been gathered up to that point in time, so unless you wait for the null candidate in the pc.onicecandidate callback, you wont get all candidates this way (you seem to be waiting in your createLocalOffer, but not in your handleOffer, I think is the problem here).
That said, I don't recommend this approach since it can take up to 20 seconds for the ICE agent to exhaust all paths (happens a lot on systems with VPN for instance). Instead, I highly recommend sending candidates explicitly to the other side. i.e. Trickle ICE.
If you don't see any candidate like relay or server-reflexive then you can try first to capture a tcpdump using wireshark or so to see if there is any outgoing packet to your STUN/TURN server, if no outgoing packet by filtering as STUN then your configuration may not working. If you see out going STUN/TURN traffic then whatever the error is you might get some response from server which may have authentication error or some other error but based on the response type you can determine the issue with STUN/TRUN. If you see success response from STUN/TURN then you can see if you are including the candidates correctly in SDP or your signalling offer/answer message to advertise the candidates to other end.
I have this code.I send messages to server but i don't receive them.I have code from a post where the user say is working.I have a xmpp server where i can connect with strophe.
<html>
<head>
<script type="text/javascript" src="angular/angular.min.js"></script>
<script type="text/javascript" src="strophe.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="init">
</div>
<script type="text/javascript">
BOSH_SERVICE = 'http://localhost:5280/http-bind/';
xmpp_user = "user";
xmpp_domain = "localhost";
xmpp_userdomain = "user#localhost";
xmpp_password = "secret";
angular.
module('myApp', []).
controller('init', function(xmppAuth){
xmppAuth.auth(xmpp_userdomain,xmpp_password);
on_presence = function (presence){
console.log('presence');
return true;
}
on_message = function (message){
//console.log('message');
console.log(message);
return true;
}
}).
service('xmppAuth', function() {
return {
auth: function(login, password) {
connect = new Strophe.Connection(BOSH_SERVICE);
connect.connect(login, password, function (status) {
if (status === Strophe.Status.CONNECTED) {
console.log("Autentificare reusita!");
//try send helo
var message = "helo";
var to = "marian#localhost";
if(message && to){
var reply = $msg({
to: to,
type: 'chat'
})
.cnode(Strophe.xmlElement('body', message)).up()
.c('active', {xmlns: "http://jabber.org/protocol/chatstates"});
connect.send(reply);
console.log('I sent ' + to + ': ' + message);
}
//addhandler receive messg
connect.addHandler(onMessage, null, "message", null, null, null);
var onMessage = function (message){
console.log('S-a primit un mesaj');
console.log('message');
return true;
}
}
})
}
}
})
</script>
</body>
</html>
What i should do?Thanks for any help!
I had a similar problem and I noticed that setting the handler before sending any message would allow you to read the messages, even those you send.
Below is a working code which I have tested
...
server_connection.connect(user_id, password, function(status){
if (status == Strophe.Status.CONNECTED){
on_connected();
}
});
function on_connected(){ server_connection.addHandler(on_message, null, 'message');}
var on_message = function(message){ /* work with message here */ }
You won't receive messages which you send, only incoming messages, or use MUC plugin for history.
I'm trying to implement a websocket server in node.js without using any framework.
Sending messages from client to server is working fine. But now I tried to send a text file from client to server. I can see the content on the server side by using console.log in the terminal.
But:
how can i get the file information ? (name, created/edited date, etc. ?)
how can i save the file ?
Client code:
(function () {
'use strict';
var output, ws;
//Display logging information in the document
function log(s) {
var p = document.createElement("p");
p.style.wordWrap = "break-word";
p.textContent = s;
output.appendChild(p);
//Also log information on the javascript console
window.console.log(s);
}
//Send a message on the Websocket
function sendMessage(msg) {
console.log(ws.binaryType);
ws.send(msg);
console.log("Message sent");
}
//Initialize WebSocket connection and event handlers
function setup() {
output = document.getElementById("output");
ws = new window.WebSocket("ws://localhost:9999/");
//Listen for the connection open event then call the sendMessage function
ws.onopen = function () {
console.log("Connected");
document.getElementById('fl').onchange = function() {
sendMessage(document.querySelector('input[type="file"]').files[0]);
};
sendMessage("Hello Galileo!");
}
//Listen for the close connection event
ws.onclose = function (e) {
if(this.readyState == 2){
console.log('Connection is closing (Closing Handshake).')
}
else if(this.readyState == 3){
console.log('Connection closed. Has been closed or could not be opened.')
}
else{
console.log('Unhandled ReadyState: ',this.readyState);
}
console.log("Disconnected: " +
' reason:' + e.reason +
' was clean:' + e.wasClean +
' code:' + e.code);
}
//Listen for connection errors
ws.onerror = function (e) {
console.log("Error: " + e);
}
//Listen for new messages arriving at the client
ws.onmessage = function (e) {
console.log("Message received: " + e.data);
//Close the socket once one message has arrived
ws.close();
}
}
//Start running the example
setup();
})();
HTML Code
<!DOCTYPE html>
<html>
<head>
<title>Websocket Echo Client</title>
<meta charset="utf-8"/>
</head>
<body>
<h2>Websocket Echo Client</h2>
<div id="output"></div>
<input type="file" id="fl"/>
<script src="websocket.js"></script>
</body>
</html>
Server code
switch (opcode) {
case opcodes.TEXT:
this.payload = payload.toString("utf8");
winston.log('info','Text:\r\n', this.payload);
break;
case opcodes.BINARY:
console.log('info','File:\r\n', payload.toString("utf8"));
As far as I know the payload you're receiving on the server side does not contain the meta data about the file. I believe the File object is treated as a normal Blob with some extra meta data and the ws.send is only handling it like a Blob (it has no special handling for File).
The meta data can be accessed using
document.querySelector('input[type="file"]').files[0].name
document.querySelector('input[type="file"]').files[0].size
document.querySelector('input[type="file"]').files[0].type
And then send separately.
First I built a websocket server using node js and ws module. Then using chrome and firefox, I connect to that server and the connection is successfully established. However, the message I send from browsers does not arrive at the server. I have some code on server to console.log out if message is received. Nothing appears, however when I refresh the browser, the messages I previously sent arrive. The messages did not arrive when sent them but only once I refresh the page. I don't know why. This seems to work in from some other computers but not mine.
Here is the server code:
var WebSocketServer = require('ws').Server
, http = require('http')
, express = require('express')
, app = express();
app.use(express.static(__dirname + '/views'));
var rmi = require('./RMIClient.js');
console.log(rmi);
var server = http.createServer(app);
server.listen(8080);
var wss = new WebSocketServer({server: server});
// from here is the logic codes
var clients = [];
var clientId = 0;
wss.on('connection', function(ws) {
console.log("connection established for client "+ (clients.length+1));
clients.push(ws);
console.log("index is " + clients.indexOf(ws));
clientId += 1;
ws.send("Hello Client: " + clientId);
//
// ws.send("Welcome from AMTT Chatting Server");
ws.on('message',function(data){
console.log('message receieved : '+data);
for(var i = 0;i<clients.length;i++){
clients[i].send(data);
}
});
ws.on('a',function(){
console.log("a event fire from client");
});
ws.on('close', function() {
var index = clients.indexOf(ws);
console.log('stopping client interval '+index);
if (index > -1) {
clients.splice(index, 1);
}
});
});
Here is the client code:
<html>
<script>
//var ws = new WebSocket('ws://localhost:8080/');
var messagearea,inputarea,sendButton;
var connection = new WebSocket(/*'wss://echo.websocket.org');*/'ws://192.168.8.195:8080/');
// When the connection is open, send some data to the server
console.log(connection.readyState);
connection.onopen = function () {
console.log(connection.readyState);
inputarea.disabled = false;
sendButton.disabled = false;
};
// Log errors
connection.onerror = function (error) {
console.log('sorry connection fail:' + JSON.stringify(error));
};
// Log messages from the server
connection.onmessage = function (e) {
messagearea.value = messagearea.value + '\n' + e.data;
console.log('Server: ' + e.data);
};
function sendMessage(){
if(inputarea.value !='')
connection.send(inputarea.value);
inputarea.value = '';
}
</script>
<body>
<textarea rows="15" cols="100" id="messagearea" disabled>
</textarea>
<br/>
<textarea rows="2" cols="90" id="inputarea" required autofocus>
</textarea>
<input type = 'button' value = 'send' id = 'sendbutton' onclick = "sendMessage()"/>
</body>
<script>
messagearea = document.getElementById('messagearea');
messagearea.value = '';
inputarea = document.getElementById('inputarea');
inputarea.value = '';
inputarea.disabled = true;
sendButton = document.getElementById('sendbutton');
sendButton.disabled = true;
</script>
</html>
And again I found that kind of situation when I develop that code in java and deployed in wildfly server. I am lost. I think there is something concerned with my network card. Because that same code work perfectly in my friend's machine.
Does anybody experience this situation ? or any solution?
You can also try the following:
connection.addEventListener("message", function (e) {
processSocketMessage(e);
});
good luck :)
this my code from books "The Definitive Guide to HTML5 websocket".
....
<div id="output"></div>
<script>
function setup() {
output = document.getElementById("output");
ws = new WebSocket("ws://localhost:7777");
ws.onopen = function(e) {
log("Connected");
sendMessage("Hello Websocket!");
}
ws.onclose = function(e){
log("Disconnected: " + e.reason);
}
ws.onerror = function(e){
log("Error ");
}
ws.onmessage = function(e) {
log("Message received: " + e.data);
ws.close();
}
}
function sendMessage(msg){
ws.send(msg);
log("Message Sent");
}
function log(s){
var p = document.createElement("p");
p.style.wordWrap = "break-word";
p.textContent = s;
output.appendChild(p);
console.log(s);
}
setup();
</script>
but, when i'm running it in localhost.. the output just like this
Connected
Message Sent
and stop until that. i knew event onmessage is not firing, but i dont know why. what could possibly be the problem? thanks
onmessage will only fire when the server sends a message to the client, not when the client is sending a message to the server (which is what you're doing).
If your server is sending back a message and that isn't being picked up by your client, you're going to need to provide a bit more context (server implementation, etc).