Gửi tin nhắn đến ứng dụng khách cụ thể với socket.io và node.js


189

Tôi đang làm việc với socket.io và node.js và cho đến bây giờ có vẻ khá tốt, nhưng tôi không biết cách gửi tin nhắn từ máy chủ đến một khách hàng cụ thể, đại loại như thế này:

client.send(message, receiverSessionId)

Nhưng dường như cả phương pháp .send()cũng không .broadcast()cung cấp nhu cầu của tôi.

Điều tôi đã tìm thấy như một giải pháp khả thi, đó là .broadcast()phương thức chấp nhận như là một tham số thứ hai, một mảng của SessionIds không gửi tin nhắn, vì vậy tôi có thể chuyển một mảng với tất cả các SessionIds được kết nối tại thời điểm đó đến máy chủ, ngoại trừ một tham số Tôi muốn gửi tin nhắn, nhưng tôi cảm thấy phải có một giải pháp tốt hơn.

Có ý kiến ​​gì không?

Câu trả lời:


95

Vâng, bạn phải lấy khách hàng cho điều đó (bất ngờ), bạn có thể đi theo cách đơn giản:

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

Điều này có thể phá vỡ, tôi nghi ngờ điều đó, nhưng nó luôn có khả năng io.clientscó thể bị thay đổi, vì vậy hãy cẩn thận khi sử dụng ở trên

Hoặc bạn tự theo dõi các khách hàng, do đó bạn thêm chúng vào clientsđối tượng của riêng bạn trong trình connectionnghe và loại bỏ chúng trong trình disconnectnghe.

Tôi sẽ sử dụng cái thứ hai, vì tùy thuộc vào ứng dụng của bạn, bạn có thể muốn có nhiều trạng thái hơn trên máy khách, vì vậy một cái gì đó giống như clients[id] = {conn: clientConnect, data: {...}}có thể thực hiện công việc.


6
Ivo, bạn có thể chỉ ra một ví dụ đầy đủ hơn hoặc xây dựng một chút? Tôi rất muốn hiểu cách tiếp cận này, nhưng tôi không chắc là tôi nhận ra các biến / đối tượng bạn đang sử dụng trong ví dụ này. Trong client [id] = {Conn: clientConnect, data: {...}}, có phải client [id] là một phần của đối tượng io như đã thấy trong io.clents [sessionID] ở trên không? Ngoài ra đối tượng clientConnect là gì? Cảm ơn.
AndrewHenderson

@ ivo-wetzel hi, bạn có thể giúp tôi về chủ đề này không? stackoverflow.com/questions/38817680/ từ
mahdi pishguy

178

Câu trả lời của Ivo Wetzel dường như không còn hiệu lực trong Socket.io 0.9 nữa.

Nói tóm lại, bây giờ bạn phải lưu socket.idvà sử dụng io.sockets.socket(savedSocketId).emit(...) để gửi tin nhắn đến nó.

Đây là cách tôi làm việc này trong máy chủ Node.js phân cụm:

Trước tiên, bạn cần đặt Redis store làm cửa hàng để tin nhắn có thể đi qua các quy trình:

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

});

Tôi đã bỏ qua mã phân cụm ở đây, vì nó làm cho điều này trở nên lộn xộn hơn, nhưng nó không đáng để thêm vào. Chỉ cần thêm mọi thứ vào mã worker. Thêm tài liệu ở đây http://nodejs.org/api/cluster.html


4
Cảm ơn nó rất hữu ích. Tôi chỉ phải sử dụng một mảng thay thế: io.of('/mynamespace').sockets[socketID].emit(...)(không biết có phải vì tôi đang sử dụng một không gian tên)
Adrien Schuler

trên môi trường cụm, làm thế nào để tôi chắc chắn rằng quy trình chính xác mà ổ cắm thuộc về đang gửi thông báo?
Gal Ben-Haim

Làm thế nào về một phiên dính lịch sự của NGINX hoặc HAProxy @Gal Ben-Haim?
matanster

người giữ var = socketio mới.RedisStore; ^ TypeError: không xác định không phải là một chức năng tại Object. <Anonymous> (C: \ Users \ Dev \ Desktop \ nouty-server \ server.js: 108: 14) tại Module._compile (module.js: 460: 26) tại Object.Module._extensions..js (module.js: 478: 10) tại Module.load (module.js: 355: 32) tại Function.Module._load (module.js: 310: 12) tại Function.Module. runMain (module.js: 501: 10) khi khởi động (node.js: 129: 16) tại node.js: 814: 3
Lucas Bertoche

1
io.sockets.socket(socket_id)được gỡ bỏ trong socket.io 1.0. github.com/socketio/socket.io/issues/1618#issuecomment-46151246
ImMathan

96

mỗi ổ cắm tham gia một phòng có id ổ cắm cho một tên, vì vậy bạn chỉ có thể

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

tài liệu: http://socket.io/docs/rooms-and-namespaces/#default-room

Chúc mừng


7
Đây là câu trả lời tốt nhất và hoạt động với các phiên bản mới hơn của socket.io. Có một bảng cheat tuyệt vời ở đây: stackoverflow.com/questions/10058226/ cấp
thổi vào

4
lưu ý rằng đây là loại 'phát sóng' phát ra một sự kiện. Vì vậy, nếu bạn cố gắng đặt lại cuộc gọi này, bạn sẽ gặp lỗi. nếu bạn cần gửi một sự kiện đến một ổ cắm cụ thể bằng một cuộc gọi lại, thì hãy sử dụng câu trả lời của @ PHPthinking và sử dụng io.sockets.connected[socketid].emit();. Đã thử nghiệm với 1.4.6.
tbutcaru

Nhưng tôi đã gặp lỗi này: io.to ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServe‌ r", thông tin); ^ TypeError: Không thể đọc thuộc tính 'phát ra' không xác định
Raz

85

Giải pháp đơn giản nhất, thanh lịch nhất

Nó dễ như:

client.emit("your message");

Và đó là nó.

Nhưng bằng cách nào? Cho tôi một ví dụ

Những gì chúng ta cần thực tế là một ví dụ đầy đủ, và đó là những gì tiếp theo. Điều này đã được thử nghiệm với phiên bản socket.io mới nhất (2.0.3) và hiện tại nó cũng đang sử dụng Javascript hiện đại (mà tất cả chúng ta nên sử dụng ngay bây giờ).

Ví dụ bao gồm hai phần: máy chủ và máy khách. Bất cứ khi nào máy khách kết nối, nó bắt đầu nhận từ máy chủ một số thứ tự định kỳ. Một chuỗi mới được bắt đầu cho mỗi máy khách mới, vì vậy máy chủ phải theo dõi từng cá nhân. Đó là nơi "Tôi cần gửi tin nhắn đến một khách hàng cụ thể" phát huy tác dụng. Mã này rất đơn giản để hiểu. Chúng ta hãy xem nó.

Người phục vụ

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

Máy chủ bắt đầu nghe trên cổng 8000 cho các kết nối đến. Khi một người đến, nó sẽ thêm khách hàng mới đó vào bản đồ để có thể theo dõi số thứ tự của nó. Nó cũng lắng nghe khách hàng đódisconnect sự kiện đó, khi nó sẽ xóa nó khỏi bản đồ.

Mỗi giây, một bộ đếm thời gian được kích hoạt. Khi có, máy chủ sẽ đi qua bản đồ và gửi tin nhắn đến mọi khách hàng với số thứ tự hiện tại. Sau đó, nó tăng nó và lưu lại số trong bản đồ. Đó là tất cả những gì thuộc về nó. Dễ như ăn bánh.

Khách hàng

Phần khách hàng thậm chí còn đơn giản hơn. Nó chỉ kết nối với máy chủ và lắng nghe seq-numtin nhắn, in nó ra bàn điều khiển mỗi khi nó đến.

client.js

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

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

Chạy ví dụ

Cài đặt các thư viện cần thiết:

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

Chạy máy chủ:

node server

Mở các cửa sổ đầu cuối khác và sinh ra nhiều khách hàng như bạn muốn bằng cách chạy:

node client

Tôi cũng đã chuẩn bị một ý chính với mã đầy đủ ở đây .


Là cổng 8000 là tiêu chuẩn cho socketio trong sản xuất?
Đỏ

1
Xin lỗi tôi mất quá nhiều thời gian để trả lời lại, @ingo. 8000 là một cổng phổ biến được sử dụng bởi cộng đồng Node khi kiểm tra cả websockets hoặc máy chủ HTTP (3000 cũng là phổ biến). Tất nhiên bạn có thể sử dụng nó trong sản xuất, nhưng tôi không chắc nó phổ biến đến mức nào ... dù sao, bạn chỉ có thể sử dụng bất kỳ cổng nào, miễn là cổng / bộ cân bằng tải / vv của bạn được chuẩn bị cho điều đó.
Lucio Paiva

Tôi là lập trình viên Python / PHP và tôi chưa quen với socket và nút, câu hỏi là, tại sao chúng ta cần tăng số seq của cùng một người dùng mỗi giây? đó chỉ là bản demo?
Umair

1
@Umair nó chỉ dành cho mục đích demo, không cần thực sự tăng số thứ tự. Nó chỉ để cho thấy rằng bạn có thể tiếp tục gửi công cụ đến từng khách hàng và họ sẽ nhận được nó.
Lucio Paiva

Tôi không thấy trong ví dụ này làm thế nào một khách hàng cụ thể chỉ gửi một tin nhắn và chỉ cho một khách hàng khác. Mỗi khách hàng chỉ biết số thứ tự của riêng mình như bạn đã đặt tên và tạo nó để dùng thử, và không có bản đồ nào từ số thứ tự này đến id socket cần thiết để gửi tin nhắn trực tiếp đến máy khách với id socket đó.
Selçuk

35

Bạn có thể dùng

// chỉ gửi tin nhắn cho người gửi-khách hàng

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

// hoặc bạn có thể gửi cho tất cả người nghe bao gồm cả người gửi

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

// gửi cho tất cả người nghe ngoại trừ người gửi

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

// hoặc bạn có thể gửi nó đến một phòng

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


Làm việc cho tôi, tôi cũng phải thêm "const socket = Yêu cầu ('socket.io') (http);" trong tệp server.js của tôi.
iPzard

35

Trong 1.0 bạn nên sử dụng:

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

1
Có !!!, trước đây io.sockets.sockets [socketid] .emit () đã hoạt động, nhưng điều này đã cho tôi các lỗi đối tượng không xác định trong phiên bản mới hơn của socket.io. Thay đổi thành io.sockets.connected works.
Fraggle

Đối với những người sử dụng TypeScript, đây hiện là API 'chuẩn' cho việc này theo các kiểu chữ.
Avi Cherry

Nhưng tôi nhận được một lỗi: io.sockets.connected ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", thông tin); ^ TypeError: Không thể đọc thuộc tính 'phát ra' không xác định
Raz

điều đó có lẽ là do thực tế là api đã cập nhật và không còn một đối tượng nào giống như phương thức io.sockets.connected["something"]của nó emitnữa.
Selçuk

12

Bất cứ phiên bản nào chúng tôi đang sử dụng nếu chúng tôi chỉ console.log () đối tượng "io" mà chúng tôi sử dụng trong mã nodejs phía máy chủ của chúng tôi, [ví dụ: io.on ('Connection', function (socket) {...});] , chúng ta có thể thấy rằng "io" chỉ là một đối tượng json và có nhiều đối tượng con trong đó các đối tượng socket id và socket được lưu trữ.

Tôi đang sử dụng socket.io phiên bản 1.3.5, btw.

Nếu chúng ta nhìn vào đối tượng io, nó chứa,

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

ở đây chúng ta có thể thấy các socket "B5AC9w0sYmOGwe4fAAAA", v.v. Vì vậy, chúng ta có thể làm,

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

Một lần nữa, khi kiểm tra thêm, chúng ta có thể thấy các phân đoạn như,

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

Vì vậy, chúng ta có thể lấy một ổ cắm từ đây bằng cách làm

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

Ngoài ra, dưới động cơ chúng ta có,

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

Vì vậy, chúng ta cũng có thể viết,

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

Vì vậy, tôi đoán chúng ta có thể đạt được mục tiêu của mình theo bất kỳ cách nào trong 3 cách tôi liệt kê ở trên,

  1. io.sockets.connected [socketid] .emit (); HOẶC LÀ
  2. io.eio.clents [socketid] .emit (); HOẶC LÀ
  3. io.engine.clents [socketid] .emit ();

Nhưng tôi đã gặp lỗi này: io.eio.clents ["JgCoFX9AiCND_ZhdAAAC"]. Emit ("socketFromServer", thông tin); ^ TypeError: Không thể đọc thuộc tính 'phát ra' không xác định
Raz

Hiện tại (với phiên bản 2.1.1), điều này chỉ hoạt động cho sockets.connected, nhưng không hoạt động với hai phiên bản còn lại.
James

10

Bạn có thể làm được việc này

Trên máy chủ.

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

Và trên máy khách, nó rất dễ dàng để xử lý.

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

Kể từ phiên bản 1.4.5, hãy đảm bảo bạn cung cấp một socketId có tiền tố đúng trong io.to (). Tôi đã lấy socketId Client đã đăng nhập để gỡ lỗi và nó không có tiền tố nên cuối cùng tôi đã tìm kiếm mãi cho đến khi tôi phát hiện ra! Vì vậy, bạn có thể phải làm như thế này nếu Id bạn không có tiền tố:

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

6

io.sockets.sockets [socket.id] .emit (...) đã làm việc cho tôi trong v0.9


Chào mừng bạn đến với Stack Overflow. Câu trả lời này dường như không thêm nhiều tương đối vào các câu trả lời hiện có. Khi bạn có nhiều danh tiếng hơn , bạn sẽ có thể nhận xét về bài đăng của người khác . Điều này có vẻ phù hợp hơn cho một bình luận.
jerry

2

Ngoài ra, bạn có thể giữ cho khách hàng giới thiệu. Nhưng điều này làm cho memmory của bạn bận rộn.

Tạo một đối tượng trống và đặt khách hàng của bạn vào đó.

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

sau đó gọi khách hàng cụ thể của bạn bằng id từ đối tượng

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.