I'm giving my first steps with node.js. I'm trying to implement a simple chat room to get the basics, but can't figure out why after every broadcast, a second (empty) message is automatically sent. This behaviour happens most of the time but not always. I'm using Netbeans in Windows, with Putty to simulate the client connections.
Console output:
Server running
::1:60616 joined
::1:60617 joined
-----------
Broadcasting:
hello
-----------
Broadcasting:
-----------
Broadcasting:
goodbye
-----------
Broadcasting:
-----------
Client 1:
Hi ::1:60616!
hello
goodbye
Client 2:
Hi ::1:60617!
::1:60616 says hello::1:60616 says
::1:60616 says goodbye::1:60616 says
Code
var net = require('net');
var chatServer = net.createServer();
var clientList = [];
console.log('Server running');
chatServer.on('connection', function (client) {
client.name = client.remoteAddress + ':' + client.remotePort;
client.write('Hi ' + client.name + '!\n');
console.log(client.name + ' joined');
clientList.push(client);
client.on('data', function (data) {
console.log('Broadcasting: ');
console.log(data.toString());
console.log('-----------');
broadcast(data, client);
});
client.on('end', function () {
console.log(client.name + ' quit');
clientList.splice(clientList.indexOf(client), 1);
});
client.on('error', function (e) {
console.log(e);
});
});
function broadcast(message, sender) {
var text = sender.name + " says " + message;
var cleanup = [];
clientList.forEach(function (client) {
if (client !== sender) {
if (client.writable) {
client.write(text);
} else {
cleanup.push(client);
client.destroy();
}
}
});
cleanup.forEach(function (client) {
clientList.splice(clientList.indexOf(client), 1);
});
}
chatServer.listen(9000);
You can't rely on the raw data event to present you with "well-rounded" chunks of data. It may come in pieces, and you can't necessarily control how large those pieces are, or that they get split on particular boundaries.
However, there are modules that can help you, for instance split, which will split the data into separate (full) lines:
const split = require('split');
...
client.pipe(split()).on('data', function (data) {
// `data` is now a separate line
console.log('Broadcasting: ');
console.log(data.toString());
console.log('-----------');
broadcast(data, client);
});
Related
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 + '.');
});
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.
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?
I'm trying to do the following on a Raspberry PI
I connected a Parallax RFID reader to a USB slot of the Raspberry PI.
I can connect to the reader, and read the tags that pass by.
I use the following code to read a RFID tag.
var serialport = require("serialport");
var SerialPort = serialport.SerialPort;
var serialPort = new SerialPort("/dev/ttyUSB0", {
baudrate: 2400,
parser: serialport.parsers.readline("\n")
}, false); // this is the openImmediately flag [default is true]
serialPort.open(function () {
console.log('open');
serialPort.on('data', function(data) {
console.log('data received: ' + data);
});
});
Result of this code is a console that shows the RFID tag content in real time.
However, I want the reader to just read the first tag he gets, and then close down the connection.
How can I do this? The serial port stays open now, and constantly writes data to the console.
---- EDIT ------
With the following code, my connection closes down after a single read. However, the data console.log shows an empty variable. I think I need to skip the first entry....
// Variables for connecting
var serialport = require("serialport");
var SerialPort = serialport.SerialPort;
// Variable containing technical USB port details
var serialPort = new SerialPort("/dev/ttyUSB0", {
baudrate: 2400,
parser: serialport.parsers.readline("\n")
}, false); // this is the openImmediately flag [default is true]
serialPort.open(function () {
console.log('open');
serialPort.on('data', function(data) {
console.log('data received: ' + data);
});
serialPort.close(function () {
console.log('closing');
});
});
If you really only want to read one line (like posted in your comments) try to modify your code like this:
// make connection using readline as provided in your example
var line = null;
serialPort.open(function () {
console.log('open');
serialPort.on('data', function(data) {
console.log('data received: ' + data);
if (data.trim() !== '') {
line = data;
serialPort.close(function () {
console.log('closing');
});
}
});
});
// now you may work with your "one" line:
console.log('line strored: ' + line);
This only fetches the first non-empty line received of course.
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.