My question is am i possible to run the socket.io lib without using express? The thing is i want to make node as an external web socket server which just receives sockets connection and callbacks and just simply reply to them, not to make own routes or send page view (I'm using codeigniter for that work).
My current test app is like this on Server:
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var port = process.env.PORT || 4000;
server.listen(port, function() {
console.log('Server listening at port %d', port);
});
io.on("connection", function (socket) {
console.log('A new socket has joined: ' + socket.id);
var tweet = {user: "nodesource", text: "Hello, world!"};
// to make things interesting, have it send every second
var interval = setInterval(function () {
socket.emit("tweet", tweet);
}, 1000);
socket.on("disconnect", function () {
clearInterval(interval);
});
});
On Client:
<script>
const socket = io('http://localhost:4000/node_server');
socket.on('disconnect',function(){
alert('Im not connected, server is down');
});
socket.on("tweet", function(tweet) {
// todo: add the tweet as a DOM node
console.log("tweet from", tweet.username);
console.log("contents:", tweet.text);
});
My problem is that i have tested with express the chat example of socket.io and it works ofc but they use route and send the page and in my case i just want my other external page to communicate with node and not node sending me the page. Basically when i trigger some emit or function at server or client it does not fire just on server the connection but nothing else (p.s: also used io.sockets.on and doesn't work too)
If anyone has passed this and knows what my problem might be, i'll be glad.
Okay let's start off with something really basic here is our express server which is only hosting our socket application:
var app = require("express")();
var server = require("http").createServer(app);
var io = require("socket.io")(server);
var port = process.env.PORT || 4000;
server.listen(port, function() {
console.log("Server listening at port %d", port);
});
io.on("connection", function(socket) {
console.log("A new socket has joined: " + socket.id);
socket.on("hello", function(data) {
console.log(data);
});
});
You already understand that much but, it's important to note that this server will listen for any socket connections from any address. This is important to keep in mind.
Now let's look at the client html file
<html>
<body>
<button id="hiBtn">Say Hi to your server</button>
<!-- You only need to include the client file here -->
<script src="https://rawgit.com/socketio/socket.io-client/master/dist/socket.io.js" </script>
</script>
<script>
const serverLocation = "localhost:4000" // or whatever your server location is
const socket = io(serverLocation);
window.onload = function () {
document.getElementById("hiBtn").addEventListener("click", function () {
socket.emit("hello", "Hi there, this is the client speaking");
})
}
</script>
</body>
</html>
Notice how I do not have <script src="/socket/socket.io"> this is because this html file is being hosted on a completely separate client. You need to simply include the client socket.io file here which is usually located in node_modules\socket.io-client\dist\socket.io.js if you installed it via NPM. Or you can use the url I provided in my example. Just make sure that serverLocation points to your express server and you're all set.
P.S. for this example I tested it by hosting the html file on port:5000 and the express server on port:4000 if you were curious.
I would like to setup websocket without necessarily having to return the index.html file
Am still new to the socket io and this is what i have tried
installed socket io via
npm install socket.io --save
created index.js
var http = require('http');
var fs = require('fs');
// Loading socket.io
var io = require('socket.io');
// When a client connects, we note it in the console
io.sockets.on('connection', function (socket) {
console.log('A client is connected!');
});
server.listen(1100);
Now when i run node index am getting an error
io.sockets.on('connection', function (socket) {
^
TypeError: Cannot read property 'on' of undefined
What am trying to do is connect the websocet to my vuejs client side so ive skipped the part to display html part since i dont want to display html but to use the socket events.
where am i going wrong?
Hey you need to attach socket.io to an http server for your code to work and listen to incoming events.
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io').listen(server);
//io.on is the shorter form of io.sockets.on
io.on('connection', function(socket){
console.log('user connected');
});
If I have my express.js server set up as such:
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var port = process.env.PORT || 8080;
server.listen(8080);
And elsewhere I have a io.on('connnect', function(socket){...}); , should I be putting emit/event listeners on that socket within the io.connect(...) callback, or on the io object?
My basic program flow is as follows:
Client socket emits get_new_number.
Server receives get_new_number from unique socket.
Based on that unique socket ID, it emits new_number
The client listens for new_number and logs the data within to the console when triggered.
On the server-side, for part 2, I believe that must be a within the io.on('connect'...) function, so I can access socket.id for use later :
io.on('connect', function(socket){
socket.on('get_new_number', function(){
console.log('server got request for new number.');
globalVarId = socket.id;
});
});
But from there, where should I emit a new number to that unique socket? Say I save the socket.id and then emit to only it, how can I do that? The below isn't working:
io.on('connect', function(socket){
//get_new_number stuff
socket.to(globalVarId).emit('new_number', {number: someNumber});
});
Should I instead later do a io.to(globalVarId).emit... ?
EDIT: For that matter, is there a way to put event listeners on the io object, such like
io.on('someEvent', function(socket){
console.log('this socket did a thing, ', socket.id);
});
You can assume that socket within the callback function represents a unique client/connection.
So when you want to communicate with that client, you can use socket.emit():
io.on('connect', function(socket) {
socket.on('get_new_number', function() {
console.log('server got request for new number.');
//get_new_number stuff
socket.emit('new_number', { number: someNumber });
});
});
.to(...).emit(...) is used for something else entirely (sending a message to a particular room).
I have two servers, one for the back end app, and one that serves the front end. They are abstracted, but share the same database, I have a need for both to communicate real time events between each other using socket.io.
Front end
// serves a front end website
var appPort = 9200;
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
io.sockets.on('connection', function(socket) {
socket.on('createRoom', function(room) {
socket.join(room); // use this to create a room for each socket (room) is from client side
});
socket.on('messageFromClient', function(data) {
console.log(data)
socket.broadcast.to(data.chatRoom).emit('messageFromServer', data);
});
});
Back end
//Serves a back end app
var appPort = 3100;
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
io.sockets.on('connection', function(socket) {
socket.on('createRoom', function(room) {
socket.join(room); // use this to create a room for each socket (room) is from client side
});
socket.on('messageFromClient', function(data) {
console.log(data)
socket.broadcast.to(data.chatRoom).emit('messageFromServer', data);
});
});
As an admin I want to log in to my back end where I can see all the people logged in, there I can click on whom I would like to chat with.
Say they are logged in to the front end website, when the admin submits a message client side they trigger this socket.emit('messageFromClient', Message); how can I trigger messageFromClient on the front end using port 9200 submitting from the backend port 3100?
You really dont need to start the socket.io server in the front end for this use case.
The way you can get it to work is :
Keep the backend as it is, which acts as a Socket.IO server
In the font end connect to the server using a Socket.IO client.
You can install the client by calling
npm install socket.io-client
and then connect to the server using :
var io = require('socket.io-client'),
socket = io.connect('localhost', {
port: 3100
});
socket.on('connect', function () { console.log("socket connected"); });
socket.emit('messageFromClient', { user: 'someuser1', msg: 'i am online' });
You can then create a map of socket objects with their corresponding username and send a message to that user based on your business logic.
More Information :
You may have to add cases like client disconnection etc. You can read more about the call backs for that here :
https://github.com/Automattic/socket.io-client
Tearing my hair out with this one... has anyone managed to scale Socket.IO to multiple "worker" processes spawned by Node.js's cluster module?
Lets say I have the following on four worker processes (pseudo):
// on the server
var express = require('express');
var server = express();
var socket = require('socket.io');
var io = socket.listen(server);
// socket.io
io.set('store', new socket.RedisStore);
// set-up connections...
io.sockets.on('connection', function(socket) {
socket.on('join', function(rooms) {
rooms.forEach(function(room) {
socket.join(room);
});
});
socket.on('leave', function(rooms) {
rooms.forEach(function(room) {
socket.leave(room);
});
});
});
// Emit a message every second
function send() {
io.sockets.in('room').emit('data', 'howdy');
}
setInterval(send, 1000);
And on the browser...
// on the client
socket = io.connect();
socket.emit('join', ['room']);
socket.on('data', function(data){
console.log(data);
});
The problem: Every second, I'm receiving four messages, due to four separate worker processes sending the messages.
How do I ensure the message is only sent once?
Edit: In Socket.IO 1.0+, rather than setting a store with multiple Redis clients, a simpler Redis adapter module can now be used.
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
The example shown below would look more like this:
var cluster = require('cluster');
var os = require('os');
if (cluster.isMaster) {
// we create a HTTP server, but we do not use listen
// that way, we have a socket.io server that doesn't accept connections
var server = require('http').createServer();
var io = require('socket.io').listen(server);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
setInterval(function() {
// all workers will receive this in Redis, and emit
io.emit('data', 'payload');
}, 1000);
for (var i = 0; i < os.cpus().length; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
}
if (cluster.isWorker) {
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
io.on('connection', function(socket) {
socket.emit('data', 'connected to worker: ' + cluster.worker.id);
});
app.listen(80);
}
If you have a master node that needs to publish to other Socket.IO processes, but doesn't accept socket connections itself, use socket.io-emitter instead of socket.io-redis.
If you are having trouble scaling, run your Node applications with DEBUG=*. Socket.IO now implements debug which will also print out Redis adapter debug messages. Example output:
socket.io:server initializing namespace / +0ms
socket.io:server creating engine.io instance with opts {"path":"/socket.io"} +2ms
socket.io:server attaching client serving req handler +2ms
socket.io-parser encoding packet {"type":2,"data":["event","payload"],"nsp":"/"} +0ms
socket.io-parser encoded {"type":2,"data":["event","payload"],"nsp":"/"} as 2["event","payload"] +1ms
socket.io-redis ignore same uid +0ms
If both your master and child processes both display the same parser messages, then your application is properly scaling.
There shouldn't be a problem with your setup if you are emitting from a single worker. What you're doing is emitting from all four workers, and due to Redis publish/subscribe, the messages aren't duplicated, but written four times, as you asked the application to do. Here's a simple diagram of what Redis does:
Client <-- Worker 1 emit --> Redis
Client <-- Worker 2 <----------|
Client <-- Worker 3 <----------|
Client <-- Worker 4 <----------|
As you can see, when you emit from a worker, it will publish the emit to Redis, and it will be mirrored from other workers, which have subscribed to the Redis database. This also means you can use multiple socket servers connected the the same instance, and an emit on one server will be fired on all connected servers.
With cluster, when a client connects, it will connect to one of your four workers, not all four. That also means anything you emit from that worker will only be shown once to the client. So yes, the application is scaling, but the way you're doing it, you're emitting from all four workers, and the Redis database is making it as if you were calling it four times on a single worker. If a client actually connected to all four of your socket instances, they'd be receiving sixteen messages a second, not four.
The type of socket handling depends on the type of application you're going to have. If you're going to handle clients individually, then you should have no problem, because the connection event will only fire for one worker per one client. If you need a global "heartbeat", then you could have a socket handler in your master process. Since workers die when the master process dies, you should offset the connection load off of the master process, and let the children handle connections. Here's an example:
var cluster = require('cluster');
var os = require('os');
if (cluster.isMaster) {
// we create a HTTP server, but we do not use listen
// that way, we have a socket.io server that doesn't accept connections
var server = require('http').createServer();
var io = require('socket.io').listen(server);
var RedisStore = require('socket.io/lib/stores/redis');
var redis = require('socket.io/node_modules/redis');
io.set('store', new RedisStore({
redisPub: redis.createClient(),
redisSub: redis.createClient(),
redisClient: redis.createClient()
}));
setInterval(function() {
// all workers will receive this in Redis, and emit
io.sockets.emit('data', 'payload');
}, 1000);
for (var i = 0; i < os.cpus().length; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
}
if (cluster.isWorker) {
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var RedisStore = require('socket.io/lib/stores/redis');
var redis = require('socket.io/node_modules/redis');
io.set('store', new RedisStore({
redisPub: redis.createClient(),
redisSub: redis.createClient(),
redisClient: redis.createClient()
}));
io.sockets.on('connection', function(socket) {
socket.emit('data', 'connected to worker: ' + cluster.worker.id);
});
app.listen(80);
}
In the example, there are five Socket.IO instances, one being the master, and four being the children. The master server never calls listen() so there is no connection overhead on that process. However, if you call an emit on the master process, it will be published to Redis, and the four worker processes will perform the emit on their clients. This offsets connection load to workers, and if a worker were to die, your main application logic would be untouched in the master.
Note that with Redis, all emits, even in a namespace or room will be processed by other worker processes as if you triggered the emit from that process. In other words, if you have two Socket.IO instances with one Redis instance, calling emit() on a socket in the first worker will send the data to its clients, while worker two will do the same as if you called the emit from that worker.
Let the master handle your heartbeat (example below) or start multiple processes on different ports internally and load balance them with nginx (which supports also websockets from V1.3 upwards).
Cluster with Master
// on the server
var express = require('express');
var server = express();
var socket = require('socket.io');
var io = socket.listen(server);
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
// socket.io
io.set('store', new socket.RedisStore);
// set-up connections...
io.sockets.on('connection', function(socket) {
socket.on('join', function(rooms) {
rooms.forEach(function(room) {
socket.join(room);
});
});
socket.on('leave', function(rooms) {
rooms.forEach(function(room) {
socket.leave(room);
});
});
});
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
// Emit a message every second
function send() {
console.log('howdy');
io.sockets.in('room').emit('data', 'howdy');
}
setInterval(send, 1000);
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
}
This actually looks like Socket.IO succeeding at scaling. You would expect a message from one server to go to all sockets in that room, regardless of which server they happen to be connected to.
Your best bet is to have one master process that sends a message each second. You can do this by only running it if cluster.isMaster, for example.
Inter-process communication is not enough to make socket.io 1.4.5 working with cluster. Forcing websocket mode is also a must. See WebSocket handshake in Node.JS, Socket.IO and Clusters not working