Node JS TCP Server with simple password prompt problem - javascript

I'm trying to build a TCP server on node JS. The idea is to have multiple TCP clients connect and send / receive data(from server to client / client to server) and to have some sort of authentication (at least to enter a strong password) Also not sure if this approach is worth it. But, I've come up with something (most from online sources and docs) and crated below code.
Code runs and I can connect, but if I send data from client to server, the "password" check function fires up and each time I enter the correct password, a new (duplicate connection) is created. Seems like it keeps calling the same function on each input.
Desired behavior would be ; once client try's to connect, needs to provide the password and then start sending date. Also could someone give me a hint on how to send data back from server, or is it even possible. Or do I need to create a separate function for server.createConnection()
thanks in advance
UPDATE :I've changed the code a bit, but the main issue remains. this bit was supposed to check whether "clientAddress" exists and if so skip the auth part all together.
server.on('connection', (socket) => {
let clientAddress = `${socket.remoteAddress}:${socket.remotePort}`;
console.log(clientAddress)
if(sock.indexOf(clientAddress) !== -1){
console.log('devie found, opening communication')
newConnectionHandler(socket,clientAddress)
} else {
console.log('devie not found, need to authenticate')
userAuth(socket,clientAddress)
}
but as you can guess, it's not working :) if I manually specify the "clientAddress" it works , if I place "sock.push(clientAddress);" within the first block of code, it also works. No auth is asked. But when it's placed within
function userAuth(socket,clientAddress){
socket.write('password : ' )
socket.on('data', function (data) {
let pass = data.toString()
if (pass == password) {
sock.push(clientAddress);
console.log(sock)
newConnectionHandler(socket,clientAddress)
return;
} else {
//console.log(pass)
socket.write('Sorry, you cannot access the server \n')
console.log('acess denied for ' + socket.remoteAddress + ':' + socket.remotePort + '\n')
socket.write('connection closed')
socket.destroy()
}
})
}
code does run as expected and goes all the way till
function newConnectionHandler(socket,clientAddress){
//console.log(sock)
socket.write('Welcome \n')
socket.on('data', function(data1){
console.log("Client Sent: " + data1);
});
but as soon as I send a new message from the client, it goes back as if it was never authenticated and treats my input as the password and because it does not match with the actual password,it destroys the connection.
can someone please give me a hand...
const net = require('net');
const port = 3001;
const host = '192.168.0.165';
const server = net.createServer()
let sock = [];
let password = 123
//server.on('listening',createConnection);
server.on('connection', (socket) => {
let clientAddress = `${socket.remoteAddress}:${socket.remotePort}`;
console.log(clientAddress)
if(sock.indexOf(clientAddress) !== -1){
console.log('devie found, opening communication')
newConnectionHandler(socket,clientAddress)
} else {
console.log('devie not found, need to authenticate')
userAuth(socket,clientAddress)
}
server.on('error', errorHandler);
function errorHandler(err){
console.log(`Error occurred in ${clientAddress}: ${err.message}`);
}
function userAuth(socket,clientAddress){
socket.write('password : ' )
socket.on('data', function (data) {
let pass = data.toString()
if (pass == password) {
sock.push(clientAddress);
console.log(sock)
newConnectionHandler(socket,clientAddress)
return;
} else {
//console.log(pass)
socket.write('Sorry, you cannot access the server \n')
console.log('acess denied for ' + socket.remoteAddress + ':' + socket.remotePort + '\n')
socket.write('connection closed')
socket.destroy()
}
})
}
function newConnectionHandler(socket,clientAddress){
//console.log(sock)
socket.write('Welcome \n')
socket.on('data', function(data1){
console.log("Client Sent: " + data1);
});
socket.once('close', (data) => {
let index = sock.findIndex((o) => {
return o.remoteAddress === socket.remoteAddress && o.remotePort === socket.remotePort;
})
if (index !== -1) sock.splice(index, 1);
sock.forEach((sock) => {
socket.write(`${clientAddress} disconnected\n`);
        });
console.log(`connection closed: ${clientAddress}`);
    });
}
/* function createConnection(){
// Start a connection to the server
var socket = server.on('connect',function(){
// Send the initial message once connected
socket.write({question: "Hello, world?"});
});
// Whenever the server sends us an object...
socket.on('data', function(data){
// Output the answer property of the server's message to the console
console.log("Server's answer: " + data.answer);
});
} */
})
server.listen(port, host, () => {
console.log('TCP Server is running on port ' + port + '.');
});

so it appears as the only bit of code that was causing the authentication loop was the
function userAuth(socket,clientAddress){
socket.write('password : ' )
**socket.on('data', function (data) {**
let pass = data.toString()
after changing "on" with "once" it is now functioning properly. I tested with two TCP clients, both connected and was asked to enter a password. They can both actively send messages to the server and both disconnected properly in the end.
this is the code if anyone finds any use for it :) the connection it's self is still unencrypted so not good for sending/receiving sensitive data.
const net = require('net');
const port = 3001;
const host = '192.168.0.165';
const server = net.createServer()
let sock = [];
let password = 123
//server.on('listening',createConnection);
server.on('connection', (socket) => {
let clientAddress = `${socket.remoteAddress}:${socket.remotePort}`;
console.log(clientAddress)
if(sock.indexOf(clientAddress) !== -1){
console.log('devie found, opening communication')
newConnectionHandler(socket,clientAddress)
} else {
console.log('devie not found, need to authenticate')
userAuth(socket,clientAddress)
}
})
server.on('error', errorHandler);
function errorHandler(err){
console.log(`Error occurred in ${clientAddress}: ${err.message}`);
}
function userAuth(socket,clientAddress){
socket.write('password : ' )
socket.once('data', function (data) {
let pass = data.toString()
if (pass == password) {
sock.push(clientAddress);
console.log(sock)
newConnectionHandler(socket,clientAddress)
return;
} else {
//console.log(pass)
socket.write('Sorry, you cannot access the server \n')
console.log('acess denied for ' + socket.remoteAddress + ':' + socket.remotePort + '\n')
socket.write('connection closed')
socket.destroy()
}
})
}
function newConnectionHandler(socket,clientAddress){
//console.log(sock)
socket.write('Welcome \n')
socket.on('data', function(data1){
console.log("Client Sent: " + data1);
});
socket.on('close', function(data) {
let index = sock.findIndex(function(o) {
return o.remoteAddress === sock.remoteAddress && o.remotePort === sock.remotePort;
})
if (index !== -1) sock.splice(index, 1);
console.log('CLOSED: ' + socket.remoteAddress + ' ' + socket.remotePort);
});
}
/* function createConnection(){
// Start a connection to the server
var socket = server.on('connect',function(){
// Send the initial message once connected
socket.write({question: "Hello, world?"});
});
// Whenever the server sends us an object...
socket.on('data', function(data){
// Output the answer property of the server's message to the console
console.log("Server's answer: " + data.answer);
});
} */
server.listen(port, host, () => {
console.log('TCP Server is running on port ' + port + '.');
});

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.

Running node-rdkafka code in server

I'm running the below node-rdkafka code in Eclipse as Node.js application. This is the sample code from https://blizzard.github.io/node-rdkafka/current/tutorial-producer_.html
I want to run this in a test server and call from iOS Mobile application.
I knew about running node.js app in AWS.
Question I: Is there any other options to run in a free test server environment like Tomcat?
Question II: Even If I am able to run this node.js app in a server, how do i call from a mobile application? Do I need to call producer.on('ready', function(arg) (or) What function i need to call from Mobile app?
var Kafka = require('node-rdkafka');
//console.log(Kafka.features);
//console.log(Kafka.librdkafkaVersion);
var producer = new Kafka.Producer({
'metadata.broker.list': 'localhost:9092',
'dr_cb': true
});
var topicName = 'MyTest';
//logging debug messages, if debug is enabled
producer.on('event.log', function(log) {
console.log(log);
});
//logging all errors
producer.on('event.error', function(err) {
console.error('Error from producer');
console.error(err);
});
//counter to stop this sample after maxMessages are sent
var counter = 0;
var maxMessages = 10;
producer.on('delivery-report', function(err, report) {
console.log('delivery-report: ' + JSON.stringify(report));
counter++;
});
//Wait for the ready event before producing
producer.on('ready', function(arg) {
console.log('producer ready.' + JSON.stringify(arg));
for (var i = 0; i < maxMessages; i++) {
var value = new Buffer('MyProducerTest - value-' +i);
var key = "key-"+i;
// if partition is set to -1, librdkafka will use the default partitioner
var partition = -1;
producer.produce(topicName, partition, value, key);
}
//need to keep polling for a while to ensure the delivery reports are received
var pollLoop = setInterval(function() {
producer.poll();
if (counter === maxMessages) {
clearInterval(pollLoop);
producer.disconnect();
}
}, 1000);
});
/*
producer.on('disconnected', function(arg) {
console.log('producer disconnected. ' + JSON.stringify(arg));
});*/
//starting the producer
producer.connect();
First of all, you need an HTTP server. ExpressJS can be used. Then, just tack on the Express code basically at the end, but move the producer loop into the request route.
So, start with what you had
var Kafka = require('node-rdkafka');
//console.log(Kafka.features);
//console.log(Kafka.librdkafkaVersion);
var producer = new Kafka.Producer({
'metadata.broker.list': 'localhost:9092',
'dr_cb': true
});
var topicName = 'MyTest';
//logging debug messages, if debug is enabled
producer.on('event.log', function(log) {
console.log(log);
});
//logging all errors
producer.on('event.error', function(err) {
console.error('Error from producer');
console.error(err);
});
producer.on('delivery-report', function(err, report) {
console.log('delivery-report: ' + JSON.stringify(report));
counter++;
});
//Wait for the ready event before producing
producer.on('ready', function(arg) {
console.log('producer ready.' + JSON.stringify(arg));
});
producer.on('disconnected', function(arg) {
console.log('producer disconnected. ' + JSON.stringify(arg));
});
//starting the producer
producer.connect();
Then, you can add this in the same file.
var express = require('express')
var app = express()
app.get('/', (req, res) => res.send('Ready to send messages!'))
app.post('/:maxMessages', function (req, res) {
if (req.params.maxMessages) {
var maxMessages = parseInt(req.params.maxMessages);
for (var i = 0; i < maxMessages; i++) {
var value = new Buffer('MyProducerTest - value-' +i);
var key = "key-"+i;
// if partition is set to -1, librdkafka will use the default partitioner
var partition = -1;
producer.produce(topicName, partition, value, key);
} // end for
} // end if
}); // end app.post()
app.listen(3000, () => console.log('Example app listening on port 3000!'))
I don't think the poll loop is necessary since you don't care about the counter anymore.
Now, connect your mobile app to http://<your server IP>:3000/ and send test messages with a POST request to http://<your server IP>:3000/10, for example, and adjust to change the number of messages to send
I might be late on this but this is how I did using promises and found it better than have a time out etc.
const postMessageToPublisher = (req, res) => {
return new Promise((resolve, reject) => {
producer.connect();
producer.setPollInterval(globalConfigs.producerPollingTime);
const actualBody = requestBody.data;
const requestBody = req.body;
const topicName = req.body.topicName;
const key = requestBody.key || uuid();
const partition = requestBody.partition || undefined;
const data = Buffer.from(JSON.stringify(udpatedBody));
/**
* Actual messages are sent here when the producer is ready
*/
producer.on(kafkaEvents.READY, () => {
try {
producer.produce(
topic,
partition,
message,
key // setting key user provided or UUID
);
} catch (error) {
reject(error);
}
});
// Register listener for debug information; only invoked if debug option set in driver_options
producer.on(kafkaEvents.LOG, log => {
logger.info('Producer event log notification for debugging:', log);
});
// Register error listener
producer.on(kafkaEvents.ERROR, err => {
logger.error('Error from producer:' + JSON.stringify(err));
reject(err);
});
// Register delivery report listener
producer.on(kafkaEvents.PUBLISH_ACKNOWLEDGMENT, (err, ackMessage) => {
if (err) {
logger.error(
'Delivery report: Failed sending message ' + ackMessage.value
);
logger.error('and the error is :', err);
reject({ value: ackMessage.value, error: err });
} else {
resolve({
teamName: globalConfigs.TeamNameService,
topicName: ackMessage.topic,
key: ackMessage.key.toString()
});
}
});
});
};
Please note that kafkaEvents contains my constants for the events we listen to and it is just a reference such as kafkaEvents.LOG is same as event.log
and also the calling function is expecting this to a promise and accordingly we user .then(data => 'send your response to user from here') and .catch(error => 'send error response to user
this is how I achieved it using promises

node.js / socket.io - keep track of clients

Dear friends I have a small issue while trying to keep track of a logged in users in chat. The chat is based on two separate channels that work with the help of namespaces:
chatInfra - to handle logged in users and send welcome messages.
chatCom - to handle messageing between users.
I have searched a lot but I found only theoretical explanations that the best solutions is to store users into array. Therefore I tried to keep the track of logged in users by storing them in array and then iterating through them but still the result is not good.
The problem is that after entering to the chat, only the first logged in user's name appears on screen, whereas the second user's name is not visible.
This is my server side code, I am trying to store users into clients array:
var clients = [];
var chatInfra = io.of("/chat_infra").on("connection", function(socket){
socket.on("set_name", function (data) {
clients.push(data.name);
socket.emit('name_set', data);
socket.send(JSON.stringify({
type:'serverMessage',
message:'Welcome!'
}));
socket.broadcast.emit('user_entered', data);
});
});
var chatCom = io.of("/chat_com").on("connection", function (socket) {
socket.on('message', function (message) {
message = JSON.parse(message);
for(var key in clients){
if(message.type == "userMessage"){
message.username = clients[key];
console.log('message : ', message);
socket.broadcast.send(JSON.stringify(message));
message.type = "myMessage";
socket.send(JSON.stringify(message));
}
}
});
});
Here is how it looks in browser: http://screencast.com/t/lshnfcGZ8E8
Here is the full code: https://gist.github.com/johannesMatevosyan/0b9f7e588338dbb6b7f5
I think you're creating an unnecessary overkill by using different namespaces. Here's a clearer working example achieving the same functionality:
server.js
var app = require("express")();
var server = require("http").Server(app);
var io = require("socket.io")(server);
var chat = io.of("/chat").on("connection", function(socket){
socket.on("set_name", function (data) {
socket.username = data.name;
socket.emit("name_set", data);
socket.emit("message", {
type :"serverMessage",
message :"Welcome!"
});
chat.emit("message", {
type :"serverMessage",
message : data.name + " has joined the room.!"
});
});
socket.on("message", function (message) {
message.username = socket.username;
chat.emit("message", message);
});
});
app.get("/", function (req, res) {
res.sendfile(__dirname + "/index.html");
});
server.listen(3000);
client.js
var socket = io.connect('http://localhost:3000/chat');
socket.on('name_set', function (data) {
$('#nameform').hide();
$('#messages').append('<div class="systemMessage">Hello ' + data.name + '</div>');
});
socket.on('message', function (message) {
var userNameHtml = message.username ? '<span class="name">' + message.username + ':</span>' : '';
$('#messages').append('<div class="' + message.type + '">' + userNameHtml + message.message + '</div>');
});
$(function () {
$('#setname').click(function () {
socket.emit("set_name", { name: $('#nickname').val() });
});
$('#send').click(function () {
socket.emit("message", {
message : $('#message').val(),
type : 'userMessage'
});
$('#message').val('');
});
});
I don't think you need a separate event handler for user_entered, since you are treating it as a regular message and not doing anything else with the event. Also a couple of things:
You don't need to first connect to the server and then to the namespace address, connecting to the later is just fine.
Don't set event listeners within callbacks, that will result in setting them multiple times.

Nodejs server stop accept connections after a few hours

I made an application to the control of simultaneous logins, in starts everything works perfectly, however after a few hours i can not longer connect to the server, my client returns me the following error: net :: ERR_CONNECTION_TIMED_OUT and on the server side does not happen any error like it was running correctly... code below:
CLIENT SIDE:
var socket;
function connect(id) {
socket = io.connect('http://IP:4200');
socket.on('connect', function (data) {
socket.emit('join', id);
});
socket.on('messages', function (data) {
console.log('MSG: ' + data.toString());
switch (data.toString()) {
case "kick":
socket.close();
console.log("KICK!");
break;
case "duplicate_entry":
socket.close();
console.log("Another user connection!");
break;
}
});
}
SERVER SIDE:
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var clients = [];
app.use(express.static(__dirname + '/bower_components'));
function logtimestamp() {
var log_date = new Date();
log_date = '[' + log_date.getFullYear() + '/' + (log_date.getMonth() + 1) + '/' + log_date.getDate() + ' ' + log_date.getHours() + ':' + log_date.getMinutes() + ':' + log_date.getSeconds() + ']';
return log_date;
}// FUNCTION logtimestamp
console.log("Start time: " + logtimestamp());
console.log("Server port 4200")
console.log("websocket server created!");
try {
io.on('connection', function (client) {
try {
var id;
var conexao;
client.on('join', function (data) {
try {
console.log('Client connected...'+logtimestamp()+' ID:' + data);
id = data;
conexao = {
ws: client,
id_user: data
};
clients.push(conexao);
for (var x = 0; x < clients.length; x++) {
//desconect previous user
try {
if (clients[x].id_user == id) {
if (clients[x].ws != conexao.ws) {
clients[x].ws.emit('messages', 'duplicate_entry');
clients.splice(x, 1);
}
}
} catch (err) {
console.log("ERROR 1: " + err.message);
}
}
} catch (err) {
console.log("ERROR 2: " + err.message);
}
});
} catch (err) {
console.log("ERROR 3: " + err.message);
}
});
} catch (err) {
console.log("ERROR 4: " + err.message);
}
server.listen(4200);
I see a couple possible issues. It is hard for us to know by just inspecting code which issues actually are the cause of your issue. In any case, you should clean up these issues and see if it improves the situation:
You should respond the the disconnect event and immediately remove any socket from your clients array when it disconnects.
In your loop where you are looking to removing any prior instances of a given user, your for loop will not work properly when you are doing .splice(x, 1) in the middle of the for loop. This will move all items after it does one in the array causing you to skip the comparison of the next element in the array. One simple way to get around this is to iterate the array backwards: for (var x = clients.length - 1; x >= 0; x--) because then the elements who's position are affected after the .splice() are elements you have already looked at. None will be missed.
Beyond this, you should examine the memory usage of the nodejs process, the open sockets by the nodejs process and the CPU usage of the nodejs process to see if any of those point to any possible issues.
And, what is your deployment environment? Is there a proxy server in front of your web server?

Duplicate Events Socket.io and Node.js over STOMP

I need some help about my node.js+socket.io implementation.
This service expose a server that connects to an ActiveMQ broker over the STOMP protocol, using the stomp-js node.js module to receive events; that then are displayed in a web front end through websockets using socket.io.
So, everything was fine until I started use the Filters feature of ActiveMQ, but this was not the failure point because of my and my team researching, we found the way to ensure the implementation was fine, the problem comes with the connections: So here's the thing, I receive the filters to subscribe, I successfully subscribe to but when I receive a new set of filters is when comes the duplicated, triplicated and more and more messages depending the number of times that I subscribe-unsubscribe to.
So making some debug, I cannot see what's the problem but I'm almost sure that is some bad implementation of the callbacks or the program flow, I'll attach my code to read your comments about it.
Thanks a lot!
var sys = require('util');
var stomp = require('stomp');
var io = require('socket.io').listen(3000);
var socket = io.sockets.on('connection', function (socket) {
var stomp_args = {
port: 61616,
host: 'IP.ADDRESS',
debug: true,
};
var headers;
var client = new stomp.Stomp(stomp_args);
var setFilters = false;
socket.on('filtros', function (message) {
console.log('DEBUG: Getting filters');
if(setFilters){
client.unsubscribe(headers);
}
else{
client.connect();
}
var selector = '';
headers = '';
for(var attributename in message){
console.log(attributename+" : " + message[attributename]);
if(message[attributename] != ''){
selector += ' ' + attributename + '=\'' + message[attributename] + '\' AND ';
}
}
selector = selector.substring(0, selector.length - 4)
console.log('DEBUG: Selector String: ' + selector);
headers = {
destination: '/topic/virtualtopic',
ack: 'client',
selector: selector
};
if(setFilters)
client.subscribe(headers);
client.on('connected', function() {
client.subscribe(headers);
console.log('DEBUG: Client Connected');
setFilters = true;
});
});
var bufferMessage;
client.on('message', function(message) {
console.log("Got message: " + message.headers['message-id']);
var jsonMessage = JSON.parse(message.body);
if(bufferMessage === jsonMessage){
console.log('DEBUG: recibo un mensaje repetido');
return 0;
}
else{
console.log('DEBUG: Cool');
socket.emit('eventoCajero', jsonMessage);
}
client.ack(message.headers['message-id']);
bufferMessage = jsonMessage;
});
socket.on('disconnect', function(){
console.log('DEBUG: Client disconnected');
if(setFilters){
console.log('DEBUG: Consumer disconnected');
client.disconnect();
}
});
client.on('error', function(error_frame) {
console.log(error_frame.body);
});
});
Looking in the Socket.IO documentation, I've found that this is a known issue (I think critical known issue) and they have not fixed it yet. So, to correct this is necessary to reconnect to the socket in the client side to avoid duplicate messages, using:
socket.socket.reconnect();
function to force reconnection explicitly.

Categories