Node.js with ZeroMQ + Socket.io + scoping per client

I'm writing a web application using node.js & express to provide a real-time aprs stream to each user via ZeroMQ & socket.io.

The concept is: the node.js app has the full aprs stream sent to it via subscribing to the ZeroMQ publisher. When a user visits the web page (which is a full-screen map,) a socket.io session gets set up and sends the boundaries of the current visible area of the map back to the server. The server can then scope the full feed to only send new spots that are on the map to the user.

Most of it is working well, but it breaks when more than one person loads the site. The scope of the current viewed map becomes global and if you zoom/pan one map, it will start showing spots on the new location to both browsers.

I need each socket.io session to retain its own scope and only receive the data from its unique scoped map boundaries and be unique to each visitor. I've tried nesting the zmqsocket.on "message", (data) -> callback within the socket.io connection, but it still produces the same result (even with the mapbounds variable declared within the proper callbacks.)

The full source for the application can be found here: https://github.com/gmcintire/aprs.bz

If you want to test the app locally, you should be able to clone my repo, run npm install, then coffee app.coffee to start. Just be aware it will start streaming the full APRS feed from my ZeroMQ server, which could be ~100KBytes/sec.

app.coffee

express = require("express")
routes = require("./routes")
geolib = require("geolib")
zmq = require("zmq")
zmqsocket = zmq.socket("sub")

zmqsocket.connect "tcp://stream.aprs.bz:12777"
zmqsocket.subscribe ""
app = module.exports = express.createServer()
io = require("socket.io").listen(app)

mapbounds = ""

io.sockets.on "connection", (socket) ->
  #console.log socket
  socket.on "mapmove", (mapcoords) ->
    console.log mapbounds
    mapbounds = mapcoords

zmqsocket.on "message", (data) ->
  packet = JSON.parse(data)
  #console.log packet

  if packet.latitude? and packet.longitude? and (mapbounds != "")
    insideMap = geolib.isPointInside
      latitude: packet.latitude
      longitude: packet.longitude
    , [
      latitude: mapbounds._northEast.lat
      longitude: mapbounds._southWest.lng
    ,
      latitude: mapbounds._northEast.lat
      longitude: mapbounds._northEast.lng
    ,
      latitude: mapbounds._southWest.lat
      longitude: mapbounds._northEast.lng
    ,
      latitude: mapbounds._southWest.lat
      longitude: mapbounds._southWest.lng
    ]
    if insideMap
      console.log "\n--------------------------------------------------------\n"
      io.sockets.emit "packet", packet
      console.log packet

app.configure ->
  app.set "views", __dirname + "/views"
  app.set "view engine", "jade"
  app.use express.bodyParser()
  app.use express.methodOverride()
  app.use app.router
  app.use express.static(__dirname + "/public")

app.configure "development", ->
  app.use express.errorHandler(
    dumpExceptions: true
    showStack: true
  )

app.configure "production", ->
  app.use express.errorHandler()

app.get "/", routes.index
if app.settings.env == "development"
  app.listen 3000
else
  app.listen 80

console.log "Express server listening on port %d in %s mode", app.address().port, app.settings.env

I'm not a CS or ZMQ guy either but it sounds like you're broadcasting data to too many clients. io.sockets.emit will emit the event to all connected sockets. If you want to send data to only 1 socket, you should use socket.emit where socket has been set to a single socket object.

I have no experience with Coffee, but I can see you are storing your variables globally.

var mapboudns = 10
io.sockets.on('connection', function(socket){
    socket.on("msg", function(data){
      mapbounds = data.bounds; //new value of mapbounds is altered globally
    });
});

try using something like:

socket.set("mapbounds", someNewValue);

and to get the value later:

socket.get("mapbounds");

or use a collection to map each socket to its related variables