res.write()在socket.on方法内无法正常工作的问题排查
问题:Express SSE接口res.write无输出,但服务器日志正常打印
我编写了如下Express路由代码,当请求命中/optimizationStatus接口时,会通过socket.emit发送payload数据到optimization事件,但遇到了问题:
router.get('/optimizationStatus', (req, res) => { console.log('test'); res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }); let data = { .. some Data }; socket.emit('optimization', data); socket.on('message', (data) => { console.log('MESSAGE', data); res.write(`data: ${JSON.stringify(data)} `); }); req.on('close', () => { socket.off('optimization'); }); });
问题表现:socket.on('message')回调内的console.log('MESSAGE', data)能正常打印数据,但res.write无法向客户端输出内容。用Postman测试时,请求一直处于加载状态,但服务器端能看到日志输出。
解决方法
1. 手动调用res.flush()发送缓冲区数据
Express的res.write不会自动刷新输出缓冲区,数据会暂存在内存中,客户端无法接收。需要在每次res.write后调用res.flush()(Express 4.16+版本支持):
socket.on('message', (data) => { console.log('MESSAGE', data); res.write(`data: ${JSON.stringify(data)}\n\n`); res.flush(); // 强制发送缓冲区数据到客户端 });
2. 修复事件监听的移除逻辑
当前req.close回调里移除的是optimization事件监听,但实际绑定的是message事件,这会导致内存泄漏。应该先把回调函数存为变量,再精准移除:
// 提取回调函数为变量 const messageHandler = (data) => { console.log('MESSAGE', data); res.write(`data: ${JSON.stringify(data)}\n\n`); res.flush(); }; socket.on('message', messageHandler); req.on('close', () => { socket.off('message', messageHandler); // 移除对应的message监听 });
3. 发送初始确认消息
部分客户端(包括Postman)需要先收到一条合法的SSE消息才会开始展示内容。可以在路由初始化时先发送一条连接确认消息:
res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }); // 发送初始连接确认 res.write('data: {"status": "connected"}\n\n'); res.flush();
4. 确保SSE消息格式正确
SSE要求每条消息必须以\n\n结尾,建议直接使用转义字符而非模板字符串换行,避免格式错误:
// 用\n\n替代模板字符串中的换行 res.write(`data: ${JSON.stringify(data)}\n\n`);
内容的提问来源于stack exchange,提问作者SeDi
相关产品推荐
相关产品推荐

