2

Please excuse any noobiness, I'm learning. :)

I have Socket.IO set up so that I can use io.sockets.emit inside of my routes, and I have that working. There are a few problems.

  1. (SOLVED? SEE EDIT 3) To use, I cannot start with the word socket. I have to start with ioor I get "ReferenceError: socket is not defined." I'd like to be able to use socket.broadcast.emit to emit the event to all clients except for the current user. Right now I'm having to do a check on the client side to not execute the event if it's the current user and it's becoming a real headache as I'm having to emit more events as my project progresses.

  2. (SOLVED, SEE EDIT 1 & 2) I have to run the application with node app.js and restart the server manually every time I make a server-side change. When I run nodemon, I get "Port 3000 is already in use." I feel that this must be related to the following...

  3. (SOLVED, SEE EDIT 2) When pushing to Heroku, I have the port from the code below changed from 3000 to 80 in bin/www and app.js, but it does not work (I can see a 404 error for sockets in the console). If this and #2 are caused by dealing with http/ports in both places, how do I properly set this up and why does node app.js work?

  4. I only need to run Socket.IO on the route shown below (battlefield). Am I already doing this with am I already doing this with require('./routes/battlefield')(io)?

bin/www

var app = require('../app');
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
var server = http.createServer(app);
server.listen(port);

app.js

var app = express();
var http = require('http').Server(app);
http.listen(3000);
var io = require('socket.io')(http);

app.set('socketio', io);
var battlefield = require('./routes/battlefield')(io);

battlefield.js

var express = require('express');
var router = express.Router();

var returnRouter = function(io) {
  router.get('/', function(req, res, next) {
    // other stuff
    io.sockets.emit('message', 'This works');
    socket.broadcast.emit('message', 'Socket is undefined');
  })
  return router;
};
module.exports = returnRouter;

I tried wrapping my routes in io.on('connection', function (socket) { to be able to use socket, and instead of 'Socket is undefined,' the event does not occur.

var returnRouter = function(io) {
  io.on('connection', function (socket) {
    router.get('/', function(req, res, next) {
      // other stuff
      socket.emit('message', 'This is never emitted');
    })
  })
  return router;
};

I apologize for such a lengthy question. THANK YOU for any help!


EDIT1: Writing out this question helped me understand the problem better. I commented out server.listen(port); in my bin/www and nodemon now works. However, the app crashes on Heroku. My Procfile is web: node ./bin/www... does that need to be changed?

EDIT2: After figuring out Edit1 and a bit of Googling, I found that I can't have server.listen(); (bin/www) and http.listen(3000); (app.js).

In bin/www, I removed server.listen();.

In app.js, for clarity's sake I changed var http = ... to var server = ... and had it listen for process.env.PORT || '3000';, taken from bin/www. I also removed app.set('socketio', io); because it looks like that was doing nothing... I wonder why it was in there.

app.js

var app = require('express')();

var server = require('http').Server(app);
var io = require('socket.io')(server);
var port = process.env.PORT || '3000';
server.listen(port);

This also makes Heroku work because of process.env.PORT, hurray! I'm guessing node app.js worked because I was initializing the app with app.js, I guess bin/www is not executed when you do that?

I still need help with #1 (using socket.broadcast.emit) .

EDIT 3: Well, it took me literally the entire day but I believe I have it figured out with one quirk. Of course I couldn't use socket, it is a parameter given on connect. I also need to access socket across different routes and found this SO question. This is what I ended up doing in battlefield.js:

var returnRouter = function(io) {
  var socket;
  router.get('/', authenticatedUser, function(req, res, next) {
    io.on('connection', function(client){
      socket = client;
    });

    // other stuff
    res.render('battlefield', {/* data */});
    setTimeout(function(){socket.emit('test', 'It works!')}, 500);
  });

  router.post('/', function(req, res, next) {
    // socket can be accessed
  });
  return router;
};
module.exports = returnRouter;

(Note: I took out a lot of my own code so I don't know if this is copy and pasteable ready, and you should probably check that socket is not null)

Without setTimeout, socket is undefined on GET '/'. To my understanding, the page must render first... Strange that 200 sometimes doesn't work for me and 500 does. I can leave it at 500, but this is for a game so time is pretty important.


My questions now are:

Can this be improved / is there a way I can do this without setTimeout? Am I 'connecting' clients properly with this code and am I (question #4 up there^) using Socket.IO efficiently?

P.S. If no one answers ^ these questions, I'll edit this, answer the question, and accept my answer as best answer.

Community
  • 1
  • 1
LZoog
  • 51
  • 4

1 Answers1

2

When you use sockets when doing routing in Node its not that useful.

When ever you navigate to a different name space (eg www.example.com --> www.example.com/some-name-space) your front end variables are deleted and you need to resend them. This works great if you pass an object along with the GET request for that name space. But it doesn't need sockets.

Its done like this on your router file

var canAlsoBePassed = {some: "things"};
router.get('/', function(req, res, next) {
   res.render('index', { items: "Can be passed directly", variables: canAlsoBePassed });
});

For sockets the best kind of applications are for single page apps or to replace AJAX requests. Another great thing sockets allows is for the server to be able to push information without the client asking for it.

To answer your question about SetTimeout, no you dont need this.

Make sure the socket script running on your client side is waiting for the document to be loaded.

$(document).ready(function() {

When an io.on('connection' event fires on your server side you know you have a new client to serve.

emit an event from the server side something like a welcome event that makes the client join a specific room. Once you have them in that room you can be listening for any events emitted to that room.

See socket.io official info

Custom namespaces To set up a custom namespace, you can call the of function on the server-side:

var nsp = io.of('/my-namespace');
nsp.on('connection', function(socket){
  console.log('someone connected'):
});
nsp.emit('hi', 'everyone!');
On the client side, you tell Socket.IO client to connect to that namespace:

var socket = io('/my-namespace');

Might not be the most accurate answer to your questions but I hope it pushes you in the right direction.