Usernames in a UDP chat server with NodeJS - node.js

I'm setting up a simple chat server with NodeJS that only uses a server and client. It works, and I can open up multiple client windows on the same machine, but now I need a bit more.
I would like to:
Give each client the option to set usernames
Have a client request current time from server
Action commands like "/me punches the warlock" that the server shows others as "User punches the warlock"
This sounds like a quick days work, but I just started looking at UDP and I can't quite find any examples online other than a generic server/client thing that sends and gets messages. How would I go about those tasks?
Code:
Server.js:
var dgram = require('dgram'); //import datagram to get everything needed for UDP
var PORT = 22222;
var CLIENT_PORT = 2223;
// An IP address that's reserved on each network
// Gets sent to the server
var ADDRESS = "-"; //dont want to show my IP :)
var sock = dgram.createSocket({reuseAddr: true, type: 'udp4'}); //can now open multiple clients
var current_time = Date.now(); //?
function sendMessage(data) {
sock.send(data, 0, data.length, PORT, ADDRESS, function(err){
if(err){
throw err;
}
});
}
sock.on("message", function(data, rinfo) {
//listen for messages and print them
console.log(data);
//Check if the client's port was equal to the port to find client data on
//If so, get packet
if (rinfo.port === CLIENT_PORT) {
console.log('\nreceived');
//call function to broadcast the data out to everyone on the local network
sendMessage(data);
}
//Get the string data from the data buffer.
var stringData = data.toString(); //also could be toJSON()
console.log(stringData);
//Convert that string back into a buffer by making a new Buffer and passing it in.
//The buffer class can take a string, an array or just a number of bytes to allocate to memory
var backToBuffer = new Buffer(stringData); //can take a string, array or just a size to allocate
console.log(backToBuffer);
});
//This opens the connection and starts listening
//(Client Port, Address to listen to which is ALL, What to do)
sock.bind(CLIENT_PORT, '', function(){
sock.setBroadcast(true);
console.log('listening on port ' + PORT + "\n");
});
Client.js:
var dgram = require('dgram');
var SERVER_PORT = 22222;
var PORT = 22223;
var ADDRESS = "-";
//read input from the command line
var stdin = process.stdin;
var stdout = process.stdout;
var sock = dgram.createSocket({reuseAddr: true, type: 'udp4'});
var server_sock = dgram.createSocket({reuseAddr: true, type: 'udp4'});
function sendMessage(data) {
//onsole.log("sending data");
sock.send(data, 0, data.length, PORT, ADDRESS, function(err) {
if(err) {
throw err;
}
//nsole.log("sent");
});
}
server_sock.on("message", function(data, rinfo) {
console.log("received " + data.toString());
});
server_sock.bind(SERVER_PORT, '', function() {
console.log('listening to server port');
});
sock.bind(PORT, '', function() {
sock.setBroadcast(true);
console.log("please enter a message\n");
stdin.resume();
stdin.on("data", function(data) {
sendMessage(data);
});
});

Node.js has loads of libraries to make realtime communication chats, I don't understand the choice of using UDP and managing everything by yourself, and it also looks that you are not very expert on this. My suggestion is to use a websocket library like it can be socket.io which they even have an example of a simple chat in their website http://socket.io/get-started/chat/
Using socket.io (or similar) it will help you a lot since it has sessions so you can save the username and return it everytime you post a message.
Below I wrote a small example of the server side (took from the index.js of the get-started above).
io.on('connection', function(socket){
var username = 'RANDOM';
socket.on('chat message', function(msg){
//here you should catch messages starting with "/" and parse them to write a different message
io.emit('chat message', username + ': ' msg);
});
socket.on('update username', function(msg) {
username = msg;
io.emit('username changed', username);
});
});
I hope it helps.

Related

node-ipc error "Messages are large, You may want to consider smaller messages."

So I am trying to setup a socket server in node.js using node-ipc, then send data from a client. I can connect perfectly fine, however when I send data I recieve the error Messages are large, You may want to consider smaller messages. I have followed all advice here, however, have still been unsuccessful in resolving the problem. I have tried sending from a socket connection in C and also from terminal. Any advice would be great, thanks in advance.
main.js
const ipc = require("node-ipc");
const socket = '/tmp/edufuse.sock';
ipc.serve(
socket,
function(){
ipc.server.on(
'myEvent',
function(data){
ipc.log('got a message : '.debug, data);
}
);
}
);
ipc.server.start();
test.json
```json
{ type: message, data: { foo: bar } }
command from terminal
pr -tF test.json | nc -U /tmp/edufuse.sock
Unfortunately, it appears this is an underlying problem with node-ipc. In order to get around this, I used net sockets and TCP.
const net = require('net');
const port = 8080;
const host = '127.0.0.1';
var server = net.createServer(function(socket) {
socket.on('data', function(data){
let str = data.toString('utf8');
console.log(str);
try {
let json = JSON.parse(str);
console.log(json);
} catch (e) {
console.log('error str: ' + str);
}
});
socket.on('error', function(err) {
console.log(err)
})
});
server.listen(port, host);

NodeJS net package error when client writing to server

So I took the following code https://gist.github.com/tedmiston/5935757 example and modified it slightly such that the client writes data to the server. This should be doable since the client socket does support a write. In one of my use cases the client sends a fair amount of data from the client to server in which case I get an ECONNRESET error. Attached are client and server snippets. I was wondering if anyone has seen this and if they know what is going wrong under the covers.
Here is a copy of my client:
var net = require('net');
var client = new net.Socket({writeable: true}); //writeable true does not appear to help
client.on('close', function() {
console.log('Connection closed');
});
client.on('error', function(err) {
console.error('Connection error: ' + err);
console.error(new Error().stack);
});
client.connect(5900, '127.0.0.1', function() {
var count = 0;
console.log('Connected');
for(var i = 0; i < 100000; i++) {
client.write('' + i + '');
//bufferSize does not seem to be an issue
//console.info(client.bufferSize);
}
});
and my server:
var net = require('net');
var count = 0;
var server = net.createServer(function(socket) {
socket.pipe(socket); //With this uncommented I get an ECONNRESET exception after 14299 writes with it commented it hangs after 41020 writes
socket.on('data', function(data) {
console.info(count++); //This makes it occur sooner
//count++;
//maxConnections is not the issue
//server.getConnections(function(err, count) {
//console.info('count = ' + count);
//});
});
socket.on('close', function() {
console.info('Socket close');
});
socket.on('error', function(err) {
console.error('Socket error: ' + err + ', count = ' + count);
console.error(new Error().stack);
});
});
server.listen(5900, '127.0.0.1');
When the client has finished sending data in the for loop inside its connect event handler, there is nothing more for it to do so it exits. That terminates the connection, and the server gets an ECONNRESET to inform it that the connection has been broken.
If you want the client to hang around after it has finished sending then give it some reason to stay alive. Registering a data event handler on the client socket is one possibility.

use node.js cluster with socket.io chat application

I'm trying to learn node.js cluster with socket.io to create a chat application... the problem is that I can't seem to get things working.
i've been trying to go through all the tutorials including the one that I get from this http://stackoverflow.com/questions/18310635/scaling-socket-io-to-multiple-node-js-processes-using-cluster/18650183#18650183
when I try to open two browsers, the messages does not go to the other browser.
here's the code that i got
var express = require('express'),
cluster = require('cluster'),
net = require('net'),
socketio = require('socket.io'),
socket_redis = require('socket.io-redis');
var port = 3000,
num_processes = require('os').cpus().length;
if (cluster.isMaster) {
// This stores our workers. We need to keep them to be able to reference
// them based on source IP address. It's also useful for auto-restart,
// for example.
var workers = [];
// Helper function for spawning worker at index 'i'.
var spawn = function(i) {
workers[i] = cluster.fork();
// Optional: Restart worker on exit
workers[i].on('exit', function(code, signal) {
console.log('respawning worker', i);
spawn(i);
});
};
// Spawn workers.
for (var i = 0; i < num_processes; i++) {
spawn(i);
}
// Helper function for getting a worker index based on IP address.
// This is a hot path so it should be really fast. The way it works
// is by converting the IP address to a number by removing non numeric
// characters, then compressing it to the number of slots we have.
//
// Compared against "real" hashing (from the sticky-session code) and
// "real" IP number conversion, this function is on par in terms of
// worker index distribution only much faster.
var worker_index = function(ip, len) {
var s = '';
for (var i = 0, _len = ip.length; i < _len; i++) {
if (!isNaN(ip[i])) {
s += ip[i];
}
}
return Number(s) % len;
};
// Create the outside facing server listening on our port.
var server = net.createServer({ pauseOnConnect: true }, function(connection) {
// We received a connection and need to pass it to the appropriate
// worker. Get the worker for this connection's source IP and pass
// it the connection.
var worker = workers[worker_index(connection.remoteAddress, num_processes)];
worker.send('sticky-session:connection', connection);
}).listen(port);
} else {
// Note we don't use a port here because the master listens on it for us.
var app = new express();
// Here you might use middleware, attach routes, etc.
app.use('/assets', express.static(__dirname +'/public'));
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
// Don't expose our internal server to the outside.
var server = app.listen(),
io = socketio(server);
// Tell Socket.IO to use the redis adapter. By default, the redis
// server is assumed to be on localhost:6379. You don't have to
// specify them explicitly unless you want to change them.
io.adapter(socket_redis({ host: 'localhost', port: 6379 }));
// Here you might use Socket.IO middleware for authorization etc.
io.on('connection', function(socket) {
console.log('New client connection detected on process ' + process.pid);
socket.emit('welcome', {message: 'Welcome to BlueFrog Chat Room'});
socket.on('new.message', function(message) {
socket.emit('new.message', message);
})
});
// Listen to messages sent from the master. Ignore everything else.
process.on('message', function(message, connection) {
if (message !== 'sticky-session:connection') {
return;
}
// Emulate a connection event on the server by emitting the
// event with the connection the master sent us.
server.emit('connection', connection);
connection.resume();
});
}
If I understand correctly, your problem is that the messages from a client are not broadcasted to the other clients. you can solve this easily using :
io.on('connection', function(socket) {
console.log('New client connection detected on process ' + process.pid);
socket.emit('welcome', {message: 'Welcome to BlueFrog Chat Room'});
socket.on('new.message', function(message) {
socket.emit('new.message', message); // this line sends the message back to the emitter
socket.broadcast.emit('my message', msg); // this broadcasts the message to all the clients
})
});
There are different ways to emit a message. The one you're using emits the message only to the socket that first sent a 'new.message' message to the server. Which means that a socket will receive the message that you emit there only if it first sent a message 'new.message'. That's why, in your browser, the client originating the message is the only one receiving it back.
Change it to:
socket.on('new.message', function(message) {
io.sockets.emit('new.message', message);//use this if even the browser originating the message should be updated.
socket.broadcast.emit('new.message', message);//use this if everyone should be updated excpet the browser source of the message.
})
Here are the different ways you can emit:
io.sockets.on('connection', function(socket) {
//This message is only sent to the client corresponding to this socket.
socket.emit('private message', 'only you can see this');
//This message is sent to every single socket connected in this
//session, including this very socket.
io.sockets.emit('public message', 'everyone sees this');
//This message is sent to every single connected socket, except
//this very one (the one requesting the message to be broadcasted).
socket.broadcast.emit('exclude sender', 'one client wanted all of you to see this');
});
You can also add sockets to different rooms when they connect so that you only communicate messages with sockets from a given room:
io.sockets.on('connection', function(socket) {
//Add this socket to a room called 'room 1'.
socket.join('room 1');
//This message is received by every socket that has joined
//'room 1', including this one. (Note that a socket doesn't
//necessarily need to belong to a certain room to be able to
//request messages to be sent to that room).
io.to('room 1').emit('room message', 'everyone in room 1 sees this');
//This message is received by every socket that has joined
//'room 1', except this one.
socket.broadcast.to('room 1').emit('room message', 'everyone in room 1 sees this');
});

How to distinguish between node socket server / client packets on data event?

In node, when you create a socket server and connect to it with a client, the write function triggers the data event, but it seems there is no way to distinguish the source of the traffic (other than adding your own IDs/headers to each sent buffer).
For example, this is the output "server says hello" from the server.write, and then all of the "n client msg" are from client.write, and they all come out in on('data', fn):
➜ sockets node client.js
client connected to server!
client data: server says hello
client data: 1 client msg!
client data: 2 client msg!
client data: 3 client msg!
client data: 4 client msg!
Is there a correct way to distinguish the source of the data on a socket?
The code for a simple client:
// client.js
var net = require('net');
var split = require('split');
var client = net.connect({
port: 8124
}, function() {
//'connect' listener
console.log('client connected to server!');
client.write('1 client msg!\r\n');
client.write('2 client msg!\r\n');
client.write('3 client msg!\r\n');
client.write('4 client msg!\r\n');
});
client.on('end', function() {
console.log('client disconnected from server');
});
var stream = client.pipe(split());
stream.on('data', function(data) {
console.log("client data: " + data.toString());
});
and the code for the server
// server.js
var net = require('net');
var split = require('split');
var server = net.createServer(function(c) { //'connection' listener
console.log('client connected');
c.on('end', function() {
console.log('client disconnected');
});
c.write('server says hello\r\n');
c.pipe(c);
var stream = c.pipe(split());
stream.on('data', function(data) {
console.log("client data: " + data.toString());
});
});
server.listen(8124, function() { //'listening' listener
console.log('server bound');
});
The source of the traffic is the server.
If you're wanting to know whether it's data being echoed back to the client by the server, you will have to come up with your own protocol for denoting that.
For example, the server could respond with newline-delimited JSON data that is prefixed by a special byte that indicates whether it's an echo or an "original" response (or any other kind of "type" value you want to have). Then the client reads a line in, checks the first byte value to know if it's an echo or not, then JSON.parse()s the rest of the line after the first byte.
You can distinguish each client with:
c.name = c.remoteAddress + ":" + c.remotePort;
c.on('data', function(data) {
console.log('data ' + data + ' from ' + c.name);
});

Socket.io not sending a message to all connected sockets

I'm trying out node.js and socket.io. I wan't to use to remove a ping function I have to get updates from my server. Here is an example code of what I'm doing:
var app = require('http').createServer(),
io = require('socket.io').listen(app),
cp = require('child_process');
app.listen(8080);
//I check a global value for all the connected users from the php command line
var t = setInterval(function(){
cp.exec('/usr/bin/php /Users/crear/Projects/MandaFree/symfony api:getRemainingMessages',
function(err, stdout){
if (err) {
io.sockets.emit('error', 'An error ocurred while running child process.');
} else {
io.sockets.emit('change', stdout);
}
console.log('Remaining messages: ' + stdout);
});
}, 3000);
var remaining = io.of('/getRemainingMessages')
.on('connection', function(socket){
socket.on('disconnect', function(){});
});
The Issue here, is that when I call io.sockets.emit() the debug console tells me it is doing something, but it looks like it is not getting to the clients. Because they are doing nothing.
I use to have one interval for every connected client, and when I used socket.emit() it did worked. But it is not the optimal solution.
UPDATE:
Here is my client side code.
var remaining = io.connect('http://127.0.0.1:8080/getRemainingMessages');
remaining.on('change', function(data){
console.log('Remaining messages: ' + data );
$('#count').html(data);
});
remaining.on('error', function(error){
console.log(error);
});
Had a very similar issue couple of days back and looks like socket.io had some changes in the API. I have never worked with symfony and am hoping the issues are the same.
I have a working demo of socket.io sending and receiving a message - uploaded to https://github.com/parj/node-websocket-demo as a reference
Essentially two changes
On Server side - changed socket.on to socket.sockets.on
var socket = io.listen(server);
socket.sockets.on('connection', function(client)
On Client side - URL and port not required as it is autodetected.
var socket = io.connect();
This has been tested using Express 2.5.2 and Socket.io 0.8.7
I have amalgamated your server code with mine, would you be able to try this on the server and my client javascript and client html just to see if it is working?
var socket = io.listen(server);
socket.sockets.on('connection', function(client){
var connected = true;
client.on('message', function(m){
sys.log('Message received: '+m);
});
client.on('disconnect', function(){
connected = false;
});
var t = setInterval(function(){
if (!connected) {
return;
}
cp.exec('/usr/bin/php /Users/crear/Projects/MandaFree/symfony api:getRemainingMessages',
function(err, stdout){
if (err) {
client.send('error : An error ocurred while running child process.');
} else {
client.send('change : ' + stdout);
}
console.log('Remaining messages: ' + stdout);
});
}, 3000);
t();
});

Resources