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.
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