How do I close a sockjs connection on the server side? - javascript

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 connections, I want to be able to force close on the old one from the server side, so there is only 1 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;
};

Simply use .close:
if (connections[channelId][userId]) {
// want to close the extra connection
connections[channelId][userId].close();
} else {
connections[channelId][userId] = conn;
}

Related

TypeError: serialport.parsers.readline is not a function

I am trying to run this program in Raspberry Pi 3.
I have installed nodejs and ws on my raspberry pi.
Then I installed serial port module.
I am trying to create this project:enter link description here
I have tried to find solutions everywhere but I could not find one.
If any one knows how to solve this problem please help me.
var webSocketUrl = "wss://api.artik.cloud/v1.1/websocket?ack=true";
var device_id = "5bb3ba9304674086bee67fa507a215cf"; //DEVICE ID
var device_token = "36b278345b6d4d11abf764ae213c5c70"; //DEVICE TOKEN
var WebSocket = require('ws');
var isWebSocketReady = false;
var data="";
var ws = null;
var serialport = require("serialport");
var SerialPort = serialport.SerialPort;
var sp = new SerialPort("/dev/ttyACM0", { //for serial communication with arduino
baudrate: 9600,
// The baud rate of uno is 9600
parser: serialport.parsers.readline("\n")
});
/**
* Gets the current time in millis
*/
function getTimeMillis(){
return parseInt(Date.now().toString());
}
/**
* Create a /websocket connection and setup GPIO pin
*/
function start() {
//Create the WebSocket connection
isWebSocketReady = false;
ws = new WebSocket(webSocketUrl);
ws.on('open', function() {
console.log("WebSocket connection is open ....");
register();
});
ws.on('message', function(data) {
//this loop is called whenever the client sends some message
handleRcvMsg(data); //data is send to the function handleRcvMsg()
});
ws.on('close', function() {
console.log("WebSocket connection is closed ....");
});
}
/**
* Sends a register message to /websocket endpoint
*/
//Client will only work when device gets registered from here
function register(){
console.log("Registering device on the WebSocket connection");
try{
var registerMessage = '{"type":"register", "sdid":"'+device_id+'", "Authorization":"bearer '+device_token+'", "cid":"'+getTimeMillis()+'"}';
console.log('Sending register message ' + registerMessage + '\n');
ws.send(registerMessage, {mask: true});
isWebSocketReady = true;
}
catch (e) {
console.error('Failed to register messages. Error in registering message: ' + e.toString());
}
}
//data after receiving is sent here for processing
function handleRcvMsg(msg){
var msgObj = JSON.parse(msg);
if (msgObj.type != "action") return; //Early return;
var actions = msgObj.data.actions;
var actionName = actions[0].name; //assume that there is only one action in actions
console.log("The received action is " + actionName);
}
/**
* Send one message to ARTIK Cloud
*/
//This function is responsible for sending commands to cloud
//function sendStateToArtikCloud(parking,temperature,water){
function sendDataToArtikCloud(pantry){
var result=pantry.split(" ");//data gets split by " " to get the values
try{
ts = ', "ts": '+getTimeMillis();
var data = {
"Garlic": result[1],
"Potato":result[2],
"Temperature":result[3],
"Chilli":result[4],
"Humidity": result[5],
"Ginger":result[6],
"Onion": result[7]
};
var payload = '{"sdid":"'+device_id+'"'+ts+', "data": '+JSON.stringify(data)+', "cid":"'+getTimeMillis()+'"}';
console.log('Sending payload ' + payload + '\n');
ws.send(payload, {mask: true});
} catch (e) {
console.error('Error in sending a message: ' + e.toString() +'\n');
}
}
function exitClosePins() {
console.log('Exit and destroy all pins!');
process.exit();
}
start();
//exectes every second when data is received from arduino (5sec programmed delay from arduino)
sp.on("open", function () {
sp.on('data', function(data) {
console.log("Serial port received data:" + data);
//var result=data.split(" ");//data gets split by " " to get the values
//sendStateToArtikCloud(result[0],result[2],result[1]);//parking,temperature,waterlevel
sendDataToArtikCloud(data);
});
});
process.on('SIGINT', exitClosePins);
I am getting an error on my raspberry pi
enter image description here
Suggest me a solution.
The documentation will tell you that Readline is spelled with a capital R
https://www.npmjs.com/package/serialport#module_serialport--SerialPort.parsers
parser: serialport.parsers.Readline("\n")
~
[TypeError: serialport.parsers.readline is not a function.]
If it has not been resolved yet, try this method.
var serialport = require("serialport")
var SerialPort = serialport.SerialPort;
var sp = new serialport("/dev/ttyACM0"),{
BaudRate: 9600,
parser: new serialport.parsers.Readline("\r\n")
});
I hope your problem is solved.

WebSocket connection established, onopen never runs

I'm trying to learn WebSockets and I've created a websocket server in Node and am now working on the browser implementation. I have tested that the server works and responds how I want using a chrome extension called Smart WebSocket Client.
The console in the browser says Button pressed! when you press the button and Connection lost! (1000) when I end the Node process but never has it said Connection Established!.
Edit: The client code is running on a site secured with HTTPS and that serves the HSTS header while the server code (currently, but won't continue to be) is running on localhost over normal HTTP, if it's any concern.
Server Code:
const websock = require('./node_modules/ws');
const HashMap = require('./node_modules/hashmap');
const jsonparse = require('./node_modules/jsonparse');
const randomstring = require('./node_modules/randomstring');
class Session {
constructor(server) {
this.server = server;
this.clients = [];
}
}
var connections = new HashMap();
const json = new jsonparse();
const wss = new websock.Server({ port: 36245 });
process.on('SIGINT',function () {
console.log("Recieved SIGINT, stopping gracefully...");
wss.clients.forEach(function (ws) {
console.log("-Ended connection with "+ws.upgradeReq.socket.remoteAddress+" (1001)");
ws.closeReasonCode = 1001;
ws.close();
});
process.exit(1);
});
wss.on('connection',function connection(ws,conn) {
console.log("+Recieved connection from "+ws._socket.remoteAddress);
ws.upgradeReq = conn;
ws.hasHandshook = false;
ws.onmessage = function message(msg) {
var message;
try {
message = JSON.parse(msg.data);
} catch (ex) {
ws.send("{\"e\":\"Invalid json.\"}");
return;
}
if (!ws.hasHandshook) {
ws.hasHandshook = true;
if (message.type === "client") {
//ensure code was provided and has a room
if (typeof message.code === 'undefined' || !connections.has(message.code)) {
ws.send("{\"e\":\"Invalid game code.\"}");
ws.closeReasonCode = 4001;
ws.closeDescription = "Invalid game code.";
console.log("-Ended connection with "+ws._socket.remoteAddress+ " (4001)");
ws.close();
}
if (typeof message.name === 'undefined') {
//TODO error out, no player name provided
}
//attach client to game session
ws.clientType = "client";
ws.gameCode = message.code;
ws.playerName =
connections.get(message.code).clients.add(ws);
ws.send("{\"joingame\":\"true\"}");
} else {
ws.send("{\"e\":\"Invalid type provided on handshake message.\"}");
ws.closeReasonCode = 4000;
ws.closeDescription = "Invalid type provided on handshake message.";
console.log("-Ended connection with "+ws._socket.remoteAddress+" (4000)");
ws.close();
}
}
};
ws.onclose = function close() {
console.log("-Ended connection with "+ws.upgradeReq.socket.remoteAddress+" (Client Closed)");
}
});
Client Code, which is successfully run on the press of a button on the page:
function DoJoinGame () {
console.log("Button pressed!");
gameCode = document.getElementById('base-gameCode').value.toUpperCase();
playerName = document.getElementById('base-playerName').value;
var ws = new WebSocket("ws://localhost:36245");
ws.onopen = function (event) {
console.log("Connection Established!");
ws.send("{\"type\":\"client\",\"code\":\""+gameCode+"\",\"name\":\""+playerName+"\"");
};
ws.onmessage = function (msg) {
let message = JSON.parse(msg.data);
if (message.joingame) { //if this is a "client added to session" message, set display: none; on the codeEntry div
document.getElementById('codeEntry').style.display = "none";
}
//TODO handle message
};
ws.onclose = function (evt) {
console.log("Connection lost! ("+evt.code+":"+evt.reason+")");
};
}
Thank you for your help!
Problem fixed. I was attempting to connect to a non secure websocket server from a secure origin and chrome & co. wasn't a fan.

How to start a Server-Sent Event "only once" inside a SharedWorker to push message for any open script?

I have a server-sent event (SSE) implementation that is working with almost no issues. The only issue that I am having is "one user can have many connections to the server". Basically, If a user opens more than one web browser's tab, each tab will create a brand new server-sent event request to the server which cause many requests to run from a single user.
To solve this problem, I would like to run the SSE inside a Javascript's SharedWorker.
This means that I have only one SSE communicating with a SharedWorker. Then, every page/web browser will communication with the SharedWorker. This gives me the advantage of only allowing one SSE per user.
This is how my SSE working currently without any type of worker.
$(function(){
//connect to the server to read messages
$(window).load(function(){
startPolling( new EventSource("poll.php") );
});
//function to listen for new messages from the server
function startPolling(evtSource){
evtSource.addEventListener("getMessagingQueue", function(e) {
var data = JSON.parse(e.data);
//handle recieved messages
processServerData(data);
}, false);
evtSource.onerror = function(e) {
evtSource.close();
};
}
});
I would like to have the same setup running. However, I would like to run it inside a javascript's SharedWorker to eliminate having more than one SSE per user.
I am struggling to implement the SharedWorker. Here is what I tried so far
I created a file called worker.js and added this code to it
var ports = [] ;
onconnect = function(event) {
var port = event.ports[0];
ports.push(port);
port.start();
var serv = new EventSource(icwsPollingUrl)
serv.addEventListener("getMessagingQueue", function(e) {
var data = JSON.parse(e.data);
processServerData(data);
}, false);
}
Then on the page where I want to listed to messages I have this code
$(function(){
$(window).load(function(){
var worker = new SharedWorker("worker.js");
worker.port.start();
worker.port.onmessage = function(e) {
console.log(e.data);
console.log('Message received from worker');
}
});
});
What am I missing here?
What am I doing wrong?
How can I correct the implementation?
EDITED
Based on the comments below from #Bergi, here is an updated version of my implementation which is still not posting messages to the connectors. I added comments to my code explaining understanding of what is going on with the code.
On a landing page i.e. index.php I connect to my SharedWorker like this
$(function($){
//establish connection to the shared worker
var worker = new SharedWorker("/add-ons/icws/js/worker1.js");
//listen for a message send from the worker
worker.port.addEventListener("message",
function(event) {
console.log(event.data);
}
, false
);
//start the connection to the shared worker
worker.port.start();
});
This is the code as my worker1.js file contains
var ports = [] ;
//runs only when a new connection starts
onconnect = function(event) {
var port = event.ports[0];
ports.push(port);
port.start();
//implement a channel for a communication between the connecter and the SharedWorker
port.addEventListener("message",
function(event) {
listenForMessage(event, port);
}
);
}
//reply to any message sent to the SharedWorker with the same message but add the phrase "SharedWorker Said: " to it
listenForMessage = function (event, port) {
port.postMessage("SharedWorker Said: " + event.data);
}
//runs every time and post the message to all the connected ports
function readNewMessages(){
var serv = new EventSource(icwsPollingUrl)
serv.addEventListener("getMessagingQueue", function(e) {
var queue = JSON.parse(e.data);
notifyAllPorts(queue);
}, false);
}
//check all open ports and post a message to each
function notifyAllPorts(msg){
for(i = 0; i < ports.length; i++) {
ports[i].postMessage(msg);
}
}
Here is one more version of my worker1.js
var ports = [] ;
//runs only when a new connection starts
onconnect = function(event) {
var port = event.ports[0];
ports.push(port);
port.start();
//implement a channel for a communication between the connecter and the SharedWorker
port.addEventListener("message",
function(event) {
listenForMessage(event, port);
}
);
}
//reply to any message sent to the SharedWorker with the same message but add the phrase "SharedWorker Said: " to it
listenForMessage = function (event, port) {
port.postMessage("SharedWorker Said: " + event.data);
}
readNewMessages();
//runs every time and post the message to all the connected ports
function readNewMessages(){
console.log('Start Reading...');
var serv = new EventSource(icwsPollingUrl);
serv.addEventListener("getMessagingQueue", function(e) {
var queue = JSON.parse(e.data);
console.log('Message Received');
console.log(queue);
notifyAllPorts(queue);
}, false);
}
//check all open ports and post a message to each
function notifyAllPorts(msg){
for(i = 0; i < ports.length; i++) {
ports[i].postMessage(msg);
}
}
maybe is late but you can create a EventSource singleton in the worker like this:
let ports = [];
var EventSourceSingleton = (function () {
var instance;
function createInstance() {
var object = new EventSource('your path');
return object;
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
onconnect = function(e) {
var port = e.ports[0];
ports.push(port);
var notifyAll = function(message){
ports.forEach(port => port.postMessage(message));
}
var makeConnection = function (){
var source = EventSourceSingleton.getInstance();
source.onopen = function (e){
var message = "Connection open"
port.postMessage(message);
}
source.onerror = function(e){
var message ="Ups you have an error";
port.postMessage(message);
}
source.onmessage = function(e){
// var message = JSON.parse(event.data);
notifyAll(e.data);
}
}
port.onmessage = function(e) {
makeConnection();
}
port.start();
}
And you can call it from outside like this.
var shWorker = new SharedWorker('woker.js');
shWorker.port.onmessage = function (e) {
console.log('Message received from worker');
setmsj(e.data);
}
//Dummy message - For initialize
shWorker.port.postMessage(true);
Have fun debuggin this on // chrome://inspect/#workers.

Node JS one to one Messages

I have made a Module in Node JS. In which I am pushing a Message from One user to another Using Different Web Browsers.
In CMD Console its working properly and message is sending. But it is not showing on Client Page.
Here is the CMD Output:
Here is My Node Server Code:
var app = require('http').createServer(),
io = require('socket.io').listen(app),
fs = require('fs'),
mysql = require('mysql'),
connectionsArray = [],
connection = mysql.createConnection({
host : 'localhost',
user : 'admin',
password : 'admin',
database : 'test',
port : 3306
}),
POLLING_INTERVAL = 3000,
pollingTimer;
// If there is an error connecting to the database
connection.connect(function(err) {
// connected! (unless `err` is set)
console.log( err );
});
// creating the server ( localhost:8000 )
app.listen(8000);
/*
*
* HERE IT IS THE COOL PART
* This function loops on itself since there are sockets connected to the page
* sending the result of the database query after a constant interval
*
*/
var pollingLoop = function () {
// Doing the database query
var query = connection.query('SELECT * FROM users'),
users = []; // this array will contain the result of our db query
// setting the query listeners
query
.on('error', function(err) {
// Handle error, and 'end' event will be emitted after this as well
console.log( err );
forwardMessage( err );
})
.on('result', function( user ) {
// it fills our array looping on each user row inside the db
users.push( user );
})
.on('end',function(){
// loop on itself only if there are sockets still connected
if(connectionsArray.length) {
pollingTimer = setTimeout( pollingLoop, POLLING_INTERVAL );
//updateSockets({users:users});
}
});
};
// creating a new websocket to keep the content updated without any AJAX request
io.sockets.on( 'connection', function ( socket )
{
console.log('Number of connections:' + connectionsArray.length);
// starting the loop only if at least there is one user connected
if (!connectionsArray.length)
{
//pollingLoop();
}
socket.on('addUser', function (id)
{
socket.id = id;
console.log('Here I am. My User ID is: '+socket.id);
socket.emit( 'notification' , 'ok' );
});
socket.on('disconnect', function ()
{
var socketIndex = connectionsArray.indexOf( socket );
console.log('socket = ' + socketIndex + ' disconnected');
if (socketIndex >= 0)
{
connectionsArray.splice( socketIndex, 1 );
}
});
socket.on('sendMessage', function (message)
{
console.log( 'Wow I got a New Message:'+message+' sent by: '+socket.id );
socket.emit( 'notification' , message );
//insertion in db
// get concerned ids to pass notification
var users = new Array();
users.push('777');
// call forwardMessage to send notifications...
forwardMessage(users,message);
});
console.log( 'A new socket is connected!' );
connectionsArray.push( socket );
});
var forwardMessage = function ( users, message )
{
connectionsArray.forEach(function( userSocket )
{
if(inArray(userSocket.id,users))
{
userSocket.volatile.emit( 'notification' , message );
}
});
function inArray(needle, haystack) // In_Array Function For Javascript
{
var length = haystack.length;
for(var i = 0; i < length; i++)
{
if(haystack[i] == needle)
return true;
}
return false;
}
}
And here is my Client Code:
<script>
// create a new websocket
var socket = io.connect('http://localhost:8000');
// on message received we print all the data inside the #container div
socket.on('notification', function (data)
{
//alert('got notification : '+data);
$('#container').html('Got ');
});
socket.emit('addUser','<?php echo $_GET['id']; ?>');
//----------------- SEND MESSAGE FUNCTION -------------------------//
function sendMESSAGE()
{
var message = $("#message").val();
socket.emit('sendMessage',message);
}
</script>
Can anyone tell me what am I doing wrong here or missing here ?

Socket.io Identify User for Socket

I write some code example that identifi connected users via socket.io... So now I must write a code on index page to comunicate with users.
The code is below and HOW to send a message to user[1] "Welcome" and for user[2] "HI men" and also limit connection fr 2 users. so when 2 user connected then anybody else cant connect..
Index.html:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
var users;
socket.on('hello', function (data) {
console.log(data.hello);
});
socket.on('listing', function (data) {
users = data;
});
socket.on('chat', function (message) {
console.log(message);
});
socket.on('message', function (message) {
console.log(message);
});
function chat (message) {
socket.emit('chat', message);
}
function message (user, message) {
socket.emit('message', {
user: user,
message: message
});
}
</script>
app.js
var express = require('express');
var app = express.createServer();
var io = require('socket.io').listen(app);
app.listen(3000);
app.use(express.static(__dirname));
var users = {};
var userNumber = 1;
function getUsers () {
var userNames = [];
for(var name in users) {
if(users[name]) {
userNames.push(name);
}
}
return userNames;
}
io.sockets.on('connection', function (socket) {
var myNumber = userNumber++;
var myName = 'user#' + myNumber;
users[myName] = socket;
socket.emit('hello', { hello: myName });
io.sockets.emit('listing', getUsers());
socket.on('chat', function (message) {
io.sockets.emit('chat', myName + ': ' + message);
});
socket.on('message', function (data) {
users[data.user].emit('message', myName + '-> ' + data.message);
});
socket.on('disconnect', function () {
users[myName] = null;
io.sockets.emit('listing', getUsers());
});
});
app.listen(process.env.PORT);
You can start by taking a look at how to configure authorization with Socket.io. The handshakeData provided by the callback can be modified there (ie: add a username property), and any changes will be accessible via socket.handshake in your app.js (via the object passed in to the callback for io.sockets.on('connection',..). Using request header information that's also accessible from the handshakeData, you can set user values within the authorization callback (ie: from a database) so you can identify the user for the given socket in your app.js.
Here's a similar example
I know it has been a long time since you asked this, but just 4 days ago I published a module for node js, express and socket.io which manages that exactly thing you wanted. Check the Usage and Example; I hope you will find this module helpful!
You can install it via NPM socket.io.users This is a node js module for socket.io applications. One user per client.
Some of the usage code:
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var socketUsers = require('socket.io.users');
// ...
socketUsers.Session(app); // IMPORTANT !
// ...
var rootIo = require('socket.io')(server); // default '/' as namespace.
var chatIo = rootIo.of('/chat');
var rootUsers = socketUsers.Users; /* default '/' as namespace.
Each namespace has ITS OWN users object list,
but the Id of a user of any other namespace may
have the same value if request comes from the same client-machine-user.
This makes easy to keep a kind of
synchronization between all users of all the different namespaces. */
var chatUsers = socketUsers.Users.of('/chat'); //
rootIo.use(socketUsers.Middleware());
/* IMPORTANT but no errors if you want
to skip it for a io.of(namespace)
that you don't want the socket.io.users' support. */
chatUsers.use(socketUsers.Middleware());
chatUsers.on('connected',function(user){
console.log(user.id + ' has connected to the CHAT');
user.store.username = 'username setted by server side'; /*at the store
property you can store any type of properties
and objects you want to share between your user's sockets. */
user.socket.on('any event', function(data){
/*user.socket is the current socket, to get all connected sockets from this
user, use: user.sockets */
});
chatIo.emit('set username',user.store.username);
});
rootUsers.on('connected',function(user){
console.log('User has connected with ID: '+ user.id);
});
rootUsers.on('connection',function(user){
console.log('Socket ID: '+user.socket.id+' is user with ID: '+user.id);
});
rootUsers.on('disconnected',function(user){
console.log('User with ID: '+user.id+'is gone away :(');
});
//...server.listen blabla..

Categories

Resources