socket.ioとnode.jsを使用して特定のクライアントにメッセージを送信する


189

私はsocket.ioとnode.jsを使用していますが、今のところかなり良いようですが、サーバーから特定のクライアントにメッセージを送信する方法がわかりません。

client.send(message, receiverSessionId)

しかし.send().broadcast()メソッドもメソッドも私のニーズを満たしていないようです。

私が可能な解決策として見つけたのは、.broadcast()メソッドがメッセージを送信しないSessionIdの配列を2番目のパラメーターとして受け入れるため、その時点で接続されているすべてのSessionIdを含む配列をサーバーに渡すことができるということです。メッセージを送りたいのですが、もっと良い解決策があるはずだと思います。

何か案は?

回答:


95

さてあなたはそのためにクライアントをつかむ必要があります(驚き)、あなたはどちらかの簡単な方法をとることができます:

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

これは壊れるio.clientsかもしれませんが、疑いますが、常に変更される可能性があるため、上記を慎重に使用してください

または、クライアントを自分で追跡するため、クライアントをリスナーの独自のclientsオブジェクトに追加し、connectionリスナーで削除しdisconnectます。

私は後者のものを使用します。アプリケーションによっては、とにかくクライアントでより多くの状態clients[id] = {conn: clientConnect, data: {...}}が必要になる可能性があるため、のようなものが仕事をするかもしれないからです。


6
Ivo、もっと完全な例を示すか、少し詳しく説明できますか?このアプローチを理解したいと思っていますが、この例で使用している変数/オブジェクトを認識しているかどうかはわかりません。clients [id] = {conn:clientConnect、data:{...}}で、上のio.clients [sessionID]にあるように、clients [id]はioオブジェクトの一部ですか?また、clientConnectオブジェクトとは何ですか?ありがとう。
AndrewHenderson 2012

@ ivo-wetzelこんにちは、このトピックについて教えていただけますか?stackoverflow.com/questions/38817680/...
マハディpishguy

178

Ivo Wetzelの答えは、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(...)必要がありました:(名前空間を使用しているためかどうかはわかりません)
Adrien Schuler

クラスタ環境で、ソケットが属する正しいプロセスがメッセージを送信していることをどのように確認しますか?
Gal Ben-Haim 2013年

NGINXまたはHAProxy @Gal Ben-Haimの厚意による付箋セッションはどうですか?
matanster 2013

varholder = new 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)at Module.load(module.js:355:32)at Function.Module._load(module.js:310:12)at Function.Module。 runMain(module.js:501:10)、起動時(node.js:129:16)、node.js:814:3
Lucas

1
io.sockets.socket(socket_id)socket.io 1.0で削除されました。github.com/socketio/socket.io/issues/1618#issuecomment-46151246
ImMathan

96

各ソケットは名前のソケットIDで部屋に参加するので、

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

docs: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"]。emit( "socketFromServe‌ r"、info); ^ TypeError:undefinedのプロパティ 'emit'を読み取れません
Raz

85

最もシンプルで最もエレガントなソリューション

簡単です:

client.emit("your message");

以上です。

しかし、どうやって?例を挙げて

私たち全員に必要なのは、実際には完全な例であり、それが次のとおりです。これは、最新のsocket.ioバージョン(2.0.3)でテストされており、最新のJavascript(これまでにすべて使用しているはずです)も使用しています。

この例は、サーバーとクライアントの2つの部分で構成されています。クライアントが接続するたびに、サーバーから定期的なシーケンス番号の受信を開始します。新しいクライアントごとに新しいシーケンスが開始されるため、サーバーはそれらを個別に追跡する必要があります。ここで、「特定のクライアントにメッセージを送信する必要があります」が機能します。コードは非常に簡単に理解できます。見てみようよ。

サーバ

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

ここに完全なコードの要旨も用意しました


運用中のsocketioの標準はポート8000​​ですか?

1
申し訳ありませんが、@ ingoさん、返信に時間がかかりました。8000は、両方のWebSocketまたはHTTPサーバーをテストするときにノードコミュニティが使用する共通ポートです(3000も一般的です)。もちろんそれを本番環境で使用することもできますが、それがどれほど一般的であるかはわかりません...とにかく、ゲートウェイ/ロードバランサーなどがそのために準備されている限り、実際にはどのポートでも使用できます。
Lucio Paiva

私はPython / PHPプログラマーで、ソケットとノードは初めてですが、問題は、なぜ同じユーザーのシーケンス番号を毎秒インクリメントする必要があるのですか?デモ用ですか?
Umair 2018

1
@Umairこれは単なるデモ用であり、シーケンス番号を増やす必要はありません。それはあなたが各クライアントにものを送り続けることができ、彼らがそれを受け取ることを示すためだけでした。
Lucio Paiva 2018

この例では、特定のクライアントがメッセージを別のクライアントにのみ送信する方法は示していません。各クライアントは、デモ用に名前を付けて作成したときの独自のシーケンス番号しか認識せず、このシーケンス番号から、ソケットIDを持つクライアントに直接メッセージを送信するために必要なソケットIDへのマップはありません。
セルチュク2018年

35

使用できます

//送信者クライアントのみにメッセージを送信します

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

35

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"]。emit( "socketFromServer"、info); ^ TypeError:undefinedのプロパティ 'emit'を読み取れません
Raz

これはおそらくapiが更新されio.sockets.connected["something"]、そのようなオブジェクトとそのemitメソッドがなくなったためです。
セルチュク2018年

12

サーバー側のnodejsコードで使用する "io"オブジェクト(たとえば、io.on( 'connection'、function(socket){...});]をconsole.log()にした場合、どのバージョンを使用しているか、「io」は単なるjsonオブジェクトであり、ソケットIDとソケットオブジェクトが格納される多くの子オブジェクトがあることがわかります。

私はsocket.ioバージョン1.3.5を使用しています。

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:undefinedのプロパティ 'emit'を読み取れません
Raz

現在(バージョン2.1.1)、これはsockets.connectedでのみ機能し、他の2つでは機能しませんでした。
ジェームズ

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の時点で、io.to()に適切なプレフィックス付きのsocketIdを指定してください。クライアントがデバッグのためにログに記録したsocketIdを取得しましたが、プレフィックスがなかったため、気付くまで永久に検索してしまいました!そのため、使用しているIDがプレフィックスされていない場合は、次のようにする必要があります。

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

6

io.sockets.sockets [socket.id] .emit(...)はv0.9で機能しました


Stack Overflowへようこそ。この回答は、既存の回答に多くの関連を加えているようには見えません。評判が高まると、他のユーザーの投稿コメントできるようになります。これはコメントに適しているようです。
ジェリー2014

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

次に、オブジェクトからIDで特定のクライアントを呼び出します

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.