58 votes

node-websocket-server : est-il possible d'avoir plusieurs "broadcasts" séparés pour un seul processus node.js ?

J'aimerais savoir s'il est possible de diffuser sur différentes "connexions" websocket à partir d'une même node-websocket-server exemple d'application. Imaginez un serveur de chatroom avec plusieurs salles, diffusant uniquement des messages aux participants spécifiques à chaque salle, sur un seul processus de serveur node.js. J'ai réussi à mettre en œuvre une solution de type "une salle de discussion par processus", mais je veux passer à la vitesse supérieure.

53voto

Shripad K Points 6594

Vous aimeriez probablement essayer Push-it : http://github.com/aaronblohowiak/Push-It qui est construit au-dessus de Socket.IO. La conception est conforme au protocole de Bayeux.

Cependant, si vous avez besoin de quelque chose qui utilise redis pubsub vous pouvez vérifier http://github.com/shripadk/Socket.IO-PubSub

Pour répondre spécifiquement à votre question : Vous pouvez maintenir un tableau de tous les clients connectés au serveur websocket. Et probablement juste diffuser à un sous-ensemble de ces clients ? La méthode de diffusion fait essentiellement cela sous le capot. node-websocket-server/Socket.IO maintient un tableau de tous les clients connectés et boucle sur chacun d'eux en "envoyant" un message à chacun des clients. L'essentiel du code :

// considering you storing all your clients in an array, should be doing this on connection:
clients.push(client)

// loop through that array to send to each client
Client.prototype.broadcast = function(msg, except) {
      for(var i in clients) {
          if(clients[i].sessionId !== except) {
             clients[i].send({message: msg});
          }
      }
}

Ainsi, si vous voulez relayer des messages uniquement vers des canaux spécifiques, il suffit de maintenir une liste de tous les canaux souscrits par le client. Voici un exemple simple (pour vous aider à démarrer) :

clients.push(client);

Client.prototype.subscribe = function(channel) {
      this.channel = channel;
}

Client.prototype.unsubscribe = function(channel) {
     this.channel = null;
}

Client.prototype.publish = function(channel, msg) {
      for(var i in clients) {
         if(clients[i].channel === channel) {
            clients[i].send({message: msg});
         }
      }
}

Pour rendre les choses encore plus faciles, utilisez les EventEmitters. Ainsi, dans node-websocket-server/Socket.IO, on voit où les messages sont reçus, on analyse le message pour vérifier le type (abonnement/désabonnement/publipostage) et on émet l'événement avec le message en fonction du type. Exemple :

Client.prototype._onMessage = function(message) {
       switch(message.type) {
         case 'subscribe':
             this.emit('subscribe', message.channel);
         case 'unsubscribe':
             this.emit('unsubscribe', message.channel);
         case 'publish':
             this.emit('publish', message.channel, message.data);
         default:

       }
}

Écoutez les événements émis dans le on('connection') de votre application :

client.on('subscribe', function(channel) {
     // do some checks here if u like
     client.subscribe(channel);
});
client.on('unsubscribe', function(channel) {
     client.unsubscribe(channel);
});
client.on('publish', function(channel, message) {
     client.publish(channel, message);
});

J'espère que cela vous aidera.

26voto

Shawn Mclean Points 16701

Je ne suis pas sûr que chambres étaient une fonctionnalité lorsque les autres réponses ont été créées, mais dans la documentation, ils ont une fonctionnalité correspondant exactement à ce que vous recherchez. Allez donc sur ce lien et cherchez rooms .

Voici un exemple tiré du site :

var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.join('justin bieber fans');
  socket.broadcast.to('justin bieber fans').emit('new fan');
  io.sockets.in('rammstein fans').emit('new non-fan');
});

D'après les autres réponses, l'accent était mis sur la mise à l'échelle. J'aimerais savoir si la version intégrée est aussi bien mise à l'échelle que les réponses proposées.

9voto

Sean Colombo Points 430

La réponse de Shripad K est très bien structurée. Bon travail.

Je pense que cette solution aura quelques problèmes d'échelle cependant.

Si vous avez 10 000 utilisateurs simultanés dans 500 salles de discussion, chaque fois qu'un utilisateur envoie un message, vous devez passer en revue les 10 000 clients. Je pense qu'il serait plus rapide de stocker la liste des clients d'une salle donnée dans une structure de redis et de simplement récupérer cette liste pour l'envoyer à ces clients.

1) Je ne suis pas sûr que ce soit vraiment plus rapide. 2) Pas sûr de ce qui pourrait être stocké dans redis et qui nous permettrait de référencer les clients. Peut-être qu'il pourrait y avoir un hash de tous les clients dans le serveur, par un id unique et dans redis, nous pourrions simplement stocker un ensemble de l'id utilisateur par salle de chat ?

Cela vous semble-t-il plus évolutif ?

J'ai écrit un serveur de chat node basé sur fzysqr's et j'ai besoin de le rendre extensible pour plusieurs chats avant de le déployer largement.

1voto

Volodymyr Krupach Points 312

Avec les chambres, mon chat de test simple ressemble à

chat.js :

var app = require('http').createServer(handler)
  , io = require('socket.io').listen(app)
  , fs = require('fs')

app.listen(80);

function handler (req, res) {
  fs.readFile(__dirname + '/chat.html',
  function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading chat.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

io.sockets.on('connection', function (socket) {

    socket.on('join', function (room) {
        if (Array.isArray(room)) {
            var i;
            for (i = 0; i < room.length; ++i) {
                console.log('join room ' + room[i]);
                socket.join(room[i]);
            }
        } else if (typeof room === 'string') {
            console.log('join room ' + room);
            socket.join(room);
        }
    });

    socket.on('leave', function (room) {
        if (typeof room === 'string') {
            console.log('leave room ' + room);
            socket.leave(room);
        }
    });

    socket.on('post', function (data) {
        io.sockets.in(data.room).emit('publish', data);
    });

});

et chat.html :

<html>
<head>
<title>Node js test</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script src="http://127.0.0.1:80/socket.io/socket.io.js"></script>
</head>
<body>
<h2>Node js test</h2>

<div style="height:400px;">
    <div id="controls" style="height:400px; display: inline-block; width:20%; float:left; background-color:lightyellow;">
        <input id="room1_check" type="checkbox" value="room_1" checked /><label for="room1_check">Room 1</label><br/><br/>
        <input id="room2_check" type="checkbox" value="room_2" /><label for="room2_check">Room 2</label><br/><br/>
        <input id="room3_check" type="checkbox" value="room_3" checked /><label for="room3_check">Room 3</label><br/><br/>
        <input id="room4_check" type="checkbox" value="room_4" /><label for="room4_check">Room 4</label><br/><br/>
        <input id="room5_check" type="checkbox" value="room_5" /><label for="room5_check">Room 5</label><br/><br/>
    </div>

    <div id="stream" style="height:400px; display: inline-block; width:40%; background-color:white; overflow:auto;"></div>

    <div id="post" style="height:400px; display: inline-block; width:40%; float:right; background-color:yellow;">
        <label for="postRoom">Room: </label>
        <select id="postToRoom">
            <option value="room_1">Room 1</option>
            <option value="room_2">Room 2</option>
            <option value="room_3">Room 3</option>
            <option value="room_4">Room 4</option>
            <option value="room_5">Room 5</option>
        </select>
        <br/><br/>
        <label for="postBy">By: </label>
        <select id="postBy">
            <option value="User 1">User 1</option>
            <option value="User 2">User 2</option>
            <option value="User 3">User 3</option>
            <option value="User 4">User 4</option>
            <option value="User 5">User 5</option>
        </select>
        <br/><br/>
        <label for="postMessage">Message:</label><br/>
        <textarea id="postMessage" style="width:80%; height:100px;" ></textarea>
        <br/><br/>
        <input id="postBtn" type="button" value="post message" />
    </div>

</div>

<script>
    var socket = io.connect('http://127.0.0.1:80');

    var checkedRooms = [];
    $('#controls :checked').each(function() {
        checkedRooms.push($(this).val());
    });
    socket.emit('join', checkedRooms);

    socket.on('publish', function (post) {
        //console.log(data);
        $("#stream").html($("#stream").html() + "room: " + post.room + "<br/>");
        $("#stream").html($("#stream").html() + "by: " + post.by + "<br/>");
        $("#stream").html($("#stream").html() + "on: " + post.on + "<br/>");
        $("#stream").html($("#stream").html() + "message: " + unescape(post.message) + "<br/>");
        $("#stream").html($("#stream").html() + "=============================================<br/>");
    });

    $('#controls :checkbox').change(function () {
        socket.emit(this.checked ? 'join' : 'leave', $(this).val());
     });

    $("#postBtn").click(function() {
        socket.emit('post', {room: $("#postToRoom").val(), message: escape($("#postMessage").val()), by: $("#postBy").val(), on: (new Date() + "") });
    });

</script>

</body>
</html>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X