Node.JS 服务器发送的事件:路由在 res.end() 之后继续到 运行,导致 ERR_STREAM_WRITE_AFTER_END 错误

Node.JS Server Sent Events: Route continues to run after res.end() resulting in ERR_STREAM_WRITE_AFTER_END error

我开始使用服务器发送事件 (SSE),因为我的 Web 应用程序需要从服务器接收实时更新。它不需要向服务器发送任何内容,因此选择了 SSE 而不是 Websockets。

阅读一些示例后,我有以下代码:

On my server, in ./src/routers/mainRouter.js I have:

router.get('/updates', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    })

    // Listens for 'event' and sends an 'Event triggered!' message to client when its heard.
    eventEmitter.addListener('event', () => {
        console.log('Event triggered! Sending response.')
        res.write('data: Event triggered!\n\n')
    })

    req.on('close', () => {
        console.log('Connection to client closed.')
        res.end()
    })
})

module.exports = router

On my client, in ./app/index.js I have:

const source = new EventSource('/updates')

source.onmessage = (e) => {
    console.log(e)
}

我遇到了 2 个问题:

  1. 一旦我从客户端打开一个连接然后关闭 连接(通过关闭选项卡),'close' 事件触发两次 导致 req.on('close') 运行 内的代码块两次。 我不确定为什么会这样。我的 console 在服务器端 看起来如下:

    Event triggered! Sending response.
    Connection to client closed.
    Connection to client closed.
    
  2. 更重要的是,虽然调用了req.end(),路由器仍然 继续监听该频道上的事件并尝试发送 沿着该通道的响应导致 ERR_STREAM_WRITE_AFTER_END 错误,服务器崩溃。所以 最终控制台输出如下:

    Event triggered! Sending response. // First event triggers.
    Connection to client closed. // 'close' event fires.
    Connection to client closed. // 'close' event fires a second time (not sure why).
    Event triggered! Sending response. // Router continues listening for 'event' and sends another response although res.end() was called earlier
    events.js:187
          throw er; // Unhandled 'error' event
          ^
    
    Error [ERR_STREAM_WRITE_AFTER_END]: write after end
    

当流关闭时,您需要删除事件侦听器,这样您就不会再次尝试写入流。可以这样做:

router.get('/updates', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    function listener(event) {
        console.log('Event triggered! Sending response.');
        res.write('data: Event triggered!\n\n');
    }

    // Listens for 'event' and sends an 'Event triggered!' message to client when its heard.
    eventEmitter.addListener('event', listener);

    req.on('close', () => {
        // remove listener so it won't try to write to this stream any more
        eventEmitter.removeListener('event', listener);
        console.log('Connection to client closed.');
        res.end();
    });
});

module.exports = router;

仅供参考,如果您已经收到 close 事件,我认为您不需要 res.end()。如果您单方面尝试关闭与服务器的连接,您将使用 res.send(),但如果它已经关闭,我认为您不需要它和 none 我见过的代码示例使用就这样。

我想知道您的 res.end() 是否也是您收到两个 close 事件的原因。尝试删除它。