I've spent quite a while trying to connect Socket.IO-Client to a server running on Sails.js framework. The client is basically a simple JavaScript application running with Node.js on Raspberry Pi.
The idea is that a simple device connects to a server, the server then registers the device and subscribes it for messages. The device receives a message from the server and performs some actions. I don't want the client to be dependant on any framework and, for that reason, I am trying to avoid using Sailsjs-socket.io-client. At the moment both server and the client are running on my local machine. Here is the code:
// Server side. Sails.js. DevicesController.js
module.exports = {
handshake: function (req, res) {
if (!req.isSocket) return res.badRequest();
Devices.create({}).exec(function (error, data) {
Devices.subscribe(req.socket, data);
});
return res.ok();
}
};
// Client side
var socket = require('socket.io-client')('http://localhost:1337/devices/handshake');
socket.on('error', function(e) {
console.log(e);
// Here I get 'Invalid namespace'
});
So, I get the error 'Invalid namespace' on the client side and, if I'm right, it means that the server doesn't have '/devices/handshake' open. However, on the server side if I list existing room ids (sails.sockets.rooms()), I can see that a new room is always created, when the client tries to connect to the server.
I tried to connect to '/devices/handshake' from a browser with the javascript below and apparently it worked. It obviously runs with Sailsjs-socket.io-client though.
io.socket.get('/devices/handshake', function (data, jwres) {
console.log(jwres);
});
io.socket.on('devices', function (data, jwres) {
console.log(data);
});
Any ideas what am I doing wrong?
It seems that you're confusing a few concepts here: namely Socket.io namespaces, Socket.io paths and Sails routes. You can learn a bit more about the first two by looking through the Socket.io docs. The main thing to keep in mind as far as Sails is concerned is that it only listens for connections on the default /
namespace; therefore, you should always connect client-side sockets with:
var socket = require('socket.io-client')('http://localhost:1337');
Adding the /devices/handshake
onto the end doesn't change the URL that the socket is attempting to connect to; it will always try to connect to http://localhost:1337/socket.io
. Instead, it means that it will try to register the socket with the /devices/handshake
namespace, which Sails isn't providing.
On the other hand, when you call io.socket.get('/devices/handshake')
, you're using the Sails socket client library to make a virtual request to that route in your app, just as if you'd used AJAX (e.g. $.get('/devices/handshake')
in jQuery). This is precisely why sails.io.js
was created--it makes this sort of thing very easy! It's also not really tying you down on the front-end; all sails.io.js
does is provide some wrappers to make it easy to communicate with Sails backends via sockets. Under the covers, io.socket.get
is simply using the Socket.io-client .emit()
method to send a get
event to the server, with a payload describing the URL and parameters of the Sails action to run. So, this:
io.socket.get('/devices/handshake', function(body, res){...})
is equivalent to connecting your own socket and doing this:
socket.emit('get', {url: '/devices/handshake'}, function(res){...})
The recommended approach for running logic for a socket when it first connects is to allow the socket to connect fully on the client, and then make a request from the client to the server (exactly what you're doing in your second code block). To read more about the reasoning behind this, see this note in the Sails 0.11.x migration guide. That note also explains that if you must run the logic immediately upon server connection for some reason, you can do
sails.io.on('connect', function (newlyConnectedSocket){})
in your bootstrap function (config/bootstrap.js
) .