How do I disconnect clients from the session with opentok? - javascript

Anytime I call the session.disconnect() method to remove clients from the session, I get this warning: "OpenTok:Publisher:warn Received connectivity event: "Cancel" without "Attempt"
and this error: "OpenTok:Subscriber:error Invalid state transition: Event 'disconnect' not possible in state 'disconnected'"
Could someone please explain to me what that error means? Thanks in advance.
// Initialize the session
var session = OT.initSession(data['apikey'], data['session_id']);
console.log(session);
// Initialize the publisher for the recipient
var publisherProperties = {insertMode: "append", width: '100%', height: '100%'};
var publisher = OT.initPublisher('publisher', publisherProperties, function (error) {
if (error) {
console.log(`Couldn't initialize the publisher: ${error}`);
} else {
console.log("Receiver publisher initialized.");
}
});
$('#session-modal').modal("show");
// Detect when new streams are created and subscribe to them.
session.on("streamCreated", function (event) {
console.log("New stream in the session");
var subscriberProperties = {insertMode: 'append', width: '100%', height: '100%'};
var subscriber = session.subscribe(event.stream, 'subscriber', subscriberProperties, function(error) {
if (error) {
console.log(`Couldn't subscribe to the stream: ${error}`);
} else {
console.log("Receiver subscribed to the sender's stream");
}
});
});
//When a stream you publish leaves a session, the Publisher object dispatches a streamDestroyed event:
publisher.on("streamDestroyed", function (event) {
console.log("The publisher stopped streaming. Reason: "
+ event.reason);
});
//When a stream, other than your own, leaves a session, the Session object dispatches a streamDestroyed event:
session.on("streamDestroyed", function (event) {
console.log("Stream stopped. Reason: " + event.reason);
session.disconnect();
console.log("called session.disconnect().");
});
session.on({
connectionCreated: function (event) {
connectionCount++;
if (event.connection.connectionId != session.connection.connectionId) {
console.log(`Another client connected. ${connectionCount} total.`);
}
},
connectionDestroyed: function connectionDestroyedHandler(event) {
connectionCount--;
console.log(`A client disconnected. ${connectionCount} total.`);
}
});
// Connect to the session
// If the connection is successful, publish an audio-video stream.
session.connect(data['token'], function(error) {
if (error) {
console.log("Error connecting to the session:", error.name, error.message);
} else {
console.log("Connected to the session.");
session.publish(publisher, function(error) {
if (error) {
console.log(`couldn't publish to the session: ${error}`);
} else {
console.log("The receiver is publishing a stream");
}
});
}
});
// Stop the publisher from streaming to the session if the user dismiss the modal
const stopSession = document.getElementById('stop-session');
stopSession.addEventListener("click", (event) => {
event.preventDefault();
session.disconnect();
});

I see this is kind of old, but wanted to share my solution to avoid this error. I'm not sure what the error means, but I call publisher.destroy() before calling session.disconnect() to avoid the error.
openTokPublisher.destroy();
openTokSession.disconnect();

I highly doubt you can disconnect the client with JavaScript. Here what I did.
// Connect to the session
session.connect(token, function connectCallback(error) {
// Get the connectionId
connectionId = session.connection.connectionId;
and use one of their SDK on the backend
https://tokbox.com/developer/sdks/server/
// Disconnect session
function disconnectSession() { // eslint-disable-line no-unused-vars
if (sessionId && connectionId) {
$.ajax({
url: '/OpenTok/DisconnectSession',
type: 'POST',
data: 'sessionId=' + sessionId + '&connectionId=' + connectionId,
});
}
}

Related

Transfer data between client and serviceWorker

I want to try run websockets in serviceWorker.
I write code for register serviceWorker:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(
GetDataForUser.settings.service_worker + "?version=" + GetDataForUser.settings.service_worker_version,
{scope: './'}
)
.then(() => navigator.serviceWorker
.ready
.then((worker) => {
console.log(worker);
worker.sync.register('syncdata');
})
)
.catch((err) => console.log(err));
navigator.serviceWorker.addEventListener('message', event => {
console.log('++++++++++++++++++++', event.data.msg, event.data.url);
});
}
And write serviceWorker code:
var ws = null;
self.addEventListener('install', (event) => {
console.log('Install');
});
self.addEventListener('activate', (event) => {
console.log('Activate');
ws = new WebSocket('ws://local.ll:8880');
ws.onopen = function() {
console.log("Open");
};
// On message receive
ws.onmessage = function (event) {
console.log("Message receive " + event.data);
console.log(event);
event.ports[0].postMessage({'test': 'This is my response.'});
};
// On error connection
ws.onerror = function (error) {
console.log("Error " + error.message);
};
// On close connection
ws.onclose = function (event) {
if (event.wasClean) {
console.log('clean closed');
} else {
console.log('broken connection');
}
console.log('Code: ' + event.code + ' reason: ' + event.reason);
};
});
self.addEventListener('fetch', (event) => {});
self.addEventListener('message', function (evt) {
console.log('postMessage received', evt.data);
ws.send('121212');
});
I try to send data to serviceWorker next:
navigator.serviceWorker.controller.postMessage({'hello': 'world'});
But receive an error:
Uncaught TypeError: Cannot read property 'postMessage' of null
console.log(navigator.serviceWorker);
ServiceWorkerContainer {controller: null, ready: Promise, oncontrollerchange: null, onmessage: null}
Trying to send message from serviceWorker to client next:
event.ports[0].postMessage({'test': 'This is my response.'});
But event.ports is empty.
Whats wrong?
How to transfer data between client and serviceWorker.
To fix your error of controller being null, you need to add the activate event listener
self.clients.claim()
Without that, when the service worker is activated, the page is not controlled by it until the next navigation. This allows the service worker to take control of the pages as soon as it's active.
But I don't think it's your only problem. Looking at your code I think you're confusing WebSocket and MessageChannel. You seem to try to use WebSocket to send messages between client and service worker where you should be using MessageChannel for that.
For exemple if we want to send messages from the client to the SW and allow the SW to respond to those messages, we create a MessageChannel, and we send a port of this channel allong with the message for the SW to use in his response.
in the client :
var msg = new MessageChannel();
msg.port1.onmessage = function(event){
//Response received from SW
console.log(event.data);
};
// Send message to SW
navigator.serviceWorker.controller.postMessage("hello", [msg_chan.port2]);
in the SW :
self.addEventListener('message', function(event){
//Message received from client
console.log(event.data);
//Send response to client using the port that was sent with the message
event.ports[0].postMessage("world");
});
Correct and worked example, only for understanding, not for production:
https://github.com/Shkarbatov/WebSocketInServiceWorkerJS

WebSync Publish ClientId become zero

There is an issue when I pass Client.clientId in the Websync publish method, the clientId sometimes become all zero when catch by the OnReceive event from the client side.
May I know why sometimes the clientId value is successfully sent via the channel but most often the clientId value is somehow become all zero like 00000000-0000-0000-0000-000000000000.
Below is my code
Subscribe
var client = new fm.websync.client('http://localhost:12345/websync');
client.connect
({
onSuccess: function (e) {
$('#connectionStat').val('Successfully connected');
},
onFailure: function (e) {
$('#connectionStat').val('Failed to be connected');
},
onStreamFailure: function (e) {
$('#connectionStat').val('Failed to be connected on stream');
}
});
client.subscribe({
channel: '/testService/clientId',
onSuccess: function (e) {
},
onFailure: function (e) {
},
onReceive: function (e) {
alert(e.getData());
}
});
Publish
Client client = new Client("http://localhost:12345/websync");
client.Connect(new ConnectArgs
{
OnSuccess = (e) =>
{
Debug.WriteLine("Successfully connected [OnSuccess]");
},
OnFailure = (e) =>
{
Debug.WriteLine("Failed in connected [OnFailure]");
},
OnComplete = (e) =>
{
Debug.WriteLine("Successfully connected [OnComplete]");
},
OnStreamFailure = (e) =>
{
Debug.WriteLine("Failed connected [OnStreamFailure]");
}
});
client.Publish(new PublishArgs("/testService/clientId", Json.Serialize(client.ClientId))
{
OnSuccess = (e) =>
{
Debug.WriteLine("Successfully published client id [OnSuccess] = " + client.ClientId.ToString());
},
OnComplete = (e) =>
{
Debug.WriteLine("Successfully published [OnComplete]");
},
OnFailure = (e) =>
{
Debug.WriteLine("Failed to publish [OnFailure]");
},
});
Wait to Publish until after Connect has completed successfully. The client's ID is generated by the server, and when Publish is called, there's a chance the Connect operation hasn't completed yet.
The easiest thing to do is move the Publish call into the OnSuccess callback of Connect.

Webrtc Data Channel always in connecting state and not open

I am creating a small WebRTC app that for now used to exchange text message. I have got the WebRTC connection working but Datachannel always remains in "connecting" state and never goes to "Open".
Please tell me what I am missing here.
Following is the JS.
socket.onmessage = function(e){
console.log("Message from signaling server");
writeToScreen('<span class="server">Server: </span>'+e.data);
var data = JSON.parse(e.data);
switch(data.type) {
case "login":
onLogin(data.success);
break;
case "offer":
onOffer(data.offer, data.name);
break;
case "answer":
onAnswer(data.answer);
break;
case "candidate":
onCandidate(data.candidate);
break;
default:
break;
}
}
// Enable send and close button
$('#send').prop('disabled', false);
$('#close').prop('disabled', false);
$('#connect').prop('disabled', true);
}
function close(){
socket.close();
}
function writeToScreen(msg){
var screen = $('#screen');
screen.append('<p>'+msg+'</p>');
screen.animate({scrollTop: screen.height()}, 10);
}
function clearScreen(){
$('#screen').html('');
}
function sendMessage(){
if(!socket || socket == undefined) return false;
var mess = $.trim($('#message').val());
if(mess == '') return;
writeToScreen('<span class="client">Client: </span>'+mess);
socket.send(mess);
// Clear input
$('#message').val('');
}
$(document).ready(function(){
$('#message').focus();
$('#frmInput').submit(function(){
sendMessage();
});
$('#connect').click(function(){
connect();
});
$('#close').click(function(){
close();
});
$('#clear').click(function(){
clearScreen();
});
});
if (!window.RTCPeerConnection) {
window.RTCPeerConnection = window.webkitRTCPeerConnection;
}
var configuration = {
"iceServers": [
{
"urls": "stun:mmt-stun.verkstad.net"
},
{
"urls": "turn:mmt-turn.verkstad.net",
"username": "webrtc",
"credential": "secret"
}
]
};
myConnection = new RTCPeerConnection(configuration,{optional:[{RtpDataChannels: true},{DtlsSrtpKeyAgreement: true}]});
console.log("RTCPeerConnection object was created");
console.log(myConnection);
openDataChannel();
//when the browser finds an ice candidate we send it to another peer
myConnection.onicecandidate = function (event) {
console.log(event.candidate);
if (event.candidate) {
send({
type: "candidate",
candidate: event.candidate
});
}
};
// Datachannel
var mediaConstraints = {
'offerToReceiveAudio': 1,
'offerToReceiveVideo': 1
};
var connectToOtherUsernameBtn = document.getElementById("connectToOtherUsernameBtn");
console.log(connectToOtherUsernameBtn);
connectToOtherUsernameBtn.addEventListener("click", function () {
console.log("ice state : "+myConnection.iceGatheringState);
var otherUsername = connectToOtherUsernameBtn.value;
connectedUser = otherUsername;
if (otherUsername.length > 0) {
//make an offer
myConnection.createOffer(function (offer) {
send({
type: "offer",
offer: offer
});
console.log(offer);
console.log(typeof(offer));
myConnection.setLocalDescription(offer);
console.log("localDescription");
console.log(myConnection.localDescription);
}, function (error) {
alert("An error has occurred.");
console.log(error);
});
}
});
function send(message) {
if (connectedUser) {
message.name = connectedUser;
}
socket.send(JSON.stringify(message));
};
//when somebody wants to call us
function onOffer(offer, name) {
console.log("offer recieved");
connectedUser = name;
myConnection.setRemoteDescription(new RTCSessionDescription(offer));
myConnection.createAnswer(function (answer) {
myConnection.setLocalDescription(answer);
send({
type: "answer",
answer: answer
});
}, function (error) {
alert("oops...error");
console.log(error);
});
console.log("Answer sent");
}
//when another user answers to our offer
function onAnswer(answer) {
console.log("answer recieved");
myConnection.setRemoteDescription(new RTCSessionDescription(answer));
console.log(myConnection.iceConnectionState );
}
//when we got ice candidate from another user
function onCandidate(candidate) {
myConnection.addIceCandidate(new RTCIceCandidate(candidate));
}
});
//data channel
//creating data channel
function openDataChannel() {
console.log("opening Data Channel");
var dataChannelOptions = {
reliable:true,
};
dataChannel = myConnection.createDataChannel("myDataChannel",dataChannelOptions);
dataChannel.onerror = function (error) {
console.log("Error:", error);
};
dataChannel.onmessage = function (event) {
console.log("Got message:", event.data);
};
}
function sendmsg() {
console.log("send message");
var msgInput=document.getElementById("msgInput");
var val = msgInput.value;
console.log(val);
dataChannel.send(val);
}
function checkstatus(){
console.log("Checking Status");
console.log("signalingState: "+myConnection.signalingState);
console.log("iceConnectionState: "+myConnection.iceConnectionState);
console.log("iceGatheringState: "+myConnection.iceGatheringState);
console.log("localDescription: ");
console.log(myConnection.localDescription);
console.log("remoteDescription:");
console.log(myConnection.remoteDescription);
console.log("Connestion id");
console.log(dataChannel.id);
console.log("Connestion readyState");
console.log(dataChannel.readyState);
}
Following is the console log from chrome.
remove {RtpDataChannels: true}
try again and if it works burn the tutorial or book which recommended those "rtp data channels". They are broken.
I had the same problem. my code was working fine on mozilla using localhost signalling server without internet but on chrome i had this problem. Its Trickle ICE problem.
one solution is you may set trickle ice off.
In chrome, may be you need the internet connection to gather the all possible ICE candidates. because in Chrome Datachannel will not get opened untill peer get all possible ICE candidates.
you can try the following link with internet or without internet. you will have brief idea.
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
for further information you can check this
https://webrtcstandards.info/webrtc-trickle-ice/

nodejs does not emit data to client

I have simple nodejs app with sockets and I've faced an error where I can't find any solution. So I'm emiting from app to client and nothing happens there. Or client can't receive it - I don't know, because I can't check if it was successfully emited to client. This is the error I got when I tried to debug callback of emit:
Error: Callbacks are not supported when broadcasting
This my app code:
http.listen(6060, function () {
console.log("Listening on *: 6060");
});
io.set('authorization', function (handshakeData, accept) {
var domain = handshakeData.headers.referer.replace('http://', '').replace('https://', '').split(/[/?#]/)[0];
if ('***' == domain) {
accept(null, true);
} else {
return accept('You must be logged in to take an action in this site!', false);
}
});
io.use(function (sock, next) {
var handshakeData = sock.request;
var userToken = handshakeData._query.key;
if (typeof userToken !== null && userToken !== 0 && userToken !== '0' && userToken.length > 0) {
connection.query('***',
[xssfilter.filter(validator.escape(userToken))],
function (error, data) {
if (error) {
debug('Cant receive user data from database by token');
next(new Error('Failed to parse user data! Please login!'));
} else {
// load data to this user.
_updateUsers(xssfilter.filter(validator.escape(userToken)), 'add', data[0], sock.id);
_loadPreData();
next(null, true);
}
});
} else {
debug('Cant receive user token');
next(new Error('Failed to parse user data! Please login!'));
}
sock.on("disconnect", function () {
_updateUsers(false, 'remove', false, sock.id);
});
});
// we need to show people online count
io.emit('online-count', {
count: Object.keys(connectedUsers).length
});
And the function used above:
function _updateUsers(userToken, action, userData, sockedID) {
switch (action) {
case 'add':
connectedUsers[sockedID] = {...};
io.emit('online-count', io.emit('online-count', {
count: Object.keys(connectedUsers).length
}););
break;
case 'remove':
delete connectedUsers[sockedID];
io.emit('online-count', io.emit('online-count', {
count: Object.keys(connectedUsers).length
}););
break;
}
}
so after emiting online-count I should accept it on the client side as I'm doing it:
var socket;
socket = io(globalData.socketConn, {query: "key=" + globalData.userData.token});
socket.on('connect', function (data) {
console.log('Client side successfully connected with APP.');
});
socket.on('error', function (err) {
error('danger', 'top', err);
});
socket.on('online-count', function (data) {
console.log('Got online count: ' + data.count);
$('#online_count').html(data.count);
});
but the problem is with this online-count.. Nothing happens and it seems that it's not was even sent from node app. Any suggestions?
The problem was with my logic - I was sending online count only if new user were connecting/disconnecting. Problem were solved by adding function to repeat itself every few seconds and send online count to client side.

Opentok unable to connect to session

This is specific to the Opentok API. I'm quite new to javascript and struggling to get this all to work nicely.
Here's a link to the docs if you need it:
https://tokbox.com/developer/guides/
My error is:
signal error (500): Unable to send signal - you are not connected to the session.
My html file looks like this:
var apiKey = 45317102;
var sessionID = "#{#session.session_id}";
if (OT.checkSystemRequirements() == 1) {
var session = OT.initSession(apiKey, sessionID);
} else {
// the client does not support WebRTC
console.log('does not support WebRTC');
}
var token = "#{#token.token}";
var publisher;
var publisherOptions = {
width: 600,
height: 400,
name: 'Publisher',
// publishAudio: true,
// publishVideo: false
}
var subscriberOptions = {
width: 300,
height: 200,
name: 'Subscriber'
}
// for publisher
session.connect(token, function(error) {
if (error) {
console.log(error.message);
} else {
session.publish('myPublisherDiv', publisherOptions);
console.log('Publishing a stream.');
}
});
// for subscribers
session.on({
streamCreated: function(event) {
session.subscribe(event.stream, 'subscribersDiv', subscriberOptions);
console.log("New stream in the session: " + event.stream.streamId);
}
});
// check if the user is a moderator
if (session.capabilities.forceDisconnect == 1) {
console.log('you can moderate.')
} else {
console.log('you cant moderate');
}
session.signal(
{
data:"hello"
},
function(error) {
if (error) {
console.log("signal error ("
+ error.code
+ "): " + error.message);
} else {
console.log("signal sent.");
}
}
);
and my backend (ruby / rails) controller looks like this:
def create
session = OPENTOK.create_session media_mode: :routed
session_id = session.session_id
#session = Session.new(session_id: session_id)
if #session.save
redirect_to #session
else
render 'new'
end
end
def show
#session = Session.find(params[:id])
unless params[:id].blank?
s = Session.find(params[:id])
token = OPENTOK.generate_token s.session_id
#token = Token.create(session_id: s.id, token: token, active: true)
#token.save
end
end
now I'm not able to make the person who creates the session a moderator (ability to remove others) and it seems like I'm in-fact even unable to connect to the session. Even though when I run it I'm able to have 2 people in the same session at the same time.
Does anybody know what's happening (or more likely what I'm missing from my code)?

Categories