ส่งข้อความไปยังไคลเอ็นต์ที่ระบุพร้อมกับ 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
Ivo คุณสามารถชี้ไปที่ตัวอย่างที่สมบูรณ์มากขึ้นหรือทำอย่างละเอียดได้หรือไม่? ฉันกระตือรือร้นที่จะเข้าใจวิธีการนี้ แต่ฉันไม่แน่ใจว่าฉันรู้จักตัวแปร / วัตถุที่คุณใช้ในตัวอย่างนี้ ในไคลเอนต์ [id] = {conn: clientConnect ข้อมูล: {... }} เป็นลูกค้า [id] ส่วนหนึ่งของวัตถุ io ตามที่เห็นใน io.clients [sessionID] ด้านบนหรือไม่ นอกจากนี้วัตถุ clientConnect คืออะไร ขอบคุณ
AndrewHenderson

@ ivo-wetzel สวัสดีคุณช่วยคุณได้ไหมในหัวข้อนี้ stackoverflow.com/questions/38817680/…
mahdi pishguy

178

คำตอบของ Ivo Wetzel ดูเหมือนจะไม่ถูกต้องใน Socket.io 0.9 อีกต่อไป

ในระยะสั้นคุณต้องบันทึกsocket.idและใช้io.sockets.socket(savedSocketId).emit(...) เพื่อส่งข้อความไป

นี่คือวิธีที่ฉันได้ทำงานในเซิร์ฟเวอร์ Node.js คลัสเตอร์:

ก่อนอื่นคุณต้องตั้งค่า Redis store เป็น store เพื่อให้ข้อความสามารถข้ามกระบวนการได้:

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(...)(ไม่ทราบว่ามันเป็นเพราะฉันใช้ namespace ก)
Adrien ชูเลอร์

บนสภาพแวดล้อมแบบคลัสเตอร์ฉันจะแน่ใจได้อย่างไรว่ากระบวนการที่ถูกต้องที่ซ็อกเก็ตอยู่นั้นกำลังส่งข้อความ
Gal Ben-Haim

วิธีการเกี่ยวกับเซสชันเหนียวของ 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
Lucas Bertollo

1
io.sockets.socket(socket_id)ถูกลบใน socket.io 1.0 github.com/socketio/socket.io/issues/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"]. ปล่อยออกมา ("socketFromServe‌ r", info); ^ TypeError: ไม่สามารถอ่านคุณสมบัติ 'ปล่อย' ของไม่ได้กำหนด
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เหตุการณ์ของลูกค้าเมื่อมันจะลบออกจากแผนที่

ทุก ๆ วินาทีจะจับเวลาออก เมื่อใดก็ตามที่เซิร์ฟเวอร์เดินผ่านแผนที่และส่งข้อความไปยังลูกค้าทุกคนที่มีหมายเลขลำดับปัจจุบัน จากนั้นจะเพิ่มขึ้นและเก็บหมายเลขไว้ในแผนที่ นั่นคือทั้งหมดที่เป็นไปได้ peasy ง่าย ๆ

ไคลเอนต์

ส่วนลูกค้านั้นเรียบง่ายขึ้น เพียงเชื่อมต่อกับเซิร์ฟเวอร์และฟัง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 เป็นมาตรฐานสำหรับ socketio ในการผลิตหรือไม่
Red

1
ขอโทษฉันใช้เวลานานมากในการตอบกลับ @ingo 8000 เป็นพอร์ตทั่วไปที่ใช้โดยชุมชนโหนดเมื่อทำการทดสอบทั้ง websockets หรือเซิร์ฟเวอร์ HTTP (3000 ก็เป็นเรื่องปกติเช่นกัน) แน่นอนคุณสามารถใช้มันในการผลิต แต่ฉันไม่แน่ใจว่าเป็นเรื่องธรรมดาที่ ... อย่างไรก็ตามคุณสามารถใช้พอร์ตใด ๆ ได้จริงตราบใดที่เกตเวย์ / load balancer ของคุณ / etc ถูกเตรียมไว้สำหรับสิ่งนั้น
Lucio Paiva

ฉันเป็นโปรแกรมเมอร์ Python / PHP และฉันยังใหม่กับซ็อกเก็ตและโหนดคำถามคือทำไมเราต้องเพิ่มจำนวนผู้ใช้เดียวกันในแต่ละวินาที นั่นเป็นเพียงการสาธิต
Umair

1
@ Umar เป็นเพียงการสาธิตไม่จำเป็นต้องเพิ่มหมายเลข มันเป็นเพียงการแสดงให้เห็นว่าคุณสามารถส่งข้อมูลไปยังลูกค้าแต่ละรายและพวกเขาจะได้รับมัน
Lucio Paiva

ฉันไม่เห็นในตัวอย่างนี้ว่าลูกค้าเฉพาะส่งข้อความเท่านั้นและเฉพาะกับลูกค้าอื่น ลูกค้าแต่ละคนรู้หมายเลขลำดับของตัวเองตามที่คุณตั้งชื่อและสร้างขึ้นเพื่อการสาธิตและไม่มีแผนที่จากหมายเลขลำดับนี้ไปยังซ็อกเก็ต id ที่จำเป็นในการส่งข้อความโดยตรงไปยังลูกค้าด้วยซ็อกเก็ต ID นั้น
Selçuk

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 = require ('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 'canonical' สำหรับสิ่งนี้ตามการพิมพ์
Avi Cherry

แต่ฉันได้รับข้อผิดพลาด: io.sockets.connected ["JgCoFX9AiCND_ZhdAAAC"]. emit ("socketFromServer", info); ^ TypeError: ไม่สามารถอ่านคุณสมบัติ 'ปล่อย' ของไม่ได้กำหนด
Raz

นั่นอาจเป็นเพราะข้อเท็จจริงที่ว่า api ปรับปรุงและไม่มีวัตถุที่ต้องการio.sockets.connected["something"]และemitวิธีการของมันอีกต่อไป
Selçuk

12

ไม่ว่าเราจะใช้รุ่นใดหากเราเพียงแค่ console.log () วัตถุ "io" ที่เราใช้ในโค้ด nodejs ฝั่งเซิร์ฟเวอร์ของเรา [เช่น io.on ('การเชื่อมต่อ', ฟังก์ชัน (ซ็อกเก็ต) {... });] เราจะเห็นได้ว่า "io" เป็นเพียงวัตถุ json และมีวัตถุลูกมากมายที่เก็บรหัสซ็อกเก็ตและวัตถุซ็อกเก็ต

ฉันใช้ socket.io เวอร์ชั่น 1.3.5, btw

ถ้าเราดูในวัตถุ io มันจะประกอบด้วย

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

ที่นี่เราสามารถเห็น socketids "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"]. emit ("socketFromServer", info); ^ TypeError: ไม่สามารถอ่านคุณสมบัติ 'ปล่อย' ของไม่ได้กำหนด
Raz

ปัจจุบัน (กับรุ่น 2.1.1) สิ่งนี้ใช้ได้เฉพาะกับซ็อกเก็ตเชื่อมต่อแล้ว แต่ไม่ใช่อีกสองตัว
James

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


ยินดีต้อนรับสู่ Stack Overflow คำตอบนี้ดูเหมือนจะไม่เกี่ยวข้องกับคำตอบที่มีอยู่เดิมอีกมาก เมื่อคุณมีชื่อเสียงที่คุณจะสามารถที่จะแสดงความคิดเห็นในโพสต์ของคนอื่น ดูเหมือนว่าจะเหมาะสำหรับความคิดเห็น
jerry

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.