从路由发出websocket消息

时间:2018-01-14 13:51:22

标签: javascript node.js websocket socket.io

我正在尝试使用websockets设置我的服务器,这样当我通过我的路由更新某些内容时,我还可以在更新该路由上的内容时发出websocket消息。

当有人点击路线/add-team-member时,我的想法就是向我的Mongo数据库保存一些内容,然后向通过websocket连接的所有人发出消息,并且该消息是与该团队对应的任何websocket房间的一部分。

我已按照socket.io的文档以下列方式设置我的应用程序:

App.js

// there's a lot of code in here which sets what to use on my app but here's the important lines

const app = express();
const routes = require('./routes/index');

const sessionObj = {
    secret: process.env.SECRET,
    key: process.env.KEY,
    resave: false,
    saveUninitialized: false,
    store: new MongoStore({ mongooseConnection: mongoose.connection }),
             secret : 'test',
             cookie:{_expires : Number(process.env.COOKIETIME)}, // time im ms    
}

app.use(session(sessionObj));
app.use(passport.initialize());
app.use(passport.session());

module.exports = {app,sessionObj};

start.js

const mongoose = require('mongoose');
const passportSocketIo = require("passport.socketio");
const cookieParser = require('cookie-parser');

// import environmental variables from our variables.env file
require('dotenv').config({ path: 'variables.env' });

// Connect to our Database and handle an bad connections
mongoose.connect(process.env.DATABASE);

// import mongo db models
require('./models/user');
require('./models/team');

// Start our app!
const app = require('./app');
app.app.set('port', process.env.PORT || 7777);

const server = app.app.listen(app.app.get('port'), () => {
  console.log(`Express running → PORT ${server.address().port}`);
});

const io = require('socket.io')(server);

io.set('authorization', passportSocketIo.authorize({
  cookieParser: cookieParser,
  key:         app.sessionObj.key,       // the name of the cookie where express/connect stores its session_id 
  secret:      app.sessionObj.secret,    // the session_secret to parse the cookie 
  store:       app.sessionObj.store,        // we NEED to use a sessionstore. no memorystore please 
  success:     onAuthorizeSuccess,  // *optional* callback on success - read more below 
  fail:        onAuthorizeFail,     // *optional* callback on fail/error - read more below 
}));


function onAuthorizeSuccess(data, accept){}

function onAuthorizeFail(data, message, error, accept){}

io.on('connection', function(client) {  
  client.on('join', function(data) {
      client.emit('messages',"server socket response!!");
  });

  client.on('getmessage', function(data) {
    client.emit('messages',data);
});  

});

我的问题是我的./routes/index文件中有很多mongo数据库保存操作,我希望能够从我的路由发出消息,而不是从start.js结束socket.io连接的位置。

有没有办法可以从我的./routes/index文件中发出websocket消息,即使在start.js中进一步设置了IO?

例如:

router.get('/add-team-member', (req, res) => {
  // some io.emit action here
});

也许我需要移动我正在初始化socket.io的东西,但是却找不到任何关于这个的文档,或者我可以通过某种方式从路由访问socket.io?

感谢并感谢您的帮助,如果有任何不清楚的地方,请告诉我们!

5 个答案:

答案 0 :(得分:2)

是的,只要您在服务器上收到请求,就必须附加socket.io的实例。 查看你的文件start.js,你只需将你的函数替换为:

// Start our app!
const app = require('./app');
app.app.set('port', process.env.PORT || 7777);
const io = require('socket.io')(app.app);

const server = app.app.listen(app.app.get('port'), () => {
server.on('request', function(request, response){
    request.io = io;
}
console.log(`Express running → PORT ${server.address().port}`);
});

现在,当您收到要向客户端发送某些消息的事件时,您可以使用请求对象中的io实例。

router.get('/add-team-member', (req, res) => {
    req.io.sockets.emit('addteammember', {member: 6});
    //as you are doing a broadcast you just need broadcast msg
    ....
    res.status(200)
    res.end()
});

这样做我也能够像mocha一样集成测试框架,并测试发生的事件......

我做了一些类似的集成,根据我的经验,最后要做的是将msg发送到套接字中的实例。

作为一个很好的实践,我开始使用中间件功能进行数据验证,数据清理和清理数据。 这是我的工作示例:

var app = require('../app');
var server = require('http').Server(app);
var io = require('socket.io')(server);

io.on('connection', function(client) {
        client.emit('connected');
        client.on('disconnect', function() {
            console.log('disconnected', client.id);
        });
});

server.on('request', function(request, response) {
    request.io = io;
});

pg.initialize(app.config.DATABASEURL, function(err){
  if(err){
    throw err;
  }

  app.set('port', process.env.PORT || 3000);

    var server1 = server.listen(app.get('port'), function(){
    var host = 'localhost';
    var port = server1.address().port;

    console.log('Example app listening at http://%s:%s', host, port);
  });
});

答案 1 :(得分:1)

您可以使用emiter-adapter将数据发送到其他进程/服务器中的客户端。它使用redis DB作为发送消息的后端。

答案 2 :(得分:1)

如上所述,io属于您的全球范围。如果你这样做

router.get('/add-team-member', (req, res) => {
    io.sockets.emit('AddTeamMember');
});

然后,每个连接的客户端,如果收听该事件AddTeamMember,将在其各自的客户端上运行它的相关.on功能。这可能是最简单的解决方案,除非您在没有任何负载平衡计划的情况下期待大量用户,否则这应该是适合的。

你可以选择另一种选择: socket.io lib有一个房间功能,你可以使用io对象本身加入和发出https://socket.io/docs/rooms-and-namespaces/如果你有这个诀窍,它看起来像这样:

io.sockets.in('yourroom').broadcast('AddTeamMember');

这基本上与顶部做同样的事情,而不是向每个客户广播,它只向那个房间专用的人广播。您必须基本上找到一种方法来让用户插入房间//在他们发出get请求之前,或者换句话说,让它们成为独占的。这样,您可以减少服务器在发出路由请求时必须推出的负载量。

最后,如果以上两个选项都不适合你,并且你只需要在发起它时发送给那个单一的客户端,那么它就会变得混乱,因为你必须有某种对于那个人的id,由于你没有引用,你必须在连接时存储所有的套接字,然后进行比较。我没有完全推荐这样的东西,因为我还没有测试过它,也不知道会发生什么样的反响,但这里有一个我想法的主旨:

app.set('trust proxy', true)
var SOCKETS = []
io.on('connection', function(client) {
  SOCKETS.push(client);
  client.on('join', function(data) {
    client.emit('messages',"server socket response!!");
  });

  client.on('getmessage', function(data) {
    client.emit('messages',data);
  });
});

router.get('/add-team-member', (req, res) => {
    for (let i=0; i< SOCKETS.length; i++){
        if(SOCKETS[i].request.connection.remoteAddress == req.ip)
          SOCKETS[i].emit('AddTeamMember');
    }
});

请记住,如果您沿着这条路走下去,当用户断开连接时,您将需要维护该阵列,如果您正在进行会话管理,那么它真的会变得毛茸茸非常快。

祝你好运,让我们知道你的结果。

答案 3 :(得分:1)

我过去做过类似的事情,使用namespaces

假设您的客户端使用“Frontend”作为命名空间连接到您的服务器。 我的解决方案是在单独的文件中创建socket.io实例作为类:

<强>的WebSockets / index.js

nullptr

然后在 App.js

const socket = require('socket.io');

class websockets {
  constructor(server) {
    this.io = socket(server);
    this.frontend = new Frontend(this.io);

    this.io.use((socket, next) => {
      // put here the logic to authorize your users..
      // even better in a separate file :-)
      next();
    });
  }
}

class Frontend {
  constructor(io) {
    this.nsp = io.of('/Frontend');

    [ ... ]
  }
}

module.exports = websockets;

最后,您的路线可以:

<强>路由/ index.js

const app = require('express')();
const server = require('http').createServer(app);
const websockets = require('./websockets/index');
const WS = new websockets(server);

app.use('/', (req, res, next) => {
  req.websocket = WS;
  next();
}, require('./routes/index'));

[ ... ]

答案 4 :(得分:1)

您的io实际上是套接字对象,您可以通过 -

从该对象向任何特定用户发出事件
io.to(userSocketId).emit('eventName', data);

或者您可以通过 -

进行广播
io.emit('eventName', data);

在使用之前创建require socket.io:)