วิธีส่งข้อความไปยังไคลเอนต์เฉพาะด้วย socket.io


97

ฉันเริ่มต้นด้วย socket.io + node.js ฉันรู้วิธีส่งข้อความในเครื่องและsocket.broadcast.emit()ฟังก์ชั่นการออกอากาศ: - ไคลเอนต์ที่เชื่อมต่อทั้งหมดได้รับข้อความเดียวกัน

ตอนนี้ฉันต้องการทราบวิธีการส่งข้อความส่วนตัวไปยังลูกค้ารายใดรายหนึ่งฉันหมายถึงซ็อกเก็ตหนึ่งช่องสำหรับการแชทส่วนตัวระหว่าง 2 คน (สตรีมไคลเอนต์ถึงไคลเอนต์) ขอบคุณ.


ขออภัย psiphi75 แต่ลิงค์นี้ไม่ตอบกลับคำตอบของฉันไม่ใช่คำถามที่ซ้ำกัน
Nizar B.

1
@ psiphi75 ไม่มีทางซ้ำ
softvar

1
ทนไม่ไหวคนอย่าง @psiphi. คุณเป็นนักพัฒนาหรือไม่? คำถามเฉพาะของ HTML5 เกี่ยวข้องกับไลบรารีแบบสแตนด์อโลนอย่างไร และสำหรับสิ่งที่คุ้มค่า WebSockets ไม่ใช่ Socket.io Socket.io เป็นไลบรารีที่สามารถใช้ WebSockets ได้ แต่ฉันพูดนอกเรื่อง นอกจากนี้ยังเป็นคำถามที่เฉพาะเจาะจงมากขึ้นเกี่ยวกับไลบรารีเกี่ยวกับการส่งข้อมูลไปยังลูกค้าบางรายเท่านั้นไม่เกี่ยวกับเทคโนโลยี
Levi Roberts


@ bugwheels94 ไม่จริงโพสต์นี้มาจากปี 2011 และเนื่องจาก nodejs มีการเปลี่ยนแปลงโค้ดที่ชาญฉลาดมากมาย โพสต์นี้เป็นคำถาม / คำตอบที่ถูกต้องสำหรับปัญหานี้
Nizar B.

คำตอบ:


102

เมื่อผู้ใช้เชื่อมต่อควรส่งข้อความไปยังเซิร์ฟเวอร์ด้วยชื่อผู้ใช้ซึ่งจะต้องไม่ซ้ำกันเช่นอีเมล

คู่ของชื่อผู้ใช้และซ็อกเก็ตควรถูกเก็บไว้ในวัตถุเช่นนี้:

var users = {
    'userA@example.com': [socket object],
    'userB@example.com': [socket object],
    'userC@example.com': [socket object]
}

บนไคลเอนต์ปล่อยอ็อบเจ็กต์ไปยังเซิร์ฟเวอร์พร้อมข้อมูลต่อไปนี้:

{
    to:[the other receiver's username as a string],
    from:[the person who sent the message as string],
    message:[the message to be sent as string]
}

บนเซิร์ฟเวอร์รับฟังข้อความ เมื่อได้รับข้อความให้ส่งข้อมูลไปยังผู้รับ

users[data.to].emit('receivedMessage', data)

บนไคลเอนต์ให้ฟังเสียงที่ปล่อยออกมาจากเซิร์ฟเวอร์ที่เรียกว่า 'receivedMessage' และด้วยการอ่านข้อมูลคุณสามารถจัดการได้ว่าใครมาจากไหนและข้อความที่ถูกส่ง


28
หากคุณใช้วิธีนี้คุณต้องเข้าใจ: 1. เมื่อผู้ใช้ตัดการเชื่อมต่อคุณต้องล้างอ็อบเจ็กต์ 'ผู้ใช้' 2. ไม่รองรับการเชื่อมต่อครั้งที่สองเช่นจากเบราว์เซอร์อื่น ดังนั้นหากผู้ใช้เชื่อมต่อจากเบราว์เซอร์อื่นการเชื่อมต่อแบบเก่าจะถูกลบล้าง
Vladimir Kurijov

1
คุณจะจัดเก็บอ็อบเจ็กต์ซ็อกเก็ตในที่เก็บข้อมูลอย่างไร ฉันสมมติว่าสิ่งนี้ใช้ไม่ได้หากคุณมีกระบวนการโหนดมากกว่าหนึ่งกระบวนการ
chovy

@chovy คุณต้องใช้ redis ตรวจสอบที่github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO
Vladimir Kurijov

11
ฉันขอแนะนำว่าอย่าใช้วิธีนี้ ฉันต้องเสียเวลามากมายในการพยายามเอาชนะข้อ จำกัด ของแนวทางนี้ ดู@ แก้ปัญหา az7ar ของและคำอธิบายว่าทำไมมันจะดีกว่า
Daniel Que

@DanielQue +1. วิธีที่ดีกว่าในการใช้ฟังก์ชันดั้งเดิม ขอบคุณ!
Aaron Lelevier

280

คุณสามารถใช้ห้อง socket.io จากฝั่งไคลเอ็นต์จะปล่อยเหตุการณ์ ("เข้าร่วม" ในกรณีนี้อาจเป็นอะไรก็ได้) โดยมีตัวระบุที่ไม่ซ้ำกัน (อีเมล, id)

ด้านลูกค้า:

var socket = io.connect('http://localhost');
socket.emit('join', {email: user1@example.com});

ตอนนี้จากฝั่งเซิร์ฟเวอร์ใช้ข้อมูลนั้นเพื่อสร้างห้องเฉพาะสำหรับผู้ใช้นั้น

ฝั่งเซิร์ฟเวอร์:

var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.on('join', function (data) {
    socket.join(data.email); // We are using room of socket io
  });
});

ตอนนี้ผู้ใช้ทุกคนเข้าร่วมห้องที่ตั้งชื่อตามอีเมลของผู้ใช้แล้ว ดังนั้นหากคุณต้องการส่งข้อความถึงผู้ใช้เฉพาะคุณก็ต้องทำ

ฝั่งเซิร์ฟเวอร์:

io.sockets.in('user1@example.com').emit('new_msg', {msg: 'hello'});

สิ่งสุดท้ายที่ต้องทำในฝั่งไคลเอ็นต์คือฟังเหตุการณ์ "new_msg"

ด้านลูกค้า:

socket.on("new_msg", function(data) {
    alert(data.msg);
}

ฉันหวังว่าคุณจะได้รับความคิด


3
โปรดเปลี่ยนบรรทัดนี้ io.socket.in ('user1@example.com '). emit (' new_msg ', {msg:' hello '}); เช่นนี้ io.sockets.in ('user1@example.com '). emit (' new_msg ', {msg:' hello '});
silvesterprabu

44
คำตอบนี้มากดีกว่าคำตอบที่ได้รับการยอมรับในขณะนี้ นี่คือเหตุผล: 1) คุณไม่จำเป็นต้องจัดการและทำความสะอาดอาร์เรย์ของไคลเอ็นต์ทั่วโลก 2) ใช้งานได้แม้ว่าผู้ใช้จะเปิดหลายแท็บในหน้าเดียวกัน 3) มันสามารถขยายได้อย่างง่ายดายที่จะทำงานร่วมกับคลัสเตอร์โหนด (กระบวนการหลาย) socket.io-redisด้วย
Daniel Que

2
คุณจะสร้าง{email: user1@example.com}ความแตกต่างสำหรับการเชื่อมต่อทั้งหมดได้อย่างไรไม่ให้ไคลเอนต์ทั้งหมดที่ไปที่แอพมีผู้ใช้user1@example.comว่าฉันจะทำให้เป็นผู้ใช้user2@example.comสำหรับไคลเอนต์ที่สองที่เชื่อมต่อได้อย่างไรเพื่อที่ฉันจะได้มีห้องต่างๆ ขออภัยหากเป็นคำถามที่น่ารำคาญ
แจ็คเปล่า

2
ชอบวิธีแก้ปัญหา แต่ฉันเชื่อว่าความปลอดภัยถูกบุกรุกที่นี่ จะเกิดอะไรขึ้นถ้าฉันเปลี่ยนอีเมลในสคริปต์ฝั่งไคลเอ็นต์ ตอนนี้ฉันสามารถอ่านข้อความส่วนตัวอื่น ๆ ได้ด้วย คุณกำลังพูดอะไร?
Gopinath Shiva

3
ไม่จำเป็นต้องเป็นอีเมลที่ฉันพูดถึงตัวระบุที่ไม่ซ้ำใคร
az7ar

34

แน่ใจ: เพียงแค่

นี่คือสิ่งที่คุณต้องการ:

io.to(socket.id).emit("event", data);

เมื่อใดก็ตามที่ผู้ใช้เข้าร่วมเซิร์ฟเวอร์รายละเอียดซ็อกเก็ตจะถูกสร้างขึ้นรวมถึง ID นี่คือรหัสที่ช่วยในการส่งข้อความถึงคนบางคน

ก่อนอื่นเราต้องเก็บ socket.ids ทั้งหมดไว้ในอาร์เรย์

var people={};

people[name] =  socket.id;

ชื่อนี้คือชื่อผู้รับ ตัวอย่าง:

people["ccccc"]=2387423cjhgfwerwer23;

ดังนั้นตอนนี้เราจะได้รับ socket.id พร้อมชื่อผู้รับเมื่อใดก็ตามที่เราส่งข้อความ:

สำหรับสิ่งนี้เราจำเป็นต้องทราบชื่อผู้รับ คุณต้องส่งชื่อผู้รับไปยังเซิร์ฟเวอร์

สิ่งสุดท้ายคือ:

 socket.on('chat message', function(data){
io.to(people[data.receiver]).emit('chat message', data.msg);
});

หวังว่านี่จะเหมาะกับคุณ

โชคดี!!


คุณสามารถสร้างโค้ดฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์ได้หรือไม่? ฉันยังใหม่ในการเขียนโปรแกรม scoket hmahajan.dmi@gmail.com
Harish Mahajan

@HARISH อ้างอิงลิงค์ด้านล่างอธิบายสิ่งที่เกิดขึ้นจริงจากฝั่งไคลเอ็นต์. ฉันหวังว่านี่จะช่วยคุณได้มากขึ้น .. socket.io/get-started/chat
Trojan

2
แต่จะเกิดอะไรขึ้นถ้าผู้ใช้สนทนากับคนอื่น 2 คนในเวลาเดียวกัน ข้อความจากผู้ใช้ทั้งสองจะไม่เข้ามาในหน้าต่างทั้งสอง?
Sharan Mohandas

หากมีผู้ใช้มากกว่าสองคนจะใช้ไม่ได้
Mohhamad Hasham

ทุกครั้งที่ผู้ใช้เชื่อมต่อการเปลี่ยนแปลงรหัสซ็อกเก็ตดังนั้นในกรณีที่ผู้ใช้เปิดแอปของคุณในหลายแท็บแอปจะได้รับการตอบสนองต่อแอปที่คุณจัดเก็บและส่งไปยังการเชื่อมต่อซ็อกเก็ตอื่น ๆ
Vikas Kandari

8

คุณสามารถดู socket.io ห้อง เมื่อคุณจับมือซ็อกเก็ต - คุณสามารถเข้าร่วมกับห้องที่มีชื่อเช่น "user.

หลังจากนั้นคุณสามารถส่งข้อความส่วนตัวถึงลูกค้าโดยใช้ชื่อที่สะดวกเช่น:

io.sockets.in ('user.125'). emit ('new_message', {text: "Hello world"})

ในการดำเนินการข้างต้นเราจะส่ง "new_message" ไปยังผู้ใช้ "125"

ขอบคุณ.


สวัสดีเพื่อนขอบคุณสำหรับคำตอบแรกของคุณฉันจะลองและแจ้งให้คุณทราบเกี่ยวกับเรื่องนี้เพราะฉันไม่ต้องการสร้างการแชท แต่เป็นผู้ส่งสารส่วนตัวอย่างที่คุณสามารถใช้บนเครือข่าย Facebook ได้
Nizar B.

มีใครช่วยฉันในเรื่องนี้ได้ไหมฉันติดขัดจริงๆฉันต้องการเพิ่มชื่อผู้ใช้ที่เชื่อมโยงกับซ็อกเก็ตของฉันและอนุญาตให้แอปพลิเคชันของฉันส่งข้อความไปยังผู้ใช้เฉพาะไม่มีห้องแชทเป็น Facebook เหมือน Messenger ขอบคุณถ้าคุณรู้!
Nizar B.

ในตอนแรกคุณต้องเข้าใจว่าชื่อผู้ใช้นี้จะต้องไม่ซ้ำกันสำหรับผู้ใช้ทั้งหมด และอันที่สอง - คุณไม่ลืมสมัครรับข้อความที่ปล่อยออกมาทางฝั่งไคลเอ็นต์ใช่หรือไม่?
Vladimir Kurijov

ปัญหานี้คือคุณสูญเสียความสามารถในการใช้การโทรกลับเนื่องจากไม่ได้รับอนุญาตในการออกอากาศซึ่งเป็นสิ่งที่คุณกำลังทำอยู่ที่นี่เป็นการออกอากาศไปยังห้องส่วนตัวของเขา
bluehallu

@VladimirKurijov คุณ Katcha ไม่ต้องการใช้ห้องและฉันยอมรับว่ามันไม่ใช่วิธีแก้ปัญหาของเขา
miksiii

4

ในโครงการของ บริษัท เราใช้แนวทาง "ห้อง" และชื่อนี้เป็นการรวมรหัสผู้ใช้ของผู้ใช้ทั้งหมดในการสนทนาเป็นตัวระบุที่ไม่ซ้ำกัน (การใช้งานของเราคล้ายกับ facebook messenger) ตัวอย่างเช่น:

| id | ชื่อ | 1 | สก็อต | 2 | ซูซาน

ชื่อ "ห้อง" จะเป็น "1-2" (รหัสได้รับคำสั่ง Asc.) และเมื่อถอดปลั๊ก socket.io จะทำความสะอาดห้องโดยอัตโนมัติ

ด้วยวิธีนี้คุณจะส่งข้อความไปยังห้องนั้นและเฉพาะผู้ใช้ออนไลน์ (ที่เชื่อมต่อ) เท่านั้น (มีการส่งพัสดุน้อยลงทั่วเซิร์ฟเวอร์)


3

ในฐานะที่เป็นaz7arคำตอบที่มีการกล่าวอย่างสวยงาม แต่ผมขอให้มันง่ายมีห้อง socket.io ขอเซิร์ฟเวอร์ที่มีตัวระบุเฉพาะเพื่อเข้าร่วมเซิร์ฟเวอร์ ที่นี่เราใช้อีเมลเป็นตัวระบุเฉพาะ

Client Socket.io

socket.on('connect', function () {
  socket.emit('join', {email: user@example.com});
});

เมื่อผู้ใช้เข้าร่วมเซิร์ฟเวอร์ให้สร้างห้องสำหรับผู้ใช้นั้น

เซิร์ฟเวอร์ Socket.io

io.on('connection', function (socket) {
   socket.on('join', function (data) {    
    socket.join(data.email);
  });
});

ตอนนี้เราพร้อมที่จะเข้าร่วมแล้ว ปล่อยให้บางสิ่งบางอย่างออกมาจากtoห้องเซิร์ฟเวอร์เพื่อให้ผู้ใช้สามารถฟังได้

เซิร์ฟเวอร์ Socket.io

io.to('user@example.com').emit('message', {msg: 'hello world.'});

ให้จบหัวข้อด้วยการฟังmessageเหตุการณ์จากฝั่งลูกค้า

socket.on("message", function(data) {
  alert(data.msg);
});

ข้อมูลอ้างอิงจาก ห้อง Socket.io


1
ดูเหมือนว่าคำตอบของคุณเป็นสำเนาของ @ az7ar ของคำตอบจากด้านล่างโปรดให้เครดิตถ้าคุณกำลังใช้คำตอบของคนที่มีการปรับเปลี่ยน
ราหุลกระทิง

ขอบคุณ @RahulGaur ใช่มันคล้ายไม่ใช่สำเนา แต่แน่นอนคำตอบของฉันเปลี่ยนไป เขาเคยinฉายภาพเหตุการณ์ แต่ในกรณีของฉันฉันเคยtoปล่อยเหตุการณ์ ฉันหวังว่าคุณจะได้เห็นสิ่งนี้มาก่อน
Lalit Mohan

เป็นเรื่องดีที่ได้ยินว่าไม่ใช่สำเนา แต่ดูเหมือนเป็นแรงบันดาลใจดังนั้นจึงเป็นสิ่งที่ดีที่จะให้เครดิต
Rahul Gaur

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