Within IBM bluemix, my node clients keep having their websocket connections closed even though my server code does not initiate any closures.
My server side code is as follows:
app.ws('/problemUpdate', function(ws, req) {
// if we have the maximum number of clients, remove the oldest
if (clients.length>MAX_CLIENTS){
ws=clients.pop();
ws.close();
}
clients.unshift(ws);
ws.on('close', function(msg) {
// on close, remove clients
for (var i = 0; i < clients.length; i++) {
if(clients[i]==ws){
console.log("removing");
clients.splice(i,1);
}
}
});
ws.on('message', function(msg) {
if (readyToSend(ws)){
ws.send(ws);
}
});
// listen the event
eventEmitter.on('updateProblem', function(updatedProblem){
//Broadcast to all clients
console.log("Total Clients: "+clients.length);
for (var i = 0; i < clients.length; i++) {
var client = clients[i];
if (readyToSend(client)){
client.send(updatedProblem);
}
}
});
});
My client side websocket related code is as follows:
updateWebsocket(webSocketProblemUpdate);
function updateWebsocket(socket){
socket.onopen = function(){
console.log("Connection Opened");
}
socket.onclose = function(){
}
socket.onerror = function(evt){
console.log("The following error occurred: " + evt.data);
}
socket.onmessage = function(evt){
var jsonProblem = JSON.parse(evt.data);
var problemName = jsonProblem.envelope.problemName;
delete jsonProblem["envelope"];
var option = document.createElement('option');
option.text=problemName;
option.value=problemName;
var alreadyAdded=false;
[].forEach.call( document.getElementById('problems') , function(elm){
if(elm.value==option.value && elm.text==option.text){
//if(elm.text==option.text){
alreadyAdded=true;
// update the content of an already added scenario
accumulatedJsonProblems[problemName]=JSON.stringify(jsonProblem);
$('.problems').change();
}
})
if (!alreadyAdded){
accumulatedJsonProblems[problemName]=JSON.stringify(jsonProblem);
var select = $("#problems")[0];
select.add(option,$('#problems').children('option').length);
document.getElementById('problems').value=problemName;
$('.problems').change();
console.log("The following data was received:" + JSON.stringify(jsonProblem));
}
}
}
Any clues as to what is closing my web sockets?
Thanks,
Aaron
After some research, I found that IBM bluemix closes connections every 2 minutes. A security standard was implemented. To solve the problem, I reopen the websocket from the client every 5 minutes and catch an client side closures with a reopen.
//refresh the websocket every 5 minutes
setInterval(function() {
console.log("Interval expired, refreshing websocket");
// only closing because the on close method automatically opens a new websocket
webSocketProblemUpdate.close();
}, 300000);
socket.onclose = function(){
console.log("Connection Closed");
window.WebSocket = window.WebSocket || window.MozWebSocket;
webSocketProblemUpdate = new WebSocket("ws://"+window.document.location.host+"/problemUpdate");
updateWebsocket(webSocketProblemUpdate);
}
Cheers,
Aaron
Related
I'm trying to write a simple JavaScript application to add an entry to indexedDB from a web form. For some reason, the add request shows as successful, but I can't see anything in the chrome developer window, even if I refresh the database. Here's the javascript that I'm writing.
$(document).ready(function() {
$("#addcsub").click(function(e){
e.preventDefault();
//add the contact
addContact($("#clast").val(), $("#cfirst").val(), $("#cphone").val(), $("#cemail").val());
});
});
document.addEventListener("DOMContentLoaded", function() {
if(!"indexedDB" in window){
return;
}
var openRequest = indexedDB.open("theDatabase",1);
openRequest.onupgradeneeded = function(e) {
var thisDB = e.target.result;
if(!thisDB.objectStoreNames.contains("contacts")) {
var objectStore = thisDB.createObjectStore("contacts", {keyPath:"contactid",autoIncrement: true});
objectStore.createIndex("contactid","contactid",{unique:true});
objectStore.createIndex("lastname","lastname", {unique:false});
objectStore.createIndex("firstname","firstname", {unique:false});
objectStore.createIndex("phone","phone", {unique:false});
objectStore.createIndex("email","email", {unique:false});
}
}
openRequest.onsuccess = function(e) {
db = e.target.result;
}
openRequest.onerror = function(e) {
console.log("Open Request Error");
}
},false);
function addContact(last,first,phone,email){
var transaction = db.transaction(["contacts"],"readwrite");
var store = transaction.objectStore("contacts");
var tc = {
lastname:last,
firstname:first,
phone:phone,
email:email
}
var request = store.add(tc);
request.onerror = function(e){
console.error(request.error);
}
request.onsuccess = function(e){ //this runs when I hit submit
console.log("Add Successful"); //this appears in the console
}
}
Update: I did some poking around and realized that my add transaction is aborting with an error of DOMexception code 22.
Update 2: for some reason, it works on other computers, just not my laptop.
I had issues with the devtools gui with indexedDb. You can try pulling the record through the console and see if it is there, but in the past I've had to close devtools and open it again for it to refresh.
Please help me figure out why my socket.io app fires too many emit messages.
Yes, I have spent weeks on the socket.io docs and relevant stackO questions, experimented with the nesting of my functions and everything but I am just totally stuck.
This is supposed to allow people in a 'room' to vote on an issue. Each client gets one vote (yea, nay, or abstain). The problem is when someone votes, it cannot just add one vote it adds a vote for the number of people in the socket. For example, if three people are in a room and the first vote is cast for 'yea' it registers 3x 'yea' votes. I cant get a good count ever!
client site:
var socket = io.connect('http://localhost');
const url = window.location.href;
let voteName = url.substring(url.lastIndexOf("/") + 1);
let notVoted = true;
$('.voteName').html(voteName.replace(/_/g, ' '));
function getRoom(){
return voteName;
}
socket.on('connect', () => {
// Connected, let's sign-up for to receive messages for this room
socket.emit('room', getRoom());
console.dir(socket.id);
});
function yea () {
if(notVoted){
socket.emit('yea',voteName);
console.log(voteName)
notVoted = false;
}
}
function nay () {
if(notVoted){
io.emit('nay',voteName);
notVoted = false;
}
}
function abs () {
if(notVoted){
io.emit('abs',voteName);
notVoted = false;
}
}
Server Side:
app.post('/votePick', (req, res) => {
let voteName = req.body.votePick;
res.send(voteName);
app.get('/'+voteName,(req,res)=>{
res.sendFile(__dirname + '/canvas.html');
});
var nsp = io.of('/');
io.sockets.on('connection', (socket) => {
socket.on('room', (voteName) => {
//TODO check if room exists
socket.join(voteName);
if(!db[voteName]) {
db[voteName]={'yea':0,'nay':0,'abs':0,'cnctCount':1};
io.emit('update',db[voteName]);
} else {
io.emit('update',db[voteName]);
}
});
socket.on('yea', (voteName)=>{
db[voteName].yea++;
io.emit('update',db[voteName]);
console.log(db[voteName].cnctCount);
});
socket.on('nay', (voteName)=>{
db[voteName].nay++;
io.emit('update',db[voteName]);
console.log(db[voteName]);
});
socket.on('abs', (voteName)=>{
db[voteName].abs++;
io.emit('update',db[voteName]);
console.log(db[voteName]);
});
});
});
my full repo is here:
https://github.com/Bokeefe/sockerPractice
You should do user validation. No one can prevent code on the clientside to be changed, so you always need to check on the serverside. For example you could store the socket ids that have already voted:
const voted = new Set;
then inside the socket event handlers:
if(voted.has(socket)) return;
voted.add(socket);
//vote
this should also solve the problem od votes counted twice.
I have node.js and socket.io performing real-time updates on a DataTables table. When a row is edited, other clients see the updates to the row without refreshing.
This all works well, but I'm at a loss regarding how to automatically refresh the table when node/socket reconnect to the server after a disconnect.
Currently, this is what happens:
Go to page containing table
Let device sleep/disconnect from
server
Power on device and see page containing table
socket.io reconnects to the server, but the table doesn't refresh to get the latest changes.
How can I get socket.io to refresh the table upon reconnect to the server? Ideally, the process would look like this:
Power on device
socket.io reconnects to server, triggers CSS "Loading..." overlay to prevent user from making changes to table
socket.io refreshes the table to show latest content
CSS "Loading..." overlay closes
Server-side script:
console.log('Starting the server');
var app = require('express')();
var express = require('express');
var fs = require("fs");
var server = require('https').createServer(SSL, app);
var io = require('socket.io')(server);
server.listen(100);
io.on('connection', function (socket) {
/*socket.emit('news', { hello: 'world' });
/socket.on('my other event', function (data) {
console.log(data);
});*/
});
io.set('authorization', function(handshakeData, accept) {
console.log('io.authorization called');
accept(null, true);
} );
var lockedRowIDs = [];
io.sockets.on('connection', function(socket) {
console.log('io.connection called');
socket.on('lock', function(rowID) {
console.log('Lock event for rowID: '+rowID);
lock(socket, rowID);
lockedRowIDs.push(rowID);
} );
socket.on('clientJoin', function(username) {
console.log('clientJoin event received');
socket.join('clients');
if (typeof lockedRowIDs !== 'undefined' && lockedRowIDs.length > 0) {
socket.emit('lockedRows', lockedRowIDs);
}
} );
socket.on('unlock', function(rowID) {
console.log('Unlock event for rowID: '+rowID);
unlock(socket, rowID);
removeItemFromArray(lockedRowIDs, rowID);
} );
socket.on('updateData', function(json, action, id) {
if (action == "edit" || action == "create") {
console.log('updateData event for rowID: '+json.row['DT_RowId']);
}
updateData(socket, json, action, id);
} );
} );
function lock(socket, rowID) {
socket.broadcast.to('clients').emit('lock', rowID);
setTimeout(function() {
io.sockets.in('clients').emit('timeout', rowID);
removeItemFromArray(lockedRowIDs, rowID);
},
180000);
}
function unlock(socket, rowID) {
socket.broadcast.to('clients').emit('unlock', rowID);
}
function removeItemFromArray(array, item) {
console.log('removeItemFromArray called with item: '+item);
for(var i = array.length - 1; i >= 0; i--) {
if(array[i] === item) {
array.splice(i, 1);
}
}
}
function updateData(socket, json, action, id) {
if (action == "edit" || action == "create") {
console.log('updateData called with rowID:'+json.row['DT_RowId']);
}
socket.broadcast.to('clients').emit('updateData', json, action, id);
}
Client-side script:
var socket = io('https://www.***.com:100');
socket.on('connect', function() {
console.log('Connected');
socket.emit('clientJoin');
} );
socket.on('lock', function(rowID) {
console.log('Lock event received for rowID: '+rowID);
row = $("tr[id='"+rowID+"']");
row.addClass('locked');
/* Pagenation fix Start */
var table = $('#example').DataTable();
table.row('#'+rowID).nodes().to$().addClass('locked');
/* Pagenation fix End */
} );
socket.on('unlock', function(rowID) {
console.log('Unlock event received for rowID: '+rowID);
row = $("tr[id='"+rowID+"']");
row.removeClass('locked');
/* Pagenation fix Start */
var table = $('#example').DataTable();
table.row('#'+rowID).nodes().to$().removeClass('locked');
/* Pagenation fix End */
} );
socket.on('timeout', function(rowID) {
console.log('Time out event received for rowID: '+rowID);
row = $("tr[id='"+rowID+"']");
row.removeClass('locked');
/* Pagenation fix Start */
var table = $('#example').DataTable();
table.row('#'+rowID).nodes().to$().removeClass('locked');
/* Pagenation fix End */
/* Check if the editor corresponds to the timed out rowID - start */
var modifier = editor.modifier();
if (modifier) {
var data = table.row(modifier).data();
console.log('rowID is '+data.DT_RowId);
if (data.DT_RowId == rowID) {
console.log('Timed out rowID: '+rowID+' matches Editor rowID: '+data.DT_RowId+'. Closing Editor now.');
editor.close();
}
else {
console.log('Timed out rowID: '+rowID+' does not match Editor rowID: '+data.DT_RowId+'. Keeping Editor open.');
}
}
/* Check if the editor corresponds to the timed out rowID - end */
} );
socket.on('lockedRows', function (rowIDs) {
console.log('Iterate through the list of rows and mark it as locked');
table = $('#example').DataTable();
rowCount = rowIDs.length;
console.log('Row count: '+rowCount);
for (var i=0; i<rowCount; i++) {
console.log(rowIDs[i]);
row = $("tr[id='"+rowIDs[i]+"']");
row.addClass('locked');
table.row('#'+rowIDs[i]).nodes().to$().addClass('locked');
}
} );
socket.on('updateData', function(json, action, id) {
if (action == "create" || action == "edit") {
var DT_RowId = json.row['DT_RowId'];
console.log('updateData socket event for rowID: '+DT_RowId+' and action: '+action);
}
var table = $('table#example').DataTable();
if (action == "edit") {
var editedRow = table.row('#'+DT_RowId).nodes().to$();
table.row(editedRow).data(json.row).draw();
console.log('Row updated');
}
if (action == "create") {
console.log('Row created');
table.row.add(json.row).draw();
}
if (action == "remove") {
var removedRow = table.row('#'+id).nodes().to$();
table.row(removedRow).remove().draw();
console.log('Row removed with id '+id);
}
} );
/* Ajax request has been completed, data retrieved from the server */
editor.on('postSubmit', function(e,json, data, action) {
console.log('Post submit');
console.log(data);
console.log('With JSON:');
console.log(json);
console.log('With action:');
console.log(action);
if (action == "create" || action== "edit") {
if (json.row){
console.log('rowID from JSON: '+json.row['DT_RowId']);
socket.emit('updateData', json, action);
}
}
if (action == "remove") {
console.log('rowID from JSON: '+data.id[0]);
socket.emit('updateData', null, action, data.id[0]);
}
} );
editor.on('close', function(e) {
console.log('Close event');
console.log(e);
var modifier = editor.modifier();
console.log(modifier)
if (modifier !== null) {
console.log('Inside modifier')
table = $('#example').DataTable();
if (table.row(modifier).node()) {
rowID = table.row(modifier).node().id;
console.log('rowID='+rowID);
row = $("tr[id='"+rowID+"']");
row.removeClass('locked');
table.row('#'+rowID).nodes().to$().removeClass('locked');
socket.emit('unlock', rowID);
}
}
} );
As I think you know, when you're connected, you're keeping the data up-to-date, but if you have a momentary disconnect and then reconnect, you might have missed some data updates.
There are a number of possible strategies for dealing with this.
Brute-force. Upon a reconnect, get a fresh copy of all the data as if the device had just been turned on. Less efficient, but easy to implement.
Transaction ID or Transaction Time. Each time the server sends an update, it sends either a transaction ID or a transaction server time with that update. The client then keeps track of the last transaction ID or transaction time that it received. When it does a reconnect, it sends an initial message with the last transaction ID or transaction time asking for any updates that have happened since that last transaction. The server can then look through its database to see if there are any newer transactions and, if so, send them to that client. This requires keep track of transactions at your database. It is common that the server can possibly return a "transaction id not supported" type of response which then forces the client back to a brute-force update from scratch. This is a back-stop in case a database rebuild or server crash causes older transaction values to get lost.
True Synchronization. The client reconnects and then does a true synchronization with the server where the client essentially says "this is what I have, do you have anything newer". In the interest of efficiency, many synchronization systems solve this problem by implementing the transaction id described in option 2, but there are certainly many other ways to do synchronization. True synchronization is more useful if the client might also be changing the data while it was disconnected.
As a simplifying way of implementing option 2, if your database does not already support the notion of journaled transactions, then some servers will implement a transaction log in memory where it keeps track of the last N hours of transactions. As long as the server stays up and a disconnect doesn't last longer than N hours, then a request for new data can be satisfied from the in memory transaction log (which is efficient). If the server restarts or the client has been gone longer than N hours, then the client is just forced to do a brute-force update, the same as the client would have done if it was powered off and then powered back on (losing all prior knowledge of the data).
Of course, if there are multiple users of the database, then the transaction log has to be implemented by the database itself in order to make sure it includes all possible transactions.
I'm getting more into JS development on my own personal time and for this created a sort of SDK to log into openID connect implementation ( very basic, only implicit flow and code flow). This post is about the client side JS for the implicit flow.
I have successfully created a small SDK that allows me to launch the login request and monitor the pop up for the finished log in (or window close). In the end I now want to add a custom event so that other code can subscribe and be notified when this happens. I have been unable to get this to work. So my question is, how can I do it given the code posted, and of course if you have any suggestions on bettering my code, I'll gladly accept them.
Here is the code:
var mysystemID = {
authorizationURL:"MYRUL_TO_SERVER/oauth/v2/authorize",
client_id:"MYCLIENTID",
redirect_uri:window.location.protocol+"//"+window.location.host+"/callback",
response_type:"id_token token",
windowref : undefined,
intervalID : null,
access_token:"",
id_token:"null",
parser:document.createElement('a'),
checkLogin : function(){
if (this.windowref==undefined){
return false;
}
if (this.windowref.closed){
console.log("window closed")
clearInterval(this.intervalID)
this.intervalID=null
}
try{
this.parser.href = this.windowref.location.href;
if((this.parser.protocol + "//" +this.parser.host + this.parser.pathname) == this.redirect_uri)
{
console.log("login detected")
clearInterval(this.intervalID)
this.intervalID=null
this.windowref.close()
var obj = {}
var str = this.parser.hash.substring(1)
$.each(str.split("&"),function(index,value){
var pieces = value.split("=")
obj[pieces[0]] = pieces[1]
})
console.log(obj)
this.access_token = obj.token
this.id_token = JSON.parse(atob(obj.id_token.split(".")[1]))
// var event = new CustomEvent("my_login", {"detail":{"access_token":this.id_token}});
// this.dispatchEvent(event);
console.log(this.id_token)
}
}catch(e){
//do nothing
}
}
}
mysystemID.login_process = function(){
var scope = $('#scope').val() ||"openid"
var uri = this.authorizationURL+"?response_type="+encodeURIComponent(this.response_type)
+"&client_id=" + this.client_id +"&redirect_uri="+encodeURIComponent(this.redirect_uri)
+"&scope="+encodeURIComponent(scope)+"&nonce="+Math.floor( Math.random()*99999);
console.log(uri)
this.windowref = window.open(uri,
"login with mysystem
ID","width=400, height=600")
if (this.windowref!=null){
this.intervalID = setInterval(this.checkLogin.bind(mysystemID),500)
}
}.bind(mysystemID)
mysystemID.logout = function(){
this.access_token = ""
this.id_token = ""
}.bind(mysystemID)
mysystemID.isLoggedIn = function(){
return this.access_token!=""
}.bind(mysystemID)
A few notes, I rely on jQuery for some things,
and I instantiate it like so:
$(document).ready(function(){
$('a#login_push').on("click",mysystemID.login_process)
});
Within the code you can see my attempt at the event:
// var event = new CustomEvent("my_login", {"detail":{"access_token":this.id_token}});
// this.dispatchEvent(event);
the listener had been chained to the
$(document).ready(function(){
$('a#login_push').on("click",mysystemID.login_process).on("my_login",function(e,data){console.log('logged in fired')});
});
I'm trying to place OpenTok video calls within my website. How do I notify a particular user for video chat?
var apiKey = "447302";
var sessionId = "1_MX40NDczMDE5Mn5";
var token = "T1==cGFydG5lcl9pZD00NDczMDE5MiZzZGtfdmVyc2l";
// Initialize session, set up event listeners, and connect
var session = TB.initSession(sessionId);
session.addEventListener('sessionConnected', sessionConnectedHandler);
session.connect(apiKey, token);
function sessionConnectedHandler(event) {
var publisher = TB.initPublisher(apiKey, 'myPublisherDiv');
session.publish(publisher);
}
session.on("streamCreated", function(e) {
for (var i = 0; i < e.streams.length; i++) {
var div = document.createElement('div');
div.setAttribute('id', 'stream' + e.streams[i].streamId);
document.getElementById('chatDiv').appendChild(div);
session.subscribe(e.streams[i], e.streams[i].streamId);
}
});
OpenTok's API does not provide any notification logic. You can notify users by playing a sound or sending alert messages.
When a new person joins your session, you will get a connectionCreated event. You can throw a notification on your connectionCreated handler.