Надішліть повідомлення конкретному клієнту за допомогою socket.io та node.js


191

Я працюю з socket.io і node.js, і до цього часу це здається досить непоганим, але я не знаю, як надіслати повідомлення з сервера певному клієнту, приблизно так:

client.send(message, receiverSessionId)

Але ні .send()ні, ні .broadcast()методи, здається, не задовольняють моєї потреби.

Що я знайшов як можливе рішення, це те, що .broadcast()метод приймає як другий параметр масив SessionIds, на який не надсилає повідомлення, тож я міг би передати масив усіх підключених на той момент SessionIds до сервера, крім одного Я хотів би надіслати повідомлення, але я вважаю, що повинно бути краще рішення.

Будь-які ідеї?

Відповіді:


95

Ну, вам доведеться захопити клієнта за це (сюрприз), ви можете піти простим шляхом:

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

Що може зламатися, я сумніваюся в цьому, але це завжди можливість, що io.clientsможе змінитися, тому використовуйте вищесказане з обережністю

Або ви самі стежите за клієнтами, тому додаєте їх до власного clientsоб'єкта в connectionслухачі і видаляєте їх у disconnectслухачі.

Я б застосував останній, оскільки залежно від вашої програми ви хочете мати більше станів на клієнтах, так що щось подібне clients[id] = {conn: clientConnect, data: {...}}може зробити цю роботу.


6
Іво, ти можеш вказати на більш повний приклад чи трішки детальніше? Я готовий зрозуміти такий підхід, але я не впевнений, що я розпізнаю змінні / об'єкти, які ви використовуєте в цьому прикладі. У клієнтах [id] = {conn: clientConnect, дані: {...}}, чи клієнти [id] є частиною об'єкта io, як видно з io.clients [sessionID] вище? Також що таке об’єкт clientConnect? Дякую.
AndrewHenderson

@ ivo-wetzel привіт, ти можеш привіт мені на цю тему? stackoverflow.com/questions/38817680 / ...
Махді pishguy

178

Відповідь Іво Ветцеля, здається, більше не вірна в Socket.io 0.9.

Якщо коротко, то тепер потрібно зберегти socket.idта використовувати io.sockets.socket(savedSocketId).emit(...) для надсилання повідомлень.

Ось як я це працював на кластерному сервері Node.js:

Спочатку потрібно встановити магазин Redis як магазин, щоб повідомлення могли перетинати процеси:

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);
    });
  });

});

Тут я пропустив код кластеризації, оскільки він робить це більш захаращеним, але додати тривіально. Просто додайте все до робочого коду. Більше документів тут http://nodejs.org/api/cluster.html


4
Спасибі це було корисно. Мені просто довелося використовувати масив замість цього: io.of('/mynamespace').sockets[socketID].emit(...)(не знаю, чи це тому, що я використовую простір імен)
Адріен Шулер

у кластерному середовищі, як я можу переконатися, що правильний процес, до якого належить сокет, посилає повідомлення?
Гал Бен-Хаїм

Як щодо липкої сесії люб’язно надано NGINX або HAProxy @Gal Ben-Haim?
matanster

var держатель = новий socketio.RedisStore; ^ TypeError: undefined не є функцією в Object. <anonymous> (C: \ Users \ Dev \ Desktop \ nouty-server \ server.js: 108: 14) в Module._compile (module.js: 460: 26) у Object.Module._extensions..js (module.js: 478: 10) на Module.load (module.js: 355: 32) у Function.Module._load (module.js: 310: 12) у Function.Module. runMain (module.js: 501: 10) при запуску (node.js: 129: 16) в node.js: 814: 3
Лукас Бертолло

1
io.sockets.socket(socket_id)видаляється в socket.io 1.0. github.com/socketio/socket.io/isissue/1618#issuecomment-46151246
ImMathan

98

кожна розетка приєднується до кімнати з ідентифікатором розетки для імені, так що ви можете просто

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

документи: http://socket.io/docs/rooms-and-namespaces/#default-room

Ура


7
Це найкраща відповідь і працює з новішими версіями socket.io. Там в шпаргалку тут добре: stackoverflow.com/questions/10058226 / ...
blented

4
зауважте, що це "ефірний" тип емісії події. тому якщо ви спробуєте встановити зворотний дзвінок на це, ви отримаєте помилку. якщо вам потрібно надіслати подію до певного сокета із зворотним дзвоном, тоді використовуйте @ PHPthinking відповідь і використовуйте io.sockets.connected[socketid].emit();. Перевірено з 1.4.6.
tbutcaru

Але я отримав цю помилку: io.to ["JgCoFX9AiCND_ZhdAAAC"]. Emmit ("socketFromServe‌ r", інформація); ^ TypeError: Не вдається прочитати властивість 'emit' для невизначеного
Raz

85

Найпростіше, найелегантніше рішення

Це так просто, як:

client.emit("your message");

І це все.

Але як? Наведіть мені приклад

Все, що нам потрібно, насправді є повним прикладом, і ось що випливає. Це тестується з останньою версією socket.io (2.0.3), і він також використовує сучасний Javascript (який ми повинні використовувати вже зараз).

Приклад складається з двох частин: сервера та клієнта. Щоразу, коли клієнт підключається, він починає отримувати від сервера періодичний порядковий номер. Починається нова послідовність для кожного нового клієнта, тому сервер повинен відстежувати їх окремо. Ось тут починає грати "Мені потрібно надіслати повідомлення конкретному клієнту" . Код зрозуміти дуже просто. Подивимось.

Сервер

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);

Сервер починає прослуховувати порт 8000 для вхідних з'єднань. Коли хтось прибуває, він додає нового клієнта на карту, щоб він міг відслідковувати його порядковий номер. Він також слухає для цього клієнтаdisconnect подію , коли він видалить його з карти.

Кожну секунду запускається таймер. Після цього сервер проходить по карті та надсилає повідомлення кожному клієнту з його поточним порядковим номером. Потім він збільшує його і зберігає число назад на карті. Це все, що до цього. Простенька.

Клієнт

Клієнтська частина ще простіша. Він просто підключається до сервера і слухає seq-numповідомлення, друкуючи його на консоль кожного разу, коли воно надійде.

client.js

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

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

Запуск прикладу

Встановіть потрібні бібліотеки:

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

Запустіть сервер:

node server

Відкрийте інші вікна терміналів та породжуйте стільки клієнтів, скільки вам потрібно:

node client

Крім того, я підготував суть з повним кодом тут .


Чи є порт 8000 нормою для сокетіо у виробництві?
Червоний

1
Вибачте, що я так довго відповів, @ingo. 8000 - це загальний порт, який використовується спільнотою Node при тестуванні як вебсокетів, так і HTTP-серверів (3000 також є загальним). Звичайно, ви можете використовувати його у виробництві, але я не впевнений, наскільки це звичайно ... все одно ви можете просто використовувати будь-який порт, доки ваші шлюзи / балансири навантажень / тощо готові до цього.
Лусіо Пайва

Я програміст Python / PHP, і я новачок у сокетах та вузлах, питання полягає в тому, чому нам потрібно збільшувати кількість послідовних номерів одного і того ж користувача щосекунди? це тільки для демонстрації?
Умаїр

1
@Umair це просто для демонстраційних цілей, немає необхідності збільшувати порядковий номер. Просто потрібно було показати, що ви можете продовжувати надсилати речі кожному клієнту, і він їх отримає.
Лусіо Пайва

Я не бачу в цьому прикладі, як конкретний клієнт надсилає повідомлення лише та лише іншому клієнту. Кожен клієнт знає лише свій власний порядковий номер, як ви його назвали та створили для демонстрації, і немає цієї карти з цих порядкових номерів на ідентифікатор сокета, який необхідний для того, щоб надіслати повідомлення клієнту безпосередньо з цим ідентифікатором сокета.
Сельчук

36

Можна використовувати

// надіслати повідомлення лише відправника-клієнту

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

// або ви можете надсилати всіх слухачів, включаючи відправника

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

// надсилати всім слухачам, окрім відправника

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

// або ви можете відправити його до кімнати

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


Працював для мене, я також повинен був додати "const socket = requ ('socket.io') (http);" в моєму файлі server.js.
iPzard

36

В 1.0 ви повинні використовувати:

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

1
Так !!!, раніше io.sockets.sockets [socketid] .emit () працював, але це дало мені невизначені об'єктні помилки в новій версії socket.io. Зміна роботи на io.sockets.connected.
Fraggle

Для тих, хто використовує TypeScript, це зараз "канонічний" API для цього відповідно до типізації.
Avi Cherry

Але я отримую помилку: io.sockets.connected ["JgCoFX9AiCND_ZhdAAAC"]. Emmit ("socketFromServer", інформація); ^ TypeError: Неможливо прочитати властивість 'emit' невизначеного
Raz

це, мабуть, пов’язано з тим, що api оновився і більше немає такого об'єкта, як io.sockets.connected["something"]його emitметод.
Сельчук

12

Яку б версію ми не використовували, якщо ми просто console.log () об'єкт "io", який ми використовуємо в коді nodejs на стороні сервера, [наприклад, io.on ("з'єднання", функція (socket) {...});] , ми можемо бачити, що "io" - це просто об'єкт json, і є багато дочірніх об'єктів, де зберігаються ідентифікатор сокета та сокет.

Я використовую socket.io версії 1.3.5, btw.

Якщо ми подивимось на об’єкт io, він містить,

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

тут ми можемо побачити сокетіди "B5AC9w0sYmOGWe4fAAAA" тощо. Отже, ми можемо зробити це,

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

Знову ж таки, при подальшому огляді ми можемо побачити такі сегменти,

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

Отже, ми можемо отримати розетку звідси, зробивши це

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

Також під двигуном ми маємо,

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

Отже, ми також можемо написати,

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

Отже, я думаю, що ми можемо досягти своєї мети будь-яким із 3-х способів, які я перераховував вище,

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

Але я отримав цю помилку: io.eio.clients ["JgCoFX9AiCND_ZhdAAAC"]. Emmit ("socketFromServer", інформація); ^ TypeError: Неможливо прочитати властивість 'emit' невизначеного
Raz

В даний час (з версією 2.1.1) це працює лише для sockets.connected, але не для двох інших.
Джеймс

10

Ви можете це зробити

На сервері.

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 "});
    }

А з клієнтом це дуже просто впоратися.

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, переконайтеся, що ви надали правильно встановлений socketId в io.to (). Я брав socketId Клієнт увійшов до налагодження, і це було без префіксу, тому я назавжди шукав, поки не дізнався! Таким чином, можливо, вам доведеться зробити це так, якщо ідентифікатор, який ви маєте, не має префікса:

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

6

io.sockets.sockets [socket.id] .emit (...) працював для мене в v0.9


Ласкаво просимо до переповнення стека. Ця відповідь, здається, не додає великого відношення до існуючих відповідей. Як тільки ви отримаєте більше репутації , ви зможете коментувати пости інших людей . Це здається краще підходить для коментаря.
Джеррі

2

Крім того, ви можете утримати відмову клієнтів. Але це робить вашу пам’ять зайнятою.

Створіть порожній об’єкт і встановіть у нього своїх клієнтів.

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];
  });

потім зателефонуйте своєму конкретному клієнту за ідентифікатором від об’єкта

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.