Socket.io ve node.js ile belirli bir istemciye mesaj gönderin


191

Socket.io ve node.js ile çalışıyorum ve şimdiye kadar oldukça iyi görünüyor, ancak sunucudan belirli bir istemciye nasıl bir mesaj gönderileceğini bilmiyorum:

client.send(message, receiverSessionId)

Ama ne .send()ne de .broadcast()yöntemler benim ihtiyacımı karşılamıyor.

Ne olası bir çözüm olarak buldum, .broadcast()yöntem ikinci bir parametre olarak ileti göndermek değil SessionIds bir dizi kabul, bu yüzden bir anda sunucuya o anda bağlı tüm SessionIds ile bir dizi geçirebilir Mesajı göndermek isterdim, ama daha iyi bir çözüm olması gerektiğini hissediyorum.

Herhangi bir fikir?

Yanıtlar:


95

Bunun için müşteriyi (sürpriz) yakalamanız gerekiyor, ya basit yoldan gidebilirsiniz:

var io = io.listen(server);
io.clients[sessionID].send()

Hangisi kırılabilir, bundan şüpheliyim, ancak her zaman io.clientsdeğişebilecek bir olasılıktır , bu yüzden yukarıdakileri dikkatli kullanın

Veya istemcileri kendiniz takip edersiniz, bu nedenle onları dinleyicideki kendi clientsnesnenize ekler ve connectiondinleyicide kaldırırsınız disconnect.

İkincisini kullanırım, çünkü uygulamanıza bağlı olarak istemcilerde daha fazla durum olmasını isteyebilirsiniz, bu yüzden böyle bir şey clients[id] = {conn: clientConnect, data: {...}}işi yapabilir.


6
Ivo, daha eksiksiz bir örneğe işaret edebilir veya biraz ayrıntı verebilir misiniz? Bu yaklaşımı anlamaya hevesliyim, ancak bu örnekte kullandığınız değişkenleri / nesneleri tanıdığımdan emin değilim. [İd] = {conn: clientConnect istemcilerinde, data: {...}}, istemciler [id] yukarıdaki io.clients [sessionID] öğesinde görüldüğü gibi io nesnesinin bir parçası mı? Ayrıca clientConnect nesnesi nedir? Teşekkürler.
AndrewHenderson

@ ivo-wetzel merhaba, bu konuda beni sevebilir misin? stackoverflow.com/questions/38817680/…
Mehdi pishguy

178

Ivo Wetzel'in cevabı Socket.io 0.9'da artık geçerli görünmüyor.

Kısacası şimdi kaydetmeniz socket.idve io.sockets.socket(savedSocketId).emit(...) mesaj göndermek için kullanmanız gerekir.

Kümelenmiş Node.js sunucusunda bu çalışma var:

İlk olarak, iletilerin çapraz işlemlere girebilmesi için Redis deposunu mağaza olarak ayarlamanız gerekir:

var express = require("express");
var redis = require("redis");
var sio = require("socket.io");

var client = redis.createClient()
var app = express.createServer();
var io = sio.listen(app);

io.set("store", new sio.RedisStore);


// In this example we have one master client socket 
// that receives messages from others.

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

  // Promote this socket as master
  socket.on("I'm the master", function() {

    // Save the socket id to Redis so that all processes can access it.
    client.set("mastersocket", socket.id, function(err) {
      if (err) throw err;
      console.log("Master socket is now" + socket.id);
    });
  });

  socket.on("message to master", function(msg) {

    // Fetch the socket id from Redis
    client.get("mastersocket", function(err, socketId) {
      if (err) throw err;
      io.sockets.socket(socketId).emit(msg);
    });
  });

});

Kümeleme kodunu burada atladım, çünkü bu daha karmaşık hale getiriyor, ancak eklemek önemsiz. Her şeyi çalışan koduna ekleyin. Burada daha fazla doküman http://nodejs.org/api/cluster.html


4
Teşekkürler yardımcı oldu. Bunun yerine sadece bir dizi kullanmak zorunda kaldım: io.of('/mynamespace').sockets[socketID].emit(...)( bunun bir ad alanı kullanıp kullanmadığımı bilmiyorum)
Adrien Schuler

kümelenmiş bir ortamda, soketin ait olduğu doğru işlemin iletimi gönderdiğinden nasıl emin olabilirim?
Gal Ben-Haim

NGINX veya HAProxy @ Gal Ben-Haim'in izniyle yapışkan bir oturuma ne dersiniz?
matanster

var tutucu = yeni soket.RedisStore; ^ TypeError: undefined, Object öğesinde bir işlev değil. Moduleancompile'da (module.js: 460: 26) Function.Module'daki Module.load'da (module.js: 355: 32) Object.Module._extensions..js (module.js: 478: 10). runMain (module.js: 501: 10) başlangıçta (node.js: 129: 16) node.js'de: 814: 3
Lucas Bertollo

1
io.sockets.socket(socket_id)socket.io 1.0 içinde kaldırıldı. github.com/socketio/socket.io/issues/1618#issuecomment-46151246
ImMathan

98

her soket bir isim için soket kimliğine sahip bir odaya katılır, böylece

io.to(socket#id).emit('hey')

dokümanlar: http://socket.io/docs/rooms-and-namespaces/#default-room

Şerefe


7
Bu en iyi yanıttır ve socket.io'nun yeni sürümlerinde çalışır. Burada güzel bir hile sayfası var: stackoverflow.com/questions/10058226/…
harmanlanmış

4
bunun bir "etkinlik" yayınlama türü olduğunu unutmayın. bu nedenle buna bir geri arama ayarlamayı denerseniz bir hata alırsınız. bir olayı geri arama ile belirli bir sokete göndermeniz gerekiyorsa, @ PHPthinking'in yanıtını kullanın ve kullanın io.sockets.connected[socketid].emit();. 1.4.6 ile test edilmiştir.
tbutcaru

Ama bu hatayı aldım: io.to ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServe‌ r", bilgi); ^ TypeError: undefined 'emit' özelliği okunamıyor
Raz

85

En basit, en zarif çözüm

Bu kadar kolay:

client.emit("your message");

Ve bu kadar.

Ama nasıl? Bana bir örnek ver

Hepimizin ihtiyacı olan şey aslında tam bir örnektir ve işte bunu takip eder. Bu, en son socket.io sürümü (2.0.3) ile test edilmiştir ve ayrıca modern Javascript'i kullanmaktadır (şimdiye kadar hepimiz kullanmalıyız).

Örnek iki bölümden oluşmaktadır: bir sunucu ve bir istemci. İstemci her bağlandığında, sunucudan periyodik bir sıra numarası almaya başlar. Her yeni istemci için yeni bir dizi başlatılır, bu nedenle sunucunun bunları tek tek izlemesi gerekir. "Belirli bir müşteriye mesaj göndermem gerekiyor" işte burada devreye giriyor. Kodun anlaşılması çok basittir. Hadi onu görelim.

Sunucu

server.js

const
    io = require("socket.io"),
    server = io.listen(8000);

let
    sequenceNumberByClient = new Map();

// event fired every time a new client connects:
server.on("connection", (socket) => {
    console.info(`Client connected [id=${socket.id}]`);
    // initialize this client's sequence number
    sequenceNumberByClient.set(socket, 1);

    // when socket disconnects, remove it from the list:
    socket.on("disconnect", () => {
        sequenceNumberByClient.delete(socket);
        console.info(`Client gone [id=${socket.id}]`);
    });
});

// sends each client its current sequence number
setInterval(() => {
    for (const [client, sequenceNumber] of sequenceNumberByClient.entries()) {
        client.emit("seq-num", sequenceNumber);
        sequenceNumberByClient.set(client, sequenceNumber + 1);
    }
}, 1000);

Sunucu, gelen bağlantılar için 8000 numaralı bağlantı noktasını dinlemeye başlar. Birisi geldiğinde, sıra numarasını takip edebilmesi için yeni istemciyi haritaya ekler. Ayrıca, söz konusu istemcinin disconnectetkinliğini haritadan kaldırdığında da dinler .

Her saniye, bir zamanlayıcı tetiklenir. Bunu yaptığında, sunucu harita üzerinde dolaşır ve her istemciye geçerli sıra numarasıyla bir mesaj gönderir. Daha sonra onu artırır ve numarayı tekrar haritaya kaydeder. Hepsi bu kadar. Tereyağından kıl çeker gibi.

müşteri

Müşteri kısmı daha da basittir. Sadece sunucuya bağlanır ve seq-nummesajı her dinlediğinde konsola yazdırarak dinler .

client.js

const
    io = require("socket.io-client"),
    ioClient = io.connect("http://localhost:8000");

ioClient.on("seq-num", (msg) => console.info(msg));

Örneği çalıştırma

Gerekli kitaplıkları yükleyin:

npm install socket.io
npm install socket.io-client

Sunucuyu çalıştırın:

node server

Diğer terminal pencerelerini açın ve çalıştırarak istediğiniz sayıda istemci oluşturun:

node client

Ayrıca burada tam kod ile bir öz hazırladım .


8000 numaralı bağlantı noktası üretimdeki soket normu mu?
Kırmızı

1
Üzgünüm, cevap vermem çok uzun sürdü, @ingo. 8000, hem web soketleri hem de HTTP sunucularını sınarken Düğüm topluluğu tarafından kullanılan ortak bir bağlantı noktasıdır (3000 de yaygındır). Tabii ki bunu üretimde kullanabilirsiniz, ancak bunun ne kadar yaygın olduğundan emin değilim ... yine de, ağ geçitleriniz / yük dengeleyicileriniz / vb. İçin hazırlandığı sürece herhangi bir bağlantı noktasını gerçekten kullanabilirsiniz.
Lucio Paiva

Python / PHP programcısıyım ve soketler ve düğümlerde yeniyim, soru şu, neden her saniye aynı kullanıcının seq sayısını artırmamız gerekiyor? bu sadece demo için mi?
Umair

1
@Umair sadece demo amaçlıdır, sıra numarasını artırmaya gerek yoktur. Her müşteriye bir şeyler göndermeye devam edebileceğinizi göstermek için aldılar ve alacaklar.
Lucio Paiva

Bu örnekte belirli bir istemcinin yalnızca başka bir istemciye nasıl yalnızca ileti gönderdiğini görmüyorum. Her istemci, yalnızca demo için adlandırdığınız ve oluşturduğunuz sırada kendi sıra numarasını bilir ve bu sıra numaralarından, bu yuva kimliğine sahip istemciye doğrudan bir mesaj göndermek için gereken bir yuva kimliğine eşleme yoktur.
Selçuk

36

Kullanabilirsiniz

// yalnızca gönderen istemciye mesaj gönder

socket.emit('message', 'check this');

// veya gönderen dahil tüm dinleyicilere gönderebilirsiniz

io.emit('message', 'check this');

// gönderen dışındaki tüm dinleyicilere gönder

socket.broadcast.emit('message', 'this is a message');

// veya bir odaya gönderebilirsiniz

socket.broadcast.to('chatroom').emit('message', 'this is the message to all');


Benim için çalıştım, "const socket = requir ('socket.io') (http);" benim server.js dosyamda.
iPzard

36

1.0'da şunları kullanmalısınız:

io.sockets.connected[socketid].emit();

1
Evet !!!, önceden io.sockets.sockets [socketid] .emit () çalıştı, ancak bu bana socket.io'nun daha yeni sürümünde tanımlanmamış nesne hataları verdi. İo.sockets.connected çalışır.
Fraggle

TypeScript kullananlar için bu, şu an için yazılara göre 'standart' API'dir.
Avi Cherry

Ama bir hata alıyorum: io.sockets.connected ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", bilgi); ^ TypeError: undefined 'emit' özelliği okunamıyor
Raz

Muhtemelen api güncellenmiş olması ve artık böyle bir nesne io.sockets.connected["something"]ve emityöntemi yoktur.
Selçuk

12

Sunucu tarafındaki nodejs kodumuzda kullandığımız "io" nesnesini sadece console.log () kullanırsak hangi sürümü kullanırsak kullanalım [[io.on ('bağlantı', işlev (soket) {...});] , "io" nun sadece bir json nesnesi olduğunu ve soket kimliğinin ve soket nesnelerinin depolandığı birçok alt nesne olduğunu görebiliriz.

Socket.io 1.3.5 sürümünü kullanıyorum, btw.

Eğer io nesnesine bakarsak,

 sockets:
  { name: '/',
    server: [Circular],
    sockets: [ [Object], [Object] ],
    connected:
     { B5AC9w0sYmOGWe4fAAAA: [Object],
       'hWzf97fmU-TIwwzWAAAB': [Object] },

Burada "B5AC9w0sYmOGWe4fAAAA" soketlerini görebiliriz. Böylece yapabiliriz,

io.sockets.connected[socketid].emit();

Yine, daha fazla incelemede aşağıdaki gibi segmentleri görebiliriz:

 eio:
  { clients:
     { B5AC9w0sYmOGWe4fAAAA: [Object],
       'hWzf97fmU-TIwwzWAAAB': [Object] },

Böylece, buradan bir soket alabiliriz.

io.eio.clients[socketid].emit();

Ayrıca motorumuzda,

engine:
 { clients:
    { B5AC9w0sYmOGWe4fAAAA: [Object],
      'hWzf97fmU-TIwwzWAAAB': [Object] },

Yani, biz de yazabiliriz,

io.engine.clients[socketid].emit();

Yani, sanırım yukarıda sıraladığım 3 yoldan biriyle hedefimize ulaşabiliriz,

  1. io.sockets.connected [socketid] .emit (); VEYA
  2. io.eio.clients [socketid] .emit (); VEYA
  3. io.engine.clients [socketid] .emit ();

Ama bu hatayı aldım: io.eio.clients ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", bilgi); ^ TypeError: undefined 'emit' özelliği okunamıyor
Raz

Şu anda (2.1.1 sürümü ile) bu sadece soketler için çalıştı. Bağlı, ancak diğer ikisi için değil.
James

10

Bunu yapabilirsiniz

Sunucuda.

global.io=require("socket.io")(server);

io.on("connection",function(client){
    console.log("client is ",client.id);
    //This is handle by current connected client 
    client.emit('messages',{hello:'world'})
    //This is handle by every client
    io.sockets.emit("data",{data:"This is handle by every client"})
    app1.saveSession(client.id)

    client.on("disconnect",function(){
        app1.deleteSession(client.id)
        console.log("client disconnected",client.id);
    })

})

    //And this is handle by particular client 
    var socketId=req.query.id
    if(io.sockets.connected[socketId]!=null) {
        io.sockets.connected[socketId].emit('particular User', {data: "Event response by particular user "});
    }

Ve istemcide, kullanımı çok kolaydır.

var socket=io.connect("http://localhost:8080/")
    socket.on("messages",function(data){
        console.log("message is ",data);
        //alert(data)
    })
    socket.on("data",function(data){
        console.log("data is ",data);
        //alert(data)
    })

    socket.on("particular User",function(data){
        console.log("data from server ",data);
        //alert(data)
    })

7

1.4.5 sürümünden itibaren, io.to () 'da düzgün ön ekli bir socketId sağladığınızdan emin olun. Istemci hata ayıklama için giriş soket alıyordu ve önek olmadan bu yüzden öğrendim kadar sonsuza kadar arama sona erdi! Bu nedenle, sahip olduğunuz Kimlik'e önek eklenmemişse bunu böyle yapmanız gerekebilir:

io.to('/#' + socketId).emit('myevent', {foo: 'bar'});

6

io.sockets.sockets [socket.id] .emit (...) v0.9 sürümünde benim için çalıştı


Stack Overflow'a hoş geldiniz. Bu yanıtın mevcut cevaplara fazla bir bağıntı sağladığı görülmemektedir. Daha fazla üne sahip olduğunuzda , diğer insanların yayınlarına yorum yapabilirsiniz . Bu bir yorum için daha uygun görünüyor.
jerry

2

Ayrıca istemcilerin referanslarını tutabilirsiniz. Ama bu anılarınızı meşgul ediyor.

Boş bir nesne oluşturun ve istemcilerinizi bu nesneye ayarlayın.

const myClientList = {};

  server.on("connection", (socket) => {
    console.info(`Client connected [id=${socket.id}]`);
     myClientList[socket.id] = socket;   
  });
  socket.on("disconnect", (socket) => {
    delete myClientList[socket.id];
  });

ardından nesneden kimliğinizi kullanarak belirli istemcinizi arayın

myClientList[specificId].emit("blabla","somedata");

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.