使用websockets跟踪用户在线状态时是否有重复的事件处理程序?

时间:2018-02-27 15:16:18

标签: node.js events socket.io sails.js sails.io.js

我想使用websockets来跟踪用户上次看到的时间,以及他们当前的在线状态(如果他们当前已经过身份验证并使用该应用程序)。

我有适用于应用程序某些部分的策略,因此用户将登录,然后转到策略适用的概述。

如果用户未经过身份验证,或者他们是,但会话信息与数据库的会话信息冲突,则概述将以notFound响应。

如果这些简单检查通过,则用户存在,因此应创建两个事件处理程序:

  1. 如果会话到期之前没有活动,请通知客户返回身份验证页面
  2. 当客户端断开连接时,请更新上次查看时间并更改在线状态
  3. 以下是政策代码:

    const schedule = require('node-schedule')
    var sheduledJob
    
    module.exports = async function isSignedIn(req, res, next) {
    
      // If the user has not signed in, pretend the page does not exist
      if (!req.session.app || !req.session.app.userId) return res.notFound()
    
      // Store the users id
      let userId = req.session.app.userId
    
      try {
        // Update the user
        var users = await User.update({
          id: userId
        })
        .set({
          last_accessed: new Date().getTime(),
          online: true
        })
        .meta({
          fetch:true
        })
      } catch (err) {
        // Handle errors
        return res.serverError(err.message)
      }
    
      // If the user does not exist
      if (!users || users.length !== 1) {
        console.log('policies/isSignedIn: no matching user '+userId)
        // Sign the user out
        delete(req.session.app)
        // Pretend the page does not exist
        return res.notFound()
      }
    
      // When the user exists
      else {
        // When there is a new client connection
        var io = sails.io
        io.on('connection', socket => {
          console.log("==> Socket Connection Established")
          console.log(socket.id, socket.request.headers.cookie.replace('sails.sid=',''))
    
          // Cancel an existing job if one exists
          if (sheduledJob) sheduledJob.cancel()
          // Shedule a job to notify the client when the session has expired
          var d = req.session.cookie._expires
          sheduledJob = schedule.scheduleJob(d, () => {
            console.log('The cookie has expired')
            // The client should return to the auth page (will fire disconnecting when doing so)
            req.socket.emit('logout', true)
          })
    
          // When the client is disconnecting
          socket.on('disconnecting', async reason => {
            console.log('Client disconnecting')
            // As of Sails v1.0.0-46 reason is undefined when the application is lowering
            if (reason) {
              try {
                // Update the user
                var users = await User.update({
                  id: userId
                })
                .set({
                  last_accessed: new Date().getTime(),
                  online: false
                })
              } catch (err) {
                // Handle errors
                return res.serverError(err.message)
              }
            }
          })
        })
    
        // Proceed as the user exists and we can handle the disconnecting event
        return next()
      }
    
    };
    

    我遇到的问题是,这会在最初加载页面时起作用,但如果我重新加载概述页面,那么我最终会遇到重复的事件处理程序:

    加载页面一次(记住会话年龄为10秒进行测试):

    ==> Socket Connection Established
    <socketId> <sessionId>
    The cookie has expired
    Client disconnecting
    

    但如果我重新加载页面:

    ==> Socket Connection Established
    <socketId> <sessionId>
    Client disconnecting
    ==> Socket Connection Established
    <socketId> <sessionId>
    ==> Socket Connection Established
    <socketId> <sessionId>
    The cookie has expired
    Client disconnecting
    Client disconnecting
    

    所以我认为没关系,如果是这样的话那么我可以为事件监听器创建一个命名函数,然后最初删除事件监听器:

    const schedule = require('node-schedule')
    var sheduledJob
    function connectionHandler(req, res, socket) {
      console.log("==> Socket Connection Established")
      console.log(socket.id, socket.request.headers.cookie.replace('sails.sid=',''))
      ...same as before...
    }
    
    module.exports = async function isSignedIn(req, res, next) {
    
     ...
    
     // When the user exists
     else {
       // When there is a new client connection
       var io = sails.io
       var nsp = io.of('/')
       nsp.removeListener('connection', connectionHandler)
       io.on('connection', socket => connectionHandler(req, res, socket))
    
       // Proceed as the user exists and we can handle the disconnecting event
       return next()
     }
    
    };
    

    但是这导致了相同的重复处理程序,所以我从处理程序中删除了所有req / res代码:

    function connectionHandler(socket) {
      console.log("==> Socket Connection Established")
      console.log(socket.id, socket.request.headers.cookie.replace('sails.sid=',''))
      socket.on('disconnecting', async reason => {
        console.log('Client disconnecting')
      })
    }
    

    在创建事件时进行修改:

    io.on('connection', connectionHandler)
    

    结果按照我的意图工作,重新加载页面时没有创建任何重复的事件处理程序:

    ==> Socket Connection Established
    <socketId> <sessionId>
    Client disconnecting
    ==> Socket Connection Established
    <socketId> <sessionId>
    

    有人可以向我解释我在哪里出错了,我真的不明白为什么:

    io.on('connection', socket => connectionHandler(req, res, socket))
    

    重复事件处理程序的结果,而:

    io.on('connection', connectionHandler)
    

    不是吗?

    如果有人可以提出任何有关我在这里出错的建议,或者我如何能够更好地达到预期的结果,那么我们将非常感谢,非常感谢!

    以下是我过去常用的一些参考资料:

    1. https://gist.github.com/mikermcneil/6598661
    2. https://github.com/balderdashy/sails-docs/blob/1.0/concepts/Sessions/sessions.md#when-does-the-sailssid-change
    3. how to disconnect socket on session expire
    4. https://stackoverflow.com/a/5422730/2110294
    5. https://github.com/expressjs/session/issues/204#issuecomment-141473499
    6. https://stackoverflow.com/a/33308406/2110294

1 个答案:

答案 0 :(得分:0)

我做了一些似乎已经解决的改变:

const schedule = require('node-schedule')
var sheduledJob;

module.exports = async function isSignedIn(req, res, next) {

  // If the user has not signed in, pretend the page does not exist
  if (!req.session.app || !req.session.app.userId) return res.notFound()

  // Store the users id and ip
  let userId = req.session.app.userId
  let userIp = req.headers['x-real-ip']

  try {
    // Update the user
    var users = await User.update({
     id: userId
    })
    .set({
     last_accessed: new Date().getTime(),
     last_accessed_ip: userIp,
     online: true
    })
    .meta({
     fetch:true
    })
  } catch (err) {
    // Handle errors
    return res.serverError(err.message)
  }

  // If the user does not exist
  if (users.length !== 1) {
    console.log('policies/isSignedIn: no matching user '+userId)
    // Sign the user out
    delete(req.session.app)
    // Pretend the page does not exist
    return res.notFound()
  }

  // When the user exists
  else {
    // When there is a new client connection
    var io = sails.io

    io.on('connection', function connectionHandler(socket) {
      // console.log("==> Socket Connection Established")
      // console.log(socket.id, socket.request.headers.cookie.replace('sails.sid=',''))

      // Cancel the existing job if one exists
      if (sheduledJob) sheduledJob.cancel()
      // Shedule a job to notify the client when the session has expired
      var d = req.session.cookie._expires
      sheduledJob = schedule.scheduleJob(d, () => {
        //console.log('The cookie has expired')
        // Sign the user out
        delete req.session.app
        // The client should return to the auth page
        socket.emit('session-expired', 'Your session has expired!')
      })

      // When the client is disconnecting
      socket.on('disconnecting', async function disconnectingHandler(reason) {
        //console.log('Client disconnecting: '+reason)
        // Cancel the existing job if one exists
        if (sheduledJob) sheduledJob.cancel()
        // Remove any event handlers that were created
        io.sockets.removeAllListeners('connection')
        io.sockets.removeAllListeners('disconnecting')
        // As of Sails v1.0.0-46 reason is undefined when the application is lowering
        if (reason) {
          try {
            // Update the user
            var users = await User.update({
              id: userId
            })
            .set({
              last_accessed: new Date().getTime(),
              last_accessed_ip: userIp,
              online: false
            })
          } catch (err) {
            // Handle errors
            return res.serverError(err.message)
          }
        }
      })
    })

    // Proceed as the user exists and we can handle the disconnecting event
    return next()
  }

};

我认为这里的主要问题是我没有正确删除事件监听器,这需要在解除断开连接事件时发生。

根据https://github.com/expressjs/session#rolling,每个请求刷新的会话都需要向config.sessions添加rolling: true,到期日期由config.session.cookie.maxAge确定。