Webrtc - Cannot read property 'addIceCandidate' of undefined - javascript

I am getting the error from this page. The error is client.js:166 Uncaught TypeError: Cannot read property 'addIceCandidate' of undefined. Below is the Code. How to remove that error? The video from the other browser is sending stream to the server, while adding the stream in both browser, there becomes the error. Where is the error occurs after it got stream.
var divSelectRoom = document.getElementById("selectRoom");
var divConsultingRoom = document.getElementById("consultingRoom");
var inputRoomNumber = document.getElementById("roomNumber");
var btnGoRoom = document.getElementById("goRoom");
var localVideo = document.getElementById("localVideo");
var remoteVideo = document.getElementById("remoteVideo");
// these are the global variables
var roomNumber;
var localStream;
var remotestream;
var rtcPeerConnection;
//these are the STUN servers
var iceServers = {
'iceServers': [
{
url:'stun:stun.l.google.com:19302'
},
{
url:'stun:stun.services.mozilla.com'
},
{
url: 'turn:numb.viagenie.ca',
credential: 'muazkh',
username: 'webrtc#live.com'
}
]
};
var streamConstraints = { audio: true, video: true };
var isCaller;
// Here we connect to the socket iO server. We Will create it later.
var socket = io();
// Here we Odd a click event to the button
btnGoRoom.onclick = function() {
if (inputRoomNumber.value == ""){
alert("Please type a room number");
}
else {
roomNumber = inputRoomNumber.value; //we take the value from the element
socket.emit('create or join', roomNumber); //we send a message to server
divSelectRoom.style = "display: none;"; //hide selectRoom div
divConsultingRoom.style = "display block;"; //show consultingRoom div
}
};
// when server emits created
socket.on("created", function(room){
console.log('created function');
//caller gets user media devices with defined constraints
navigator.mediaDevices.getUserMedia(streamConstraints).then(function(stream){
console.log('Created function');
const mediaStream = new MediaStream();
const video = document.getElementById('localVideo');
video.srcObject = stream;
localStream = stream; //sets local stream to variable
//localVideo.src = URL.createObjectURL(stream); //shows stream to user
isCaller = true;//sets current user as caller
}).catch(function(err){
console.log('An error occured when accessing media devices');
console.log(err.name + ": " + err.message);
});
});
// when server emits ends
socket.on("joined", function(room){
console.log('Joined function');
//caller gets user media devices with defined constraints
navigator.mediaDevices.getUserMedia(streamConstraints).then(function(stream){
localStream = stream; //sets local stream to variable
const mediaStream = new MediaStream();
const video = document.getElementById('localVideo');
video.srcObject = stream;
//localVideo.src = URL.createObjectURL(stream); //shows stream to user
socket.emit('ready',roomNumber); //sends message to the server
console.log('Joined function');
}).catch(function(err){
console.log('An error occured when accessing media devices');
console.log(err.name + ": " + err.message);
});
});
//when server emits ready
socket.on('ready', function(){
console.log('client ready function');
if(isCaller){
//creates an RTCPeerConnection object
rtcPeerConnection = new RTCPeerConnection(iceServers);
//adds event listeners to the newly created object
rtcPeerConnection.onicecandidate = onIceCandidate;
rtcPeerConnection.ontrack = onAddStream;
//add the current local stream to the object
rtcPeerConnection.addStream(localStream);
//prepares an offer
rtcPeerConnection.createOffer(setLocalAndOffer, function(e){
console.log(e);
});
}
});
//when server emits offer
socket.on('offer',function(event){
if(isCaller){
console.log('client offer function');
//creates an RTCPeerConnection object
rtcPeerConnection = new RTCPeerConnection(iceServers);
//adds event listeners to the newly created object
rtcPeerConnection.onicecandidate = onIceCandidate;
rtcPeerConnection.ontrack = onAddStream;
//adds the current local stream to the object
rtcPeerConnection.addStream(localStream);
//stores the offer as remote description
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event));
//Prepares an Answer
rtcPeerConnection.createAnswer(setLocalAndAnswer, function(e){
console.log(e);
});
}
});
//when server emits answer
socket.on('answer', function(event){
console.log('client answer function');
//stores it as remote description
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event));
});
//when server emits candidate
socket.on('candidate', function(event){
console.log('client candidate function');
var pc1 = {
addIceCandidate : function(val) {
console.log(val);
}
}
//creates a candidate object
var candidate1 = new RTCIceCandidate({
type: 'offer',
sdpMLineIndex: event.label,
candidate: event.candidate
});
addIceCandidate(candidate1);
// if(rtcPeerConnection)
// console.log('Okay Peer');
// //stores candidate
// rtcPeerConnection.addIceCandidate(candidate);
});
function addIceCandidate(message) {
if (message.candidate != null) {
rtcPeerConnection.addIceCandidate(message);
}
}
//when a user receives the other user's video and audio stream
function onAddStream(event){
console.log('On Add Stream function');
const mediaStream = new MediaStream();
const rvideo = document.getElementById('remoteVideo');
rvideo.srcObject = event.stream;
//remoteVideo.src = URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
//These are the functions referenced before as listeners for the peer connection
//sends a candidate message to server
function onIceCandidate(event){
console.log('On Ice candidate function');
if(event.candidate){
console.log('sending ice candidate');
socket.emit('candidate', {
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
room: roomNumber
});
}
}
//stores offer and sends message to server
function setLocalAndOffer(sessionDescription){
console.log('LocalAndOffer function');
rtcPeerConnection.setLocalDescription(sessionDescription);
socket.emit('offer', {
type: 'offer',
sdp: sessionDescription,
room: roomNumber
});
}
//stores answer and sends message to server
function setLocalAndAnswer(sessionDescription){
console.log('LocalAndAnswer function');
rtcPeerConnection.setLocalDescription(sessionDescription);
socket.emit('answer', {
type: 'answer',
sdp: sessionDescription,
room: roomNumber
});
}

You are probably sending the candidate socket message before the rtcPeerConnection is initialized. Then you get the error in the addIceCandidate function.

You need to check if you have a webrtc peer connection object before you call addIceCandidate when candidate web socket message arrives.
Once you init it and add event andlers it can any moment find ice candidates and hence trigger the related event, onicecandidate. Same for the session descriptions and its related event onnegotiationneeded event.
So be ready on other end you send those messages over websocket to upon they trigger on one end.

Related

Remote Video stream not showing up at all

I have been trying to get the remote video stream to show up using .ontrack which is under the peer connection function in my code. Until now the .ontrack only fires on the caller side while on the callee it does not even when the function is called.
The log that checks if .ontrack fires would state "Got Remote Stream" but only on the caller side which might be the problem here but I'm not sure why would the other party not go into the IF statement that holds the .ontrack when it does not have the event.stream[0] which the statement is checking for.
I have added the console logs from both Caller and Callee below. Not shown in the images is that after a while the candidates would show null but both users are still connected.
main.js
'use strict';
var isInitiator;
var configuration = {
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
};
var pc = new RTCPeerConnection(configuration);
// Define action buttons.
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');
/////////////////////////////////////////////
window.room = prompt('Enter room name:');
var socket = io.connect();
if (room !== '') {
console.log('Message from client: Asking to join room ' + room);
socket.emit('create or join', room);
}
socket.on('created', function(room) {
console.log('Created room ' + room);
isInitiator = true;
startVideo();
});
socket.on('full', function(room) {
console.log('Message from client: Room ' + room + ' is full :^(');
});
socket.on('joined', function(room) {
console.log('joined: ' + room);
startVideo();
callButton.disabled = true;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
////////////////////////////////////////////////
async function sendMessage(message) {
console.log('Client sending message: ', message);
await socket.emit('message', message);
}
// This client receives a message
socket.on('message', async function(message) {
try {
if (message.type === 'offer') {
await pc.setRemoteDescription(new RTCSessionDescription(message));
await pc
.setLocalDescription(await pc.createAnswer())
.then(function() {
sendMessage(pc.localDescription);
})
.catch(function(err) {
console.log(err.name + ': ' + err.message);
});
createPeerConnection();
} else if (message.type === 'answer') {
await pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate') {
await pc.addIceCandidate(candidate);
}
} catch (err) {
console.error(err);
}
});
////////////////////////////////////////////////////
const localVideo = document.querySelector('#localVideo');
const remoteVideo = document.querySelector('#remoteVideo');
// Set up initial action buttons status: disable call and hangup.
callButton.disabled = true;
hangupButton.disabled = true;
// Add click event handlers for buttons.
callButton.addEventListener('click', callStart);
hangupButton.addEventListener('click', hangupCall);
function startVideo() {
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true
})
.then(function(stream) {
localVideo.srcObject = stream;
stream.getTracks().forEach(track => pc.addTrack(track, stream));
})
.catch(function(err) {
console.log('getUserMedia() error: ' + err.name);
});
callButton.disabled = false;
}
async function callStart() {
createPeerConnection();
callButton.disabled = true;
hangupButton.disabled = false;
if (isInitiator) {
console.log('Sending offer to peer');
await pc
.setLocalDescription(await pc.createOffer())
.then(function() {
sendMessage(pc.localDescription);
})
.catch(function(err) {
console.log(err.name + ': ' + err.message);
});
}
}
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc.ontrack = event => {
if (remoteVideo.srcObject !== event.streams[0]) {
remoteVideo.srcObject = event.streams[0];
console.log('Got remote stream');
}
};
pc.onicecandidate = ({ candidate }) => sendMessage({ candidate });
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function hangupCall() {
pc.close();
pc = null;
callButton.disabled = false;
hangupButton.disabled = true;
console.log('Call Ended');
}
index.js
'use strict';
var express = require('express');
var app = (module.exports.app = express());
var path = require('path');
var server = require('http').createServer(app);
var io = require('socket.io')(server);
const PORT_NO = process.env.APP_PORT || 3000;
server.listen(PORT_NO);
app.get('/', function(request, response) {
response.sendFile(path.resolve('./index.html'));
});
app.use(express.static('.'));
io.on('connection', socket => {
function log() {
const array = ['Message from server:'];
for (let i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}
socket.on('message', message => {
log('Got message:', message);
socket.broadcast.emit('message', message);
});
socket.on('create or join', room => {
var clientsInRoom = io.sockets.adapter.rooms[room];
var numClients = clientsInRoom
? Object.keys(clientsInRoom.sockets).length
: 0;
// max two clients
if (numClients === 2) {
socket.emit('full', room);
return;
}
log('Room ' + room + ' now has ' + (numClients + 1) + ' client(s)');
if (numClients === 0) {
socket.join(room);
log('Client ID ' + socket.id + ' created room ' + room);
socket.emit('created', room, socket.id);
} else {
log('Client ID ' + socket.id + ' joined room ' + room);
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
}
});
});
Let the joiner be the initiator.
I'm guessing 'created' happens before 'joined'? I.e. one party creates the room before the second party joins it?
Since your startVideo() does more than start the local video—it actually begins connection negotiation—I suspect you begin negotiating before the second party is ready, a race. Instead try:
socket.on('created', function(room) {
console.log('Created room ' + room);
startVideo();
});
socket.on('joined', function(room) {
console.log('joined: ' + room);
isInitiator = true; // <-- begin negotiating once 2nd party arrives.
startVideo();
});
You're missing a call to createPeerConnection() on the answerer side, which means the answerer isn't properly set up to signal ICE candidates or fire the track event.
You only call it from startCall(), so this would only work if you hit the call button on both ends at almost exactly the same time.
createPeerConnection() is a misnomer. Instead, just initialize the pc with its ontrack and onicecandidate callbacks on page load.
Still not working?
The rest of your WebRTC-related code you're showing us looks fine—except you're calling getUserMedia twice on the answerer side, which is redundant, but shouldn't be a problem.
I suspect a bug in your server logic. E.g. you're not showing us how emitting 'create or join' turns into either a 'created' or 'joined' socket message. You're also trying to predetermine which side is which in the offer/answer exchange, which is fine, except this means you have a non-working Call button on the answerer side. Most demos just let whoever pushes the button first be the offerer, though that might create glare. Just FYI.
This is a two-way call. In which direction is remoteVideo not working?
Also, you have a two-way call here, sending video in both directions, yet you've not mentioned which remoteVideo you're not seeing.
For a working example, check out my two-way tab demo. Open it in two adjacent windows in the same browser, and click the Call button in one of them to connect. You should see (the same) video being sent both ways. It relies on a localSocket hack using localStorage.

peer.js webrtc >> changing stream in runtime

I am developing a cross-plattform application with peer.js and webrtc.
I am using cordova, crosswalk.
Additionaly I am using the webrtc adapter (https://github.com/webrtc/adapter)
My code is based on the webrtc-crosswalk sample. (https://github.com/crosswalk-project/crosswalk-samples)
I want to change the videosource of the stream without creating a new call.
My approche is to remove the tracks of the stream and add the new tracks of the other camera.
The result is that the local video shows the right content, but the callee's remote video freezes.
Probably I am doing a very basic mistake, but i can't find a solution.
I am looking forward to your answers and solutions.
My main codefile is attached.
//Notwendig, um die Dialogfunktion zu aktivieren
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(navigator.notification);
// Now safe to use device APIs
}
document.addEventListener('DOMContentLoaded', function () {
// PeerJS server location
var SERVER_IP = '172.20.37.147';
var SERVER_PORT = 9000;
// DOM elements manipulated as user interacts with the app
var messageBox = document.querySelector('#messages');
var callerIdEntry = document.querySelector('#caller-id');
var connectBtn = document.querySelector('#connect');
var recipientIdEntry = document.querySelector('#recipient-id');
var dialBtn = document.querySelector('#dial');
var remoteVideo = document.querySelector('#remote-video');
var localVideo = document.querySelector('#local-video');
var cameraTurn = document.querySelector('#camera_turn');
var stop = document.querySelector('#stop');
// the default facing direction
var dir = "environment";
// the ID set for this client
var callerId = null;
// PeerJS object, instantiated when this client connects with its
// caller ID
var peer = null;
// the local video stream captured with getUserMedia()
var localStream = null;
// DOM utilities
var makePara = function (text) {
var p = document.createElement('p');
p.innerText = text;
return p;
};
var addMessage = function (para) {
if (messageBox.firstChild) {
messageBox.insertBefore(para, messageBox.firstChild);
}
else {
messageBox.appendChild(para);
}
};
var logError = function (text) {
var p = makePara('ERROR: ' + text);
p.style.color = 'red';
addMessage(p);
};
var logMessage = function (text) {
addMessage(makePara(text));
};
// get the local video and audio stream and show preview in the
// "LOCAL" video element
// successCb: has the signature successCb(stream); receives
// the local video stream as an argument
var getLocalStream = function (successCb, ask = true) {
if (localStream && successCb) {
successCb(localStream);
}
else {
navigator.mediaDevices.getUserMedia({ audio: true, video: { facingMode: dir } })
.then(function (stream) {
if (localStream == null) {
/* use the stream */
localStream = stream;
}
else {
stream.getTracks().forEach(function (track) {
localStream.addTrack(track);
});
}
localVideo.src = window.URL.createObjectURL(localStream);
if (successCb) {
successCb(stream);
}
})
.catch(function (err) {
/* handle the error */
logError('failed to access local camera');
logError(err.message);
});
}
};
// set the "REMOTE" video element source
var showRemoteStream = function (stream) {
remoteVideo.src = window.URL.createObjectURL(stream);
};
// set caller ID and connect to the PeerJS server
var connect = function () {
callerId = callerIdEntry.value;
if (!callerId) {
logError('please set caller ID first');
return;
}
try {
// create connection to the ID server
peer = new Peer(callerId, { host: SERVER_IP, port: SERVER_PORT });
// hack to get around the fact that if a server connection cannot
// be established, the peer and its socket property both still have
// open === true; instead, listen to the wrapped WebSocket
// and show an error if its readyState becomes CLOSED
peer.socket._socket.onclose = function () {
logError('no connection to server');
peer = null;
};
// get local stream ready for incoming calls once the wrapped
// WebSocket is open
peer.socket._socket.onopen = function () {
getLocalStream();
};
// handle events representing incoming calls
peer.on('call', answer);
}
catch (e) {
peer = null;
logError('error while connecting to server');
}
};
// make an outgoing call
var dial = function () {
if (!peer) {
logError('please connect first');
return;
}
if (!localStream) {
logError('could not start call as there is no local camera');
return
}
var recipientId = recipientIdEntry.value;
if (!recipientId) {
logError('could not start call as no recipient ID is set');
return;
}
getLocalStream(function (stream) {
logMessage('outgoing call initiated');
var call = peer.call(recipientId, stream);
call.on('stream', showRemoteStream);
call.on('error', function (e) {
logError('error with call');
logError(e.message);
});
});
};
// answer an incoming call
var answer = function (call) {
if (!peer) {
logError('cannot answer a call without a connection');
return;
}
if (!localStream) {
logError('could not answer call as there is no localStream ready');
return;
}
//Asks user to answer the call
navigator.notification.confirm(
"Receive a call?",
function (buttonIndex) {
if (buttonIndex === 1) {
//user clicked "yes"
logMessage('incoming call answered');
call.on('stream', showRemoteStream);
call.answer(localStream);
}
else {
//user clicked "no"
logMessage('incoming call denied');
}
}
,
'Incoming Call',
['Yes', 'No']
);
};
function turnDirection() {
if (dir === "user")
return "environment";
else
return "user";
}
var turnCamera = function (call) {
dir = turnDirection();
localStream.getTracks().forEach(function (track) {
track.stop();
localStream.removeTrack(track);
});
getLocalStream(false);
};
var stopCall = function (call) { };
// wire up button events
connectBtn.addEventListener('click', connect);
dialBtn.addEventListener('click', dial);
cameraTurn.addEventListener('click', turnCamera);
stop.addEventListener('click', stopCall);
});
If you remove and then add a new track to a PeerConnection you need to renegotiate the offer-answer to get it working. I will recommend you to use the replaceTrack API to avoid the re-negotiation problem while changing the camera input.

Websockets: send messages and notifications to all clients except sender

I am developing chat based on websockets and webrtc. I would like to send messages to all connected users except sender but I cannot find suitable solution. To be more specific, I would like to send notifications to other connected users that new user has joined to the chat.
I am trying to give a unique ID to every connected user, but the first assigned ID is re-writed by every new user and I cannot diferentiate users.
Server:
// list of users
var CLIENTS=[];
var id;
// web server is using 8081 port
var webSocketServer = new WebSocketServer.Server({ port: 8081 });
// check if connection is established
webSocketServer.on('connection', function(ws) {
id = Math.random();
CLIENTS[id] = ws;
CLIENTS.push(ws);
ws.on('message', function(message) {
console.log('received: %s', message);
var received = JSON.parse(message);
if(received.type == "login"){
ws.send(message); // send message to itself
/* *********************************************************** */
/* *** Here I trying to check if message comes from sender *** */
sendNotes(JSON.stringify({
user: received.name,
type: "notes"
}), ws, id);
/* *********************************************************** */
}else if(received.type == "message"){
sendAll(message); // broadcast messages to everyone including sender
}
});
ws.on('close', function() {
console.log('user ' + CLIENTS[ws] + ' left chat');
delete CLIENTS[ws];
});
});
function sendNotes(message, ws, id) {
console.log('sendNotes : ', id);
if (CLIENTS[id] !== ws) {
console.log('IF : ', message);
for (var i = 0; i < CLIENTS.length; i++) {
CLIENTS[i].send(message);
}
}else{
console.log('ELSE : ', message);
}
}
function sendAll(message) {
for (var i=0; i < CLIENTS.length; i++) {
CLIENTS[i].send(message); // broadcast messages to everyone including sender
}
}
Client:
loginButton.addEventListener("click", function(){
name = usernameInput.value;
if(name.length > 0){
socket.send(JSON.stringify({
type: "login",
name: name
}));
}
});
function sendData() {
var data = dataChannelSend.value;
var userName = document.getElementById('greetingUser').innerHTML;
socket.send(JSON.stringify({
username : userName, // fetch user name from browser, after login
type : "message",
message : data
}));
}
socket.onmessage = function(message) {
var envelope = JSON.parse(message.data);
switch(envelope.type) {
case "login":
onLogin(envelope);
break;
case "message":
showMessage(envelope);
break;
}
};
I would highly appreciate If you could give me any hint. Thanks
Here is a very simple way of sending to everyone connected except the sender.
Create a broadcast function on your webSocketServer instance that will
take two params.
...
var webSocketServer = new WebSocketServer.Server({ port: 8081 });
...
/*
* method: broadcast
* #data: the data you wanna send
* #sender: which client/ws/socket is sending
*/
webSocketServer.broadcast = function(data, sender) {
webSocketServer.clients.forEach(function(client) {
if (client !== sender) {
client.send(data)
}
})
}
...
// On your message callback.
ws.on('message', function(message) {
...
// Note that we're passing the (ws) here
webSocketServer.broadcast(message, ws);
})
That's it, the broadcast method will send to each connected client
except the one who is sending.
Ok, so we are now storing the CLIENTS in a way that allows us to uniquely identify each client that is connecting, and store arbitrary information about them for later retrieval.
The code below will send the "notes" message to all clients, and THEN add the newly connecting client to the "all clients" list.
SERVER.JS:
var http = require('http'),
Static = require('node-static'),
WebSocketServer = new require('ws'),
// list of users
/*
We are now storing client data like this:
CLIENTS = {
uniqueRandomClientID: {
socket: {}, // The socket that this client is connected on
clientDetails: { // Any details you might wish to store about this client
username: "",
etc: "etc"
}
}
};
So now to get at the socket for a client, it'll be: CLIENTS[uniqueRandomClientID].socket.
Or to show a client's username, it'll be: CLIENTS[uniqueRandomClientID].clientDetails.username.
You might want to write a 'getClientByUsername' function that iterates the CLIENTS array and returns the client with that username.
*/
CLIENTS = {},
// web server is using 8081 port
webSocketServer = new WebSocketServer.Server({ port: 8081 });
// check if connection is established
webSocketServer.on('connection', function(ws) {
console.log('connection is established');
// Now using a randomly generated ID to reference a client. Probably should be better than Math.random :D
var wsID = Math.floor(Math.random() * 1000);
ws.on('message', function(message) {
console.log('received: %s', message);
var received = JSON.parse(message);
if(received.type == "login"){
// If a client with this login name doesnt exist already, its a new client
if(!CLIENTS[wsID]) {
doBroadcast(
{
"newuser": received.name,
type: "notes"
}
);
// Now add this new client to the list
CLIENTS[wsID] = {
socket: ws,
clientDetails: {
username: received.name
}
};
}
} else if(received.type == "message") {
doBroadcast(message); // broadcast messages to everyone including sender
}
});
ws.on('close', function(_event) {
if(CLIENTS[wsID]) {
console.log('user ' + CLIENTS[wsID].clientDetails.username + ' left chat');
delete CLIENTS[wsID];
}
});
/*
* Added this to 'catch' errors rather than just red dump to console. I've never actually done anything with this myself (I *like* red text in my console), but I know this handler should be here :P
*/
ws.on('error', function(_error) {
console.log("error!");
console.log(_error);
});
/*
* Send an object to a client
*
* #param WebSocketClient _to - The client you want to send to (generally an index in the CLIENTS array, i.e CLIENTS["bobsusername123"]
* #param Object _message - A stringifyable JSON object. Complex ones can screw things up, but your basic key/value pairs are usually fine to send.
*/
function doSend(_to, _message) {
_to.send(JSON.stringify(_message));
};
// Added broadcast function to replace sendAll
// Notice how it JSON stringifies the data before sending
/*
* Broadcast a message to all clients
*
* #param Object _message - A stringifyable JSON object. Complex ones can screw things up, but your basic key/value pairs are usually fine to send.
*/
function doBroadcast(_message) {
for(var client in CLIENTS) {
if(!CLIENTS.hasOwnProperty(client)) continue;
doSend(CLIENTS[client].socket, _message);
}
};
});
var fileServer = new Static.Server('.');
http.createServer(function (req, res) {
fileServer.server(req, res);
}).listen(8080, function(){
console.log("Server is listening 8080 port.");
});
console.log("Server is running on 8080 and 8081 ports");
MY CLIENT.JS (for your reference):
var loginButton = document.getElementById("loginbutton"),
usernameInput = document.getElementById("usernameInput");
var SocketClient = function(_uri, _callbacks) {
this.uri = _uri;
this.callbacks = _callbacks;
};
SocketClient.prototype = {
send: function(_message) {
this.socket.send(_message);
},
connect: function() {
try {
this.socket = new WebSocket("ws://" + this.uri);
} catch(e) { return false; }
for(var callback in this.callbacks) {
if(!this.callbacks.hasOwnProperty(callback)) continue;
this.socket["on" + callback] = this.callbacks[callback];
}
return true;
}
};
var socketClient = new SocketClient(
"127.0.0.1:8081",
{
open: function() {
console.log("connected.");
},
message: function(_message) {
console.log("received data:");
console.log(_message);
},
close: function() {
console.log("closed.");
},
error: function(_error) {
console.log("error: ");
console.log(_error);
}
}
);
socketClient.connect();
loginButton.addEventListener("click", function(){
name = usernameInput.value;
if(name.length > 0){
socketClient.send(JSON.stringify({
type: "login",
name: name
}));
}
});
AND THE CLIENT.HTML TO GO WITH IT:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<input type="text" id="usernameInput"/>
<button type="button" id="loginbutton">Login</button>
<script src="client.js"></script>
</body>
</html>
Ive tested this with NWJS v0.12.3 running the server and Firefox on the client.
This should work
const WebSocket = require('ws');
// Websocket variables
const wss = new WebSocket.Server({
port: 3000
});
console.log('Websocket active on port 3000...');
// New WebSocket Connection
wss.on('connection', function connection(ws) {
console.log('new connection')
// On Message Received
ws.on('message', function incoming(message) {
console.log(message)
// Send To Everyone Except Sender
wss.clients.forEach(function(client) {
if (client !== ws) client.send(message);
});
});
});

Creating and using a data channel between two peers with webRTC

I am trying to setup a peer to peer file sharing system using WebRTC. I'm able to open a data channel on each side, but I can't send messages from one user to another. Moreover, if one peer closes the channel, the other, the onclose event is only triggered for this user.
What's the proper way to setup and use a data channel with webRTC?
Could you tell me what's wrong or missing in my code?
//create RTC peer objet.
var RTCPeerConnection = webkitRTCPeerConnection;
var RTCIceCandidate = window.RTCIceCandidate;
var RTCSessionDescription = window.RTCSessionDescription;
var iceServers = {
iceServers: [{
url: 'stun:stun.l.google.com:19302'
}]
};
var p2p_connection = new RTCPeerConnection({
iceServers: [
{ 'url': (IS_CHROME ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121') }
]
});
// send offer (only executes in one browser)
function initiateConnection() {
p2p_connection.createOffer(function (description) {
p2p_connection.setLocalDescription(description);
server_socket.emit('p2p request', description,my_username);
});
};
// receive offer and send answer
server_socket.on('p2p request', function(description,sender){
console.log('received p2p request');
p2p_connection.setRemoteDescription(new RTCSessionDescription(description));
p2p_connection.createAnswer(function (description) {
p2p_connection.setLocalDescription(description);
server_socket.emit('p2p reply', description,sender);
});
});
// receive answer
server_socket.on('p2p reply', function(description,sender){
console.log('received p2p reply');
p2p_connection.setRemoteDescription(new RTCSessionDescription(description));
});
// ICE candidates
p2p_connection.onicecandidate = onicecandidate; // sent event listener
// locally generated
function onicecandidate(event) {
if (!p2p_connection || !event || !event.candidate) return;
var candidate = event.candidate;
server_socket.emit('add candidate',candidate,my_username);
}
// sent by other peer
server_socket.on('add candidate', function(candidate,my_username){
p2p_connection.addIceCandidate(new RTCIceCandidate({
sdpMLineIndex: candidate.sdpMLineIndex,
candidate: candidate.candidate
}));
});
// data channel
var dataChannel = p2p_connection.createDataChannel('label');
dataChannel.onmessage = function (event) {
var data = event.data;
console.log("I got data channel message: ", data);
};
dataChannel.onopen = function (event) {
console.log("Data channel ready");
dataChannel.send("Hello World!");
};
dataChannel.onclose = function (event) {
console.log("Data channel closed.");
};
dataChannel.onerror = function (event) {
console.log("Data channel error!");
}
Update:
Found the solution there: http://www.html5rocks.com/en/tutorials/webrtc/basics/
p2p_connection.ondatachannel = function (event) {
receiveChannel = event.channel;
receiveChannel.onmessage = function(event){
console.log(event.data);
};
};
You might consider using the simple-peer library to avoid dealing with these complexities in the future. The WebRTC API calls are confusing and the ordering is sometimes hard to get right.
simple-peer supports video/voice streams, data channel (text and binary data), and you can even use the data channel as a node.js-style duplex stream. It also supports advanced options like disabling trickle ICE candidates (so each client only needs to send one offer/answer message instead of many repeated ice candidate messages). It's un-opinionated and works with any backend.
https://github.com/feross/simple-peer
Abstractions!
https://github.com/feross/simple-peer (noted above by #Feross)
https://github.com/rtc-io/rtc-mesh
https://github.com/dominictarr/scuttlebutt
https://github.com/mafintosh/peervision
https://github.com/muaz-khan/DataChannel
Gigantic list of related projects...
https://github.com/kgryte/awesome-peer-to-peer

how to prevent new connection on every page refresh in sockjs

So, every time I refresh the page, it seems like sockjs is creating a new connection.
I am saving every message to my mongodb on every channel.onmessage, so if I refresh my page 7 times and send a message, I would save 7 messages of the same content into my mongodb.
This is very problematic because when I retrieve those messages when I go into the chat room, to see the log, I would see bunch of duplicate messages.
I want to keep track of all connections that are 'active', and if a user tries to make another connection, I want to terminate the old one so there is only one connection listening to each message at a time.
How do I do this ?
var connections = {};
//creating the sockjs server
var chat = sockjs.createServer();
//installing handlers for sockjs server instance, with the same url as client
chat.installHandlers(server, {prefix:'/chat/private'});
var multiplexer = new multiplexServer.MultiplexServer(chat);
var configChannel = function (channelId, userId, userName){
var channel = multiplexer.registerChannel(channelId);
channel.on('connection', function (conn) {
// console.log('connection');
console.log(connections);
connections[channelId] = connections[channelId] || {};
if (connections[channelId][userId]) {
//want to close the extra connection
} else {
connections[channelId][userId] = conn;
}
// }
// if (channels[channelId][userId]) {
// conn = channels[channelId][userId];
// } else {
// channels[channelId][userId] = conn;
// }
// console.log('accessing channel! ', channels[channelId]);
conn.on('new user', function (data, message) {
console.log('new user! ', data, message);
});
// var number = connections.length;
conn.on('data', function(message) {
var messageObj = JSON.parse(message);
handler.saveMessage(messageObj.channelId, messageObj.user, messageObj.message);
console.log('received the message, ', messageObj.message);
conn.write(JSON.stringify({channelId: messageObj.channelId, user: messageObj.user, message: messageObj.message }));
});
conn.on('close', function() {
conn.write(userName + ' has disconnected');
});
});
return channel;
};
The way I resolve a problem like yours was with a Closure and Promises, I don't know if that could help you. I let you the code that help me, this is with EventBus from Vertx:
window.Events = (function NewEvents() {
var eventBusUrl = $('#eventBusUrl').val();
var eventBus = null;
return new RSVP.Promise(function(resolve, reject) {
if(!eventBus) {
eventBus = new vertx.EventBus(eventBusUrl);
eventBus.onopen = function eventBusOpened() {
console.log('Event bus online');
resolve(eventBus);
}
eventBus.onclose = function() {
eventBus = null;
};
}
});
}());
And then in other script I call it in this way:
Events.then(function(eventBus) {
console.log("registering handlers for comments");
eventBus.registerHandler(address, function(incomingMessage) {
console.log(incomingMessage);
});
});
I hope this can help you.
Regards.

Categories