web-dev-qa-db-ja.com

高速ルートファイル内でsocket.ioを使用する

Node.jsでSocket.ioを使用して、ルートのロジック内のソケットに出力しようとしています。

ルートに配置されたserver.jsファイルを使用したかなり標準的なExpress 3セットアップがあり、次に、サイトのすべてのページ/パブリックにアクセス可能な機能をエクスポートするルートフォルダーに配置されたindex.jsがあります。だから彼らは次のようになります:

exports.index = function (req, res) {
    res.render('index', {
        title: "Awesome page"
    });
}; 

ルーティングはserver.jsで次のように定義されています:

app.get('/',routes.index);

サーバーオブジェクトが必要なため、server.jsにsocket.ioオブジェクトを作成する必要があると想定していますが、そのオブジェクトにアクセスして、index.jsエクスポート関数から出力するにはどうすればよいですか?

19
Matthew Arkin

ルートファイルを関数として設定し、ファイルが必要なときにSocket.IOオブジェクトを渡すことができます。

module.exports = function(io) {
  var routes = {};
  routes.index = function (req, res) {
    io.sockets.emit('payload');
    res.render('index', {
      title: "Awesome page"
    });
  };
  return routes;
};

次に、次のようなルートが必要です:

var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var routes = require('./routes')(io);
17
hexacyanide

これをExpress 4.0で行うより良い方法があります。

app.set() を使用して、ioオブジェクトへの参照を保存できます。

基本構成:

_var app = require('express')();
var server = app.listen(process.env.PORT || 3000);
var io = require('socket.io')(server);
// next line is the money
app.set('socketio', io);
_

ルート内またはミドルウェア:

_exports.foo = function(req,res){
    // now use socket.io in your routes file
    var io = req.app.get('socketio');
    io.emit('hi!');
}
_

app.set()およびapp.get()に関する情報は以下のとおりです。

app.set(name、value)

値に設定名を割り当てます。任意の値を格納できますが、特定の名前を使用してサーバーの動作を構成できます。これらの特別な名前は アプリ設定テーブル にリストされています。

ブール型プロパティに対してapp.set('foo', true)を呼び出すことは、app.enable('foo')を呼び出すことと同じです。同様に、ブール型プロパティに対してapp.set('foo', false)を呼び出すことは、app.disable('foo')を呼び出すことと同じです。

app.get()を使用して設定の値を取得します。

ソース: https://expressjs.com/en/api.html#app.set

80
aarosil

aarosilの答えは素晴らしかったが、このアプローチを使用してクライアント接続を管理する際に、Victorと同じ問題に遭遇しました。クライアントでのリロードごとに、サーバーで同じ数の重複メッセージが表示されます(2番目のリロード= 2重複、3番目= 3重複など)。

Aarosilの答えを拡張して、ルートファイルでソケットオブジェクトを使用して接続を管理し、重複メッセージを制御するために、このアプローチを使用しました。

内部サーバーファイル

// same as aarosil (LIFESAVER)
const app = require('express')();
const server = app.listen(process.env.PORT || 3000);
const io = require('socket.io')(server);
// next line is the money
app.set('socketio', io);

内部ルートファイル

exports.foo = (req,res) => {

   let socket_id = [];
   const io = req.app.get('socketio');

   io.on('connection', socket => {
      socket_id.Push(socket.id);
      if (socket_id[0] === socket.id) {
        // remove the connection listener for any subsequent 
        // connections with the same ID
        io.removeAllListeners('connection'); 
      }

      socket.on('hello message', msg => {
        console.log('just got: ', msg);
        socket.emit('chat message', 'hi from server');

      })

   });
}
12
johnjohn

ただ使うことの何が悪い

global.io = require('socket.io').listen(server);
3
Emad Said

ここで超遅い追加ですが、ルートのソケットにアクセスしたいと思い、特にデータベースに保存した後にメッセージをブロードキャストしたいと思いました。 @aarosilによって提供された回答を使用してioオブジェクトを設定/取得し、各クライアントに接続時にソケットIDを送信してから、ルートでソケットIDを使用してsocket.broadcast.emit()ではなくio.emit()を使用できるようにしました_。

サーバー:

const io = require('socket.io')(server)
app.set('socketio', io)

io.on('connect', socket => {
  socket.emit('id', socket.id) // send each client their socket id
})

各リクエストでソケットIDを送信すると、ルートで次のことができます。

router.post('/messages', requireToken, (req, res, next) => {

  // grab the id from the request
  const socketId = req.body.message.socketId

  // get the io object ref
  const io = req.app.get('socketio') 

  // create a ref to the client socket
  const senderSocket = io.sockets.connected[socketId]

  Message.create(req.body.message)
    .then(message => {

      // in case the client was disconnected after the request was sent
      // and there's no longer a socket with that id
      if (senderSocket) {

        // use broadcast.emit to message everyone except the original
        // sender of the request !!! 
        senderSocket.broadcast.emit('message broadcast', { message })
      }
      res.status(201).json({ message: message.toObject() })
    })
    .catch(next)
})

1
jme11

module.parent.exports.serverは、親モジュールでサーバーをエクスポートした場合にも機能します。

0
Paul Duncan