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エクスポート関数から出力するにはどうすればよいですか?
ルートファイルを関数として設定し、ファイルが必要なときに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);
これを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()
を使用して設定の値を取得します。
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');
})
});
}
ただ使うことの何が悪い
global.io = require('socket.io').listen(server);
ここで超遅い追加ですが、ルートのソケットにアクセスしたいと思い、特にデータベースに保存した後にメッセージをブロードキャストしたいと思いました。 @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)
})
module.parent.exports.server
は、親モジュールでサーバーをエクスポートした場合にも機能します。