Aweary
Aweary

Reputation: 2312

A simple publish/subscribe system using an event emitter

This is from the book Node.JS in Action, chapter 3, example 11. Page 52.

var events = require('events');
var net = require('net');


var channel = new events.EventEmitter();
channel.clients = {};
channel.subscriptions = {};

//Add a listener for the join event that stores a user’s client object, 
//allowing the application to send data back to the user.

channel.on('join', function(id, client) {
    console.log('join fired');
    channel.clients[id] = client;
    this.subscriptions[id] = function(senderId, message) {
        console.log('broadcast fired');
            //ignore data if it’s been directly broadcast by the user.
            if (id != senderId) {
                this.clients[id].write(message);
            }
        }
        //Add a listener, specific to the current user, for the broadcast event.
    this.on('broadcast', this.subscriptions[id]);

});

var server = net.createServer(function(client) {
    var id = client.remoteAddress + ':' + client.remotePort;
    client.on('connect', function() {
        console.log('connect fired');
        //Emit a join event when a user connects to the server, specifying the user ID and client object.
        channel.emit('join', id, client);
    });
    client.on('data', function(data) {
        console.log('data fired');
        data = data.toString();
        //Emit a channel broadcast event, specifying the user ID and message, when any user sends data.
        channel.emit('broadcast', id, data);
    });
});
server.listen(8888);

It stats that with this chat application:

"If you open up a few command lines, you’ll see that anything typed in one command line is echoed to the others."

I added the console.log() events to try and debug what was going on. The only log is get is the 'data fired' when sending a message. I'm able to launch the server, and connect to it via telnet, but any messages entered do not echo to any of the clients (including the client sending the message).

Can anyone shed some light on:

  1. Why this doesn't work
  2. Whether this is efficient or recommended code structure
  3. How it can be refined/corrected

Upvotes: 3

Views: 6137

Answers (1)

mscdex
mscdex

Reputation: 106736

There is no connect event for incoming sockets (passed to the createServer() callback). When the callback is called, the socket is already connected. So this is an error in the book then.

You should also be careful of this usage.

IMHO here is a better example:

var events = require('events');
var net = require('net');

var channel = new events.EventEmitter();
channel.clients = {};
channel.subscriptions = {};

channel.on('join', function(id, client) {
  channel.clients[id] = client;
  channel.subscriptions[id] = function(senderId, message) {
    if (id !== senderId)
      channel.clients[id].write(message);
  }
  channel.on('broadcast', channel.subscriptions[id]);
}).on('leave', function(id, client) {
  // cleanup on client disconnect
  console.log('user ' + id + ' has left');
  delete channel.clients[id];
  channel.removeListener('broadcast', channel.subscriptions[id]);
  delete channel.subscriptions[id];
});

var server = net.createServer(function(client) {
  var id = client.remoteAddress + ':' + client.remotePort;

  console.log('user ' + id + ' has joined');

  channel.emit('join', id, client);

  client.on('data', function(data) {
    channel.emit('broadcast', id, data.toString());
  }).on('close', function() {
    channel.emit('leave', id, client);
  });
});
server.listen(8888);

A further improvement would be to have only one broadcast event handler which loops over all connected sockets instead of adding a new broadcast event handler for every single socket.

Upvotes: 3

Related Questions