socket.io |我应该将路由处理程序包装在 io.on('connection') 中吗?

Posted

技术标签:

【中文标题】socket.io |我应该将路由处理程序包装在 io.on(\'connection\') 中吗?【英文标题】:socket.io | Should I wrap my route handlers inside io.on('connection')?socket.io |我应该将路由处理程序包装在 io.on('connection') 中吗? 【发布时间】:2021-12-21 15:27:25 【问题描述】:

在下面的代码中,我假设我的路由处理程序有可能会在io 连接建立之前触发并尝试发送到套接字:

server.js:

import  Server  from 'socket.io'
....
....
const app = express()
const io = new Server(....)

app.io = io

app.post('/something', (req, res) => 
  req.app.io.emit('something', doSomethingWith(req.body))
  res.status(200)
)


io.on('connection', function(socket) 
  console.log('socket connected')

  socket.on('disconnect', (reason) => 
    console.log('disconnected due to = ', reason)
  )

)

client.js:

socket = io(`http://localhost:$port`,  transports: ['websocket'] )

socket.on('something', (data) => 
  doSomethingMoreWith(data)
)

fetch('/something', ....)

在这种情况下,这样做是否更安全:

io.on('connection', function(socket) 
  app.post('/something', ....)
  app.get('/something', ....)
  .....
  ...

  socket.on('disconnect', (reason) => 
    console.log('disconnected due to = ', reason)
  )

)

还是不建议这样做,有更好的选择?

【问题讨论】:

【参考方案1】:

app.post()app.get() 放在io.on('connection', ...) 中绝不是正确的设计或实现。这是因为io.on('connection', ...) 被多次触发,并且一遍又一遍地添加相同的快速路由处理程序没有意义,因为这只会浪费内存并且没有任何用处。第一个在 socket.io 上连接的客户端将导致路由被注册,并且从那时起,所有其他客户端(无论它们是否通过 socket.io 连接)都会在那里。

目前尚不清楚您为什么要这样做。您不会为一种特定情况安装路线。为所有州的所有客户端安装一次路由。因此,如果您尝试有条件地安装路由,那么这种类型的设计将不起作用。如果您进一步解释您要完成的工作,那么也许我们可以为设计提出一些不同的建议。

在下面的代码中,我假设我的路由处理程序有可能会在 io 连接建立之前触发并尝试发送到套接字:

app.post('/something', (req, res) => 
  req.app.io.emit('something', doSomethingWith(req.body))
  res.status(200)
);

此代码的具体工作方式取决于 POST 执行的操作。如果它是现有页面中的 javascript,那么该页面将已经启动并初始化,并且您可以控制(使用页面中的 Javascript 客户端代码)是否等待向/something 发出 POST 直到建立 socket.io 连接.

如果此 POST 是常规的基于浏览器的表单提交(不涉及 Javascript),那么您还有其他问题,因为来自浏览器的表单提交会使用 POST 的响应重新加载当前浏览器页面,并且在重新加载的过程中该页面会终止该页面具有的任何现有 socket.io 连接(因为它加载了一个新页面)。由于您没有从 POST 发回任何内容,这将导致浏览器中显示一个空白页面并且没有 socket.io 连接。

在查看您的客户端代码时,似乎 POST 来自客户端代码中的 fetch()(因此完全基于 Javascript)。如果是这种情况,我建议重组您的客户端代码,以便它等到 socket.io 连接完成连接后再执行fetch()。这样一来,您就知道您将能够收到服务器所做的io.emit()

socket = io(`http://localhost:$port`,  transports: ['websocket'] )

socket.on('something', (data) => 
  doSomethingMoreWith(data)
);

socket.on('connect', () => 
   // only issue fetch after socket.io connection is operational
  fetch('/something', ....)
);

【讨论】:

好的,所以基本上你的最后一段就是我问题的答案:)。我担心在建立套接字连接之前我的 fetch 可能会消失。所以根据你的建议,我假设socket.on('connect') 只会触发一次,对吗? @SproutCoder - connect 事件在第一次连接时触发,然后如果连接因任何原因中断并且客户端自动重新连接,它将为此再次触发。如果你想确保 fetch() 只被调用一次,你可以为你是否已经调用过 fetch() 维护一个更高范围的标志,并在调用之前检查标志。 是的,我可能会使用一些 alreadyFired 布尔值之类的。谢谢你的解释!

以上是关于socket.io |我应该将路由处理程序包装在 io.on('connection') 中吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 node.js 和 socket.io 应用程序中组织套接字处理

如何使用集群处理 Socket.IO 房间?

Socket.io 连接恢复为轮询,从不触发“连接”处理程序

Socket.io:如果在快速路由中使用,事件会触发两次

是否可以将多个处理程序添加到同一个 socket.io.on 事件?

将 Node.js+Socket.io 包装成 OSX 可执行文件的可靠方法? (或用作替代品的 C/C++/Objective-C 库)