2015-07-09 11 views
12

E 'possibile rendere socket.io trasmesso a tutti gli utenti di uno spazio dei nomi che si trovano sia nella stanza A che nella stanza B ma non quelli che si trovano nella stanza A o stanza B?socket.io trasmesso solo agli utenti che si trovano nella stanza A e B

In caso contrario, come dovrei implementare questo personalmente? C'è un modo per recuperare tutti gli utenti in uno spazio dei nomi che si trovano in una determinata stanza?

Sto lavorando con socket.io 1.0 in nodo

Edit: Se non esiste un metodo nativo, come potrei fare per creare la mia propria sintassi come ad esempio: socket.broadcast.in('room1').in('room2').emit(...)?

+0

vedere questo: http://stackoverflow.com/a/24145381/3842050 Potrebbe aiutare . – Blubberguy22

+0

puoi farlo lato client inviando ad entrambi e solo reagendo al 2 ° arrivo sullo stesso cliente ... – dandavis

risposta

5

È possibile cercare tutti gli utenti di una stanza utilizzando (ref How to update socket object for all clients in room? (socket.io))

var clients = io.sockets.adapter.room["Room Name"] 

Quindi, dato due matrici per la lista roster tuoi 2 camere, è possibile calcolare l'incrocio usando qualcosa come la risposta qui (ref: Simplest code for array intersection in javascript)

e infine si può prendere quella lista di utenti in entrambe le camere ed emettono gli eventi utilizzando (ref: How to update socket object for all clients in room? (socket.io))

//this is the socket of each client in the room. 
var clientSocket = io.sockets.connected[clientId]; 

//you can do whatever you need with this 
clientSocket.emit('new event', "Updates"); 

L'alternativa è di avere stanze nascoste, dove si mantiene una combinazione di tutte le stanze e di aggiungere utenti a quelle stanze dietro le quinte, e quindi si può semplicemente emettere in quelle stanze nascoste. Ma questo soffre di un problema di crescita esponenziale.

+0

Grazie! Questa è un'informazione molto preziosa. Sai come potrei implementare questo tipo di comportamento nel prototipo socket.io? Questo è qualcosa che userò per tutto il mio codice, quindi sarebbe davvero semplice se potessi semplicemente chiamare, per esempio, "socket.broadcast.toAll ([...]). Emit (...)" e avere il La funzione 'toAll()' cattura l'unione di tutte le stanze ed emette solo quelle. –

0
io.sockets.adapter.room["Room A"].forEach(function(user_a){ 
    io.sockets.adapter.room["Room B"].forEach(function(user_b){ 
    if(user_a.id == user_b.id){ 
     user_a.emit('your event', { your: 'data' }); 
    } 
    }); 
}); 
1

Non esiste un modo per farlo. Quindi, prima diamo un'occhiata a come funziona la trasmissione:

https://github.com/Automattic/socket.io/blob/master/lib/namespace.js 206 ... 221-224 ... 230

this.adapter.broadcast(packet, { 
    rooms: this.rooms, 
    flags: this.flags 
}); 

Ora sappiamo ogni trasmissione crea un gruppo di oggetti temporanei, le ricerche indexOf, argomenti fette ... E poi chiama il metodo di trasmissione dell'adattatore. Diamo un'occhiata a che uno:

https://github.com/Automattic/socket.io-adapter/blob/master/index.js 111-151

Ora ci stanno creando ancora più oggetti temporanei e scorrere tutti i clienti nelle camere o tutti i clienti se c'è spazio è stato selezionato. Il ciclo si verifica nel callback di codifica. Tale metodo può essere trovato qui:

https://github.com/socketio/socket.io-parser/blob/master/index.js

Ma cosa succede se non stiamo inviando i nostri pacchetti tramite broadcast ma ad ogni cliente separatamente dopo il ciclo attraverso le stanze e trovare clienti che esistono sia nella stanza A e B camera?

socket.emit è definito qui: https://github.com/Automattic/socket.io/blob/master/lib/socket.js

che ci porta al metodo del client.jspacket: https://github.com/Automattic/socket.io/blob/master/lib/client.js

Ciascun pacchetto direttamente emessa verranno codificati separatamente, che a sua volta, è costoso. Perché stiamo inviando lo stesso identico pacchetto a tutti gli utenti.

Per rispondere alla tua domanda:

O cambiare la classe di adattatori socket.io e modificare il metodo di trasmissione, aggiungere i propri metodi per il prototipo o rotolare il proprio adattatore ereditando dalla classe adattatore). (var io = require('socket.io')(server, { adapter: yourCustomAdapter });)

Oppure sovrascrivere i metodi join e leave dello socket.js. Il che è piuttosto conveniente considerando che quei metodi non vengono chiamati molto spesso e non si ha il fastidio di modificare attraverso più file.

Socket.prototype.join = (function() { 
    // the original join method 
    var oldJoin = Socket.prototype.join; 

    return function(room, fn) { 

     // join the room as usual 
     oldJoin.call(this, room, fn); 

     // if we join A and are alreadymember of B, we can join C 
     if(room === "A" && ~this.rooms.indexOf("B")) { 
      this.join("C"); 
     } else if(room === "B" && ~this.rooms.indexOf("A")) { 
      this.join("C"); 
     } 
    }; 
})(); 

Socket.prototype.leave = (function() { 
    // the original leave method 
    var oldLeave = Socket.prototype.leave; 

    return function(room, fn) { 

     // leave the room as usual 
     oldLeave.call(this, room, fn); 

     if(room === "A" || room === "B") { 
      this.leave("C"); 
     } 
    }; 
})(); 

E poi trasmesso al C se si desidera trasmettere a tutti gli utenti in A e B. Questo è solo un codice di esempio, è possibile migliorare ulteriormente non codificando con difficoltà i nomi delle room, ma usando una matrice o un oggetto invece di eseguire il loop su possibili combinazioni di stanze.

come adattatore personalizzato per rendere socket.broadcast.in("A").in("B").emit() lavoro:

var Adapter = require('socket.io-adapter'); 

module.exports = CustomAdapter; 

function CustomAdapter(nsp) { 
    Adapter.call(this, nsp); 
}; 

CustomAdapter.prototype = Object.create(Adapter.prototype); 
CustomAdapter.prototype.constructor = CustomAdapter; 

CustomAdapter.prototype.broadcast = function(packet, opts){ 
    var rooms = opts.rooms || []; 
    var except = opts.except || []; 
    var flags = opts.flags || {}; 
    var packetOpts = { 
    preEncoded: true, 
    volatile: flags.volatile, 
    compress: flags.compress 
    }; 
    var ids = {}; 
    var self = this; 
    var socket; 

    packet.nsp = this.nsp.name; 
    this.encoder.encode(packet, function(encodedPackets) { 
    if (rooms.length) { 
     for (var i = 0; i < rooms.length; i++) { 
     var room = self.rooms[rooms[i]]; 
     if (!room) continue; 
     for (var id in room) { 
      if (room.hasOwnProperty(id)) { 
      if (~except.indexOf(id)) continue; 
      socket = self.nsp.connected[id]; 
      if (socket) { 
       ids[id] = ids[id] || 0; 
       if(++ids[id] === rooms.length){ 
       socket.packet(encodedPackets, packetOpts); 
       } 
      } 
      } 
     } 
     } 
    } else { 
     for (var id in self.sids) { 
     if (self.sids.hasOwnProperty(id)) { 
      if (~except.indexOf(id)) continue; 
      socket = self.nsp.connected[id]; 
      if (socket) socket.packet(encodedPackets, packetOpts); 
     } 
     } 
    } 
    }); 
}; 

E nel file app:

var io = require('socket.io')(server, { 
    adapter: require('./CustomAdapter') 
}); 
+0

Questo sembra essere l'approccio più promettente. Non ho avuto il tempo di provarlo, ma ti darò la taglia prima che scada per il tuo lavoro e il tuo sforzo. –

+0

Grazie! :) Ci sono anche altri approcci che potresti provare. Per esempio. iterando su tutti gli utenti in 'A', cercando se sono anche in' B' (usando indexOf sulla loro proprietà 'room') e poi codificando il tuo messaggio per primo e inviandolo tramite' socket.packet' invece di 'socket. emit'. Non ho provato nemmeno il codice, quindi fammi sapere se qualcosa non funziona come previsto. –

+0

Lo farà! Avrò del tempo libero alla fine di questa settimana. Il mio problema principale era l'aggiunta delle mie funzioni al prototipo di socket.io –