Socket.IO handling disconnect event - javascript

Can't handle this disconnect event, don't know why socket is not sent to the client / client doesn't respond!
Server
io.sockets.on('connection', function (socket) {
socket.on('NewPlayer', function(data1) {
online = online + 1;
console.log('Online players : ' + online);
console.log('New player connected : ' + data1);
Players[data1] = data1;
console.log(Players);
});
socket.on('DelPlayer', function(data) {
delete Players[data];
console.log(Players);
console.log('Adios' + data);
});
socket.on('disconnect', function () {
socket.emit('disconnected');
online = online - 1;
});
});
Client
var socket = io.connect('http://localhost');
socket.on('connect', function () {
person_name = prompt("Welcome. Please enter your name");
socket.emit('NewPlayer', person_name);
socket.on('disconnected', function() {
socket.emit('DelPlayer', person_name);
});
});
As you can see when a client disconnects the Array object[person_name] should be deleted, but it's not.

Ok, instead of identifying players by name track with sockets through which they have connected. You can have a implementation like
Server
var allClients = [];
io.sockets.on('connection', function(socket) {
allClients.push(socket);
socket.on('disconnect', function() {
console.log('Got disconnect!');
var i = allClients.indexOf(socket);
allClients.splice(i, 1);
});
});
Hope this will help you to think in another way

For those like #sha1 wondering why the OP's code doesn't work -
OP's logic for deleting player at server side is in the handler for DelPlayer event,
and the code that emits this event (DelPlayer) is in inside disconnected event callback of client.
The server side code that emits this disconnected event is inside the disconnect event callback which is fired when the socket loses connection. Since the socket already lost connection, disconnected event doesn't reach the client.
Accepted solution executes the logic on disconnect event at server side, which is fired when the socket disconnects, hence works.

Create a Map or a Set, and using "on connection" event set to it each connected socket, in reverse "once disconnect" event delete that socket from the Map we created earlier
import * as Server from 'socket.io';
const io = Server();
io.listen(3000);
const connections = new Set();
io.on('connection', function (s) {
connections.add(s);
s.once('disconnect', function () {
connections.delete(s);
});
});

You can also, if you like use socket id to manage your player list like this.
io.on('connection', function(socket){
socket.on('disconnect', function() {
console.log("disconnect")
for(var i = 0; i < onlineplayers.length; i++ ){
if(onlineplayers[i].socket === socket.id){
console.log(onlineplayers[i].code + " just disconnected")
onlineplayers.splice(i, 1)
}
}
io.emit('players', onlineplayers)
})
socket.on('lobby_join', function(player) {
if(player.available === false) return
var exists = false
for(var i = 0; i < onlineplayers.length; i++ ){
if(onlineplayers[i].code === player.code){
exists = true
}
}
if(exists === false){
onlineplayers.push({
code: player.code,
socket:socket.id
})
}
io.emit('players', onlineplayers)
})
socket.on('lobby_leave', function(player) {
var exists = false
for(var i = 0; i < onlineplayers.length; i++ ){
if(onlineplayers[i].code === player.code){
onlineplayers.splice(i, 1)
}
}
io.emit('players', onlineplayers)
})
})

Related

How to use html5 websockets within sharedworkers

After checking similar questions on stackoverflow I did not find anything much helpful for what I want to do in my project. Reading and researching I successfully made the application work having multiple connections to my Ratchet PHP websocket server, but I noticed every time the user reloaded a page or opened a link in a new tab, the client websocket got disconnected and then reconnected again.
So, I wonder how to get only one persistent connection to a WebSocket Server, for multiple users, in a web application using a Sharedworker.
What I have in the client side is this:
<script>
$(document).ready(function($) {
let socket = new WebSocket("ws://realtime:8090");
socket.onopen = function(e) {
console.log("Browser client connected to websocket server");
socket.send("Greetings from the browser!");
};
socket.onmessage = function(event) {
console.log('Data received from server: ' + event.data);
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log(`Connection closed cleanly, code=${event.code} reason=${event.reason}`);
}
else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
console.log('Connection closed unexpectedly.');
}
};
socket.onerror = function(error) {
alert(error.message);
};
});
</script>
Ok after reading, researching and trying different things and code samples, I came to this solution:
The client side (browser) should have the connection to a Sharedworker.
The sharedworker is a separated javascript file containing the core of the sharedworker and whatever other JS code that needs to be executed within it.
I first tested the sharedworker to work fine with the browser tabs, counting the number of opened tabs per user and sharing messages to one user, and then to a group of users.
Once the communication between the browser and the Sharedworker passed those tests I added the websocket code to the body of the Sharedworker JS file.
In the end, the client side (browser) looks like this:
<script>
$(document).ready(function($) {
var currentUser = "{{ Auth::user()->name }}";
let worker = new SharedWorker('worker.js');
worker.port.start();
worker.port.postMessage({
action: 'connect',
username: currentUser
});
worker.port.onmessage = function(message) {
console.log(message.data);
};
});
</script>
The Sharedworker looks like this:
// All this code is executed only once, until the onconnect() function.
//---------------------------------------------------------------------
// The array AllPorts contains objects with the format {user:<string>, port:<MessagePort>}
let AllPorts = [];
var socket = new WebSocket("ws://ssa:8090");
// Called when the WebSocket Server accepts the connection.
socket.onopen = function(e) {
//
};
// Event handler fired when the WebSocket Server sends a message to this client.
socket.onmessage = function(e) {
var message = JSON.parse(e.data);
// This loop sends a message to each tab opened by the given user.
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].user == message.to) {
AllPorts[i].port.postMessage(message.msg);
}
}
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log('Connection closed normally');
}
else {
console.log('Connection closed unexpectedly.');
}
};
socket.onerror = function(error) {
console.log(error.message);
};
// This event handler is fired every time a new tab is opened on the web browser.
onconnect = function(ev) {
let port = ev.ports[0];
port.onmessage = function(e) {
console.log(e.data.action);
let currentUser = e.data.username;
let userIsConnected = false;
switch (e.data.action) {
case "connect":
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].user == currentUser) {
userIsConnected = true;
}
}
// Add new connected tab to AllPorts array.
AllPorts.push({user: currentUser, port: port});
if (!userIsConnected) {
// New users are added to the list of the WebSocket Server.
setTimeout(() => {
socket.send(JSON.stringify({action: 'connect', username: currentUser}));
}, 600);
}
break;
case "close":
console.log(AllPorts);
var index;
// This is also executed when the user reloads the Tab.
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].port == port) {
index = i;
currentUser = AllPorts[i].user;
}
}
AllPorts.splice(index, 1);
userIsConnected = false;
// Check for any connected tab.
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].user == currentUser) {
userIsConnected = true;
}
}
if (!userIsConnected) {
// User doen't have more tabs opened. Remove user from WebSocket Server.
socket.send(JSON.stringify({action: 'disconnect', username: currentUser}));
}
break;
case "notify":
// Check if given user is connected.
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].user == currentUser) {
userIsConnected = true;
}
}
if (userIsConnected) {
socket.send(JSON.stringify({action: 'notify', to: currentUser, message: e.data.message}));
}
} // switch
} // port.onmessage
} // onconnect

Working with multiple tabs with Socket.io

I've the following code working in my server-side, it's all ok. But, I want to keep the same connection between n tabs, because when I open a new tab, looks like I've disconnected from the first tab... So, how can I keep the same connection?
client.js
socket.emit("connected", {user: inputUser.val()};
app.js
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
users[socket.user] = socket;
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
users[userMsg].emit("typing", {user: socket.user});
}
});
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
delete users[socket.user];
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
Update:
The typing event above works fine... So I tried the typing event according to the answer:
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// add this socket to the Set of sockets for this user
if (!users[socket.user]) {
users[socket.user] = new Set();
}
users[socket.user].add(socket);
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
users[userMsg].emit("typing", {user: socket.user});
}
});
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// remove socket for this user
// and remove user if socket count hits zero
if (users[socket.user]) {
users[socket.user].delete(socket);
if (users[socket.user].size === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
But it is giving the following error:
users[userMsg].emit("typing", {user: socket.user});
^
TypeError: users[userMsg].emit is not a function
Update²:
To fix the typing event error, I just changed to:
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
for(let userSet of users[userMsg]) {
userSet.emit("typing", {user: socket.user});
}
}
});
There is no simple way to share a single socket.io connection among multiple tabs in the same browser. The usual model for multiple tabs would be that each tab just has its own socket.io connection.
The opening of a new tab and a new socket.io connection should not, on its own, cause your server to think anything was disconnected. If your code is doing that, then that is a fault in your code and it is probably easier to fix that particular fault.
In fact, if you want to explicitly support multiple tabs and be able to recognize that multiple tabs may all be used by the same user, then you may want to change your server side code so that it can keep track of multiple sockets for a single user, rather than how it is currently coded to only keep track of one socket per user.
If your server code is really just trying to keep track of which users online, then there's probably an easier way to do that by referencing counting each user. I will post a code example in a bit.
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// increment reference count for this user
if (!users[socket.user]) {
users[socket.user] = 0;
}
++users[socket.user];
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// decrement reference count for this user
// and remove user if reference count hits zero
if (users.hasOwnProperty(socket.user)) {
--users[socket.user];
if (users[socket.user] === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
If you need the users object to have the socket object in it, then you can change what is stored in the users object to be a Set of sockets like this:
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// add this socket to the Set of sockets for this user
if (!users[socket.user]) {
users[socket.user] = new Set();
}
users[socket.user].add(socket);
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// remove socket for this user
// and remove user if socket count hits zero
if (users[socket.user]) {
users[socket.user].delete(socket);
if (users[socket.user].size === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
For anyone still having this issue. here is how i fixed it.
let me explain.
once the page refreshes or a new tab is opened, socket dosen't really care so it opens a new connection every time . this is more of a advantage than disadvantage. the best way to tackle the issue is on the server side, once a user logs in with his or her user name , you can send that name along with the query options on the client so it can be used as a unique identifier. in my case i used a token
this.socket = io.connect(`${environment.domain}` , {
query: {token: this.authservice.authToken}
});
then on the server side you can create an empty array to a key and an array of values. the username of the user will be used as a key and the corresponding array of socket as the value. in my own case like i said i used a token
const users = [ ]
socket.nickname = (decoded token username);
users[socket.nickname] = [socket];
then you can perform a simple logic to check if a user already exists in an array, if it does, push the new socket to the array of the user
if ( user.username in users) {
console.log('already exists')
users[user.username].push(socket);
}
if it dosent, just create a new key and add the socket as the key.(make sure its an array because a user can always refresh or open a new tab with the same account and you dont want the chat message to deliver in one tab and not deliver in another)
else {
socket.nickname = username;
users[socket.nickname] = [socket];
}
then to emit a message you simply loop through the array and emit the message accordingly. this way each tab gets the message
socket.on('chat', (data) => {
if (data.to in users) {
for(let i = 0; i < users[data.to].length; i++) {
users[data.to][i].emit('chat', data)
}
for(let i = 0; i < users[data.user].length; i++) {
users[data.user][i].emit('chat', data)
}
}
})
you can add a disconnect logic to remove the socket from the users array too to save memory, so only currently open tabs acre active and closed tabs are removed. i hope it solved your problem
My solution is joining socket to a room with specific user Id.
io.on('connection', async (socket) => {
socket.join('user:' + socket.handshake.headers.uid) // The right way is getting `uid` from cookie/token and verifying user
})
One advantage is sending data to specific user (sending to all tabs)
io.to('user:' + uid).emit('hello');
Hope it's helpful!
I belive the best way is create a channel for the user and unique it by their ID, so, when you need to receive or send something you use the channel and every socket connected to it will receive.
Another solution is to save the flag to localStorage and use eventListener to change localStorage.
Do not connect when another connection exists.
and save message in local storage for send with master tab.

Disconnect event fired after cilent reconnect - socket.io

I'm using socket.io for realtime communication. When inetrnet is not available for few seconds, then it will connect to server when the connection is establish and then it will immediately trigger event which will log in my clinet. After establishing connection, (i think) my socket.io on server side fire disconnect event after ~one minute (in callback function I log off my client), and that is my problem.
This is my server side. (a have server.js and he run other.js files, in object 'files' a have that files).
server.js :
io = require('socket.io').listen(server);//server listening on 8080 port
io.configure(function () {
io.set('transports', ['websocket', 'xhr-polling']);
io.set('log level', 1);
io.disable('browser client');
});
var files = {
control: null,
terminal: null,
messages: null
};
for (var game in games) {
games[game] = require('./game_modules/' + game + '.js').listen(io, create_waiter(game));
}
require('http').createServer(function (req, res) {
var resultCode = 500;
var resultText = '';
var parts = require('url').parse(req.url, true);
var matches = parts.pathname.match('^/([a-z_-]+)/([a-z_-]+)/$');
if (matches) {
var game = matches[1];
var command = matches[2];
resultText = games[game][command](parts.query);
resultCode = 200;
res.writeHead(resultCode, {'Content-Type': 'text/plain'});
res.end(resultText);
}).listen(8081, "127.0.0.1");
control.js :
var control = module.exports = function() {
};
control.listen = function(io) {
this.server = io.of('/control');
this.onTerminate = onTerminate;
//********************************************************************************
this.server.on('connection', function(socket) {
socket.on('disconnect', function(data) {
console.log(data); //after my client reconnect, disconnect event is triggered, and
//console.log write 'close timeout'
After client reconnect, I do not want trigger disconnect event, or if disconnect event was triggered I want check in callback function: is my client connected and prevent "log off" for my client.
Thanks :)

Duplicate Events Socket.io and Node.js over STOMP

I need some help about my node.js+socket.io implementation.
This service expose a server that connects to an ActiveMQ broker over the STOMP protocol, using the stomp-js node.js module to receive events; that then are displayed in a web front end through websockets using socket.io.
So, everything was fine until I started use the Filters feature of ActiveMQ, but this was not the failure point because of my and my team researching, we found the way to ensure the implementation was fine, the problem comes with the connections: So here's the thing, I receive the filters to subscribe, I successfully subscribe to but when I receive a new set of filters is when comes the duplicated, triplicated and more and more messages depending the number of times that I subscribe-unsubscribe to.
So making some debug, I cannot see what's the problem but I'm almost sure that is some bad implementation of the callbacks or the program flow, I'll attach my code to read your comments about it.
Thanks a lot!
var sys = require('util');
var stomp = require('stomp');
var io = require('socket.io').listen(3000);
var socket = io.sockets.on('connection', function (socket) {
var stomp_args = {
port: 61616,
host: 'IP.ADDRESS',
debug: true,
};
var headers;
var client = new stomp.Stomp(stomp_args);
var setFilters = false;
socket.on('filtros', function (message) {
console.log('DEBUG: Getting filters');
if(setFilters){
client.unsubscribe(headers);
}
else{
client.connect();
}
var selector = '';
headers = '';
for(var attributename in message){
console.log(attributename+" : " + message[attributename]);
if(message[attributename] != ''){
selector += ' ' + attributename + '=\'' + message[attributename] + '\' AND ';
}
}
selector = selector.substring(0, selector.length - 4)
console.log('DEBUG: Selector String: ' + selector);
headers = {
destination: '/topic/virtualtopic',
ack: 'client',
selector: selector
};
if(setFilters)
client.subscribe(headers);
client.on('connected', function() {
client.subscribe(headers);
console.log('DEBUG: Client Connected');
setFilters = true;
});
});
var bufferMessage;
client.on('message', function(message) {
console.log("Got message: " + message.headers['message-id']);
var jsonMessage = JSON.parse(message.body);
if(bufferMessage === jsonMessage){
console.log('DEBUG: recibo un mensaje repetido');
return 0;
}
else{
console.log('DEBUG: Cool');
socket.emit('eventoCajero', jsonMessage);
}
client.ack(message.headers['message-id']);
bufferMessage = jsonMessage;
});
socket.on('disconnect', function(){
console.log('DEBUG: Client disconnected');
if(setFilters){
console.log('DEBUG: Consumer disconnected');
client.disconnect();
}
});
client.on('error', function(error_frame) {
console.log(error_frame.body);
});
});
Looking in the Socket.IO documentation, I've found that this is a known issue (I think critical known issue) and they have not fixed it yet. So, to correct this is necessary to reconnect to the socket in the client side to avoid duplicate messages, using:
socket.socket.reconnect();
function to force reconnection explicitly.

Dynamic Namespaces Socket.IO

How can I use dynamic namespaces in socket.io.
I'm looking in the (poor) documentation, and it says that namespaces must be used like this:
io.of('/news')
io.of('/akfda')
To use a namespace you do io.of("/namespace").
Do I need to register every single namespace in the server? Maybe I want a namespace for dynamic content.
How can I do something like :
io.of('/:somethign/:id')
Socket.IO supports 'rooms' (https://github.com/LearnBoost/socket.io/wiki/Rooms), you can use it instead of namespaces. Also when you need dynamic in routes (and you using express in your app) - best way is to use use route-engine from express box.
Best way to do dynamic routing with Express.js (node.js)
Using routes in Express-js
http://expressjs.com/api.html#app.routes
http://shtylman.com/post/expressjs-re-routing/
http://jordanhoff.com/post/22602013678/dynamic-express-routing
However, if you still think that you need dynamic in namespaces in socket.io, here is small example how it can be implemented:
User-side:
var connect = function (ns) {
return io.connect(ns, {
query: 'ns='+ns,
resource: "socket.io"
});
}
var socket = connect('/user/12');
Server-side:
var url = require('url');
, ev = new events.EventEmitter()
// <ns name>: <ns regexp>
var routes = {
// /user/:id
'user': '^\\/user\\/(\\d+)$',
// /:something/:id
'default': '^\\/(\\\w+)\\/(\\d+)$'
};
// global entry point for new connections
io.sockets.on('connection', function (socket) {
// extract namespace from connected url query param 'ns'
var ns = url.parse(socket.handshake.url, true).query.ns;
console.log('connected ns: '+ns)
//
for (var k in routes) {
var routeName = k;
var routeRegexp = new RegExp(routes[k]);
// if connected ns matched with route regexp
if (ns.match(routeRegexp)) {
console.log('matched: '+routeName)
// create new namespace (or use previously created)
io.of(ns).on('connection', function (socket) {
// fire event when socket connecting
ev.emit('socket.connection route.'+routeName, socket);
// #todo: add more if needed
// on('message') -> ev.emit(...)
});
break;
}
}
// when nothing matched
// ...
});
// event when socket connected in 'user' namespace
ev.on('socket.connection route.user', function () {
console.log('route[user] connecting..');
});
// event when socket connected in 'default' namespace
ev.on('socket.connection route.default', function () {
console.log('route[default] connecting..');
});
I hope this will help you!
I would use "rooms" to support your dynamic content.
Server Side
var server = require('http').createServer(),
io = require('socket.io')(server);
io.on('connection', function(socket){
var room = socket.handshake['query']['r_var'];
socket.join(room);
console.log('user joined room #'+room);
socket.on('disconnect', function() {
socket.leave(room)
console.log('user disconnected');
});
socket.on('chat message', function(msg){
io.to(room).emit('chat message', msg);
});
});
server.listen(3000);
Client Side
var socket_connect = function (room) {
return io('localhost:3000', {
query: 'r_var='+room
});
}
var random_room = Math.floor((Math.random() * 2) + 1);
var socket = socket_connect(random_room);
socket.emit('chat message', 'hello room #'+random_room);
....
As of version 2.1.1 I was able to make it work with this:
wss.of((nsp, query, next) => {
const { token } = query;
// Do your authentication or whatever here...
// If success
next(null, true);
}).on('connect', (socket) => {
// socket connected to your namespace
});
Server
var MAX_CLIENTS = 5;
var namespace_queue = [];
function searchObjectOnArray(nameKey, myArray) {
for (var i = 0; i < myArray.length; i++) {
if (myArray[i].id === nameKey) {
return myArray[i];
}
}
}
function createNamespace(data){
var ns = {
//id: require('node-uuid')(),
id : data.name,
clients: 0,
};
namespace_queue.push(ns);
return ns;
}
createNamespace({name: 'primer'});
io.of('').on('connection', function(socket){
console.log('-' + socket.id);
/// Welcome to the new client
socket.emit('Welcome', {SocketId : socket.id});
socket.on('JoinToApp', function (data, callback) {
var namespaceToConnect = searchObjectOnArray(data.namespace, namespace_queue)
if(namespaceToConnect.clients <= MAX_CLIENTS){
var dynamicNamespace = io.of('/' + namespaceToConnect.id);
dynamicNamespace.on('connection', function(ns_socket){
console.log('user connected to ' + namespaceToConnect.id);
dynamicNamespace.emit('hi', 'everyone!');
});
namespaceToConnect.clients++;
}
callback({namespaces:namespace_queue});
})
socket.on('createNamespace',function(data,join_cb){
createNamespace(data);
join_cb({message:'Namespace created'});
});
});
Client
<input id="namespaceInput" type="text" placeholder="New namespace name">
<input id="namespaceToConnect" type="text" placeholder="namespace to connect">
<button onclick="javascript: createNamespace()">Create Namespace</button>
<button onclick="javascript: joinToNamespace()">Connect to Namespace</button>
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script>
var socket = null;
(function(){
socket = io.connect('http://localhost:3000/');
})()
function createNamespace(){
var namespaceName = document.getElementById("namespaceInput").value;
socket.emit('createNamespace', {name : namespaceName}, function(data){
alert(data.message);
})
}
function joinToNamespace(){
var name = document.getElementById("namespaceToConnect").value;
socket.emit('JoinToApp', {namespace: name}, function(data){
console.log('Namespaces created:');
console.log(data)
var ns_socket = io.connect('http://localhost:3000/' + name);
ns_socket.on('connect',function(){
console.log('joined namespace ' + name);
});
ns_socket.on('hi', function(data){
console.log('hi ' + data)
})
});
}
</script>
More details on: https://ingcamilorodriguez.wordpress.com/2016/06/21/como-hacer-namespaces-dinamicos-en-socket-io/
Here is one way. Here is a socket.io subclass I created to solve the problem:
https://github.com/PencilCode/dynamic.io
That subclass adds dynamic namespaces as well as virtual hostname support (each host can go into its own namespace tree if you like). That repo has some examples.
Here is a universal socket.io listener that listens to every namespace requested, and logs a message for every socket that connects. You could listen to a different regexp to listen to any subset of namespaces.
It works with the standard socket.io client library without any modification.
var DynamicServer = require('dynamic.io');
io = DynamicServer({
host: true, // Enable virtual host handling
publicStatus: true // Enable /socket.io/status page.
});
// Any number of namespace patterns can be set up.
// This is an example of a single catch-all pattern.
io.setupNamespace(/.*/, function(nsp) {
nsp.on('connect', function(socket) {
console.log('a socket connected on', nsp.fullname());
});
nsp.expire(function() {
console.log(nsp.fullname(), 'is expiring');
});
});
io.listen(8888);
On newer versions you can use something like io.of(/^\/\w+$/).on('connection', (socket) where /^\/\w+$/ is a regular expression that will allow connection if it is a match.
Here is a full example on how to use this to setup many namespaces, here I suppose the only concerne is preventing emit diffusion from reaching other namespaces.
const workspaces = {}
io.of(/^\/\w+$/).on('connection', (socket) => {
const workspace = socket.nsp;
const namespace = workspace.name;
console.log("New Connection NameSpace", namespace);
// you can test here if "namespace" is allowed to be used
// if event handlers are set no need to got further
if (workspaces[namespace]) return;
// save workspace to prevent setting event handlers on each connection
workspaces[namespace] = workspace;
workspace.on("connection", (socket) => {
console.log(`${namespace} > connection from ${socket.id}`);
// set the event handlers same as normal socket
socket.on('event-1', (msg) => {
console.log("event-1", msg);
})
})
})

Categories