【发布时间】:2014-12-10 12:22:54
【问题描述】:
我正在尝试编写一个没有 socket.io 的具有服务器发送事件功能的简单 node.js 服务器。我的代码运行良好,但我在测试时遇到了一个大问题。
如果我从浏览器连接到 node.js 服务器,它将正常接收服务器事件。但是当我刷新连接时,同一个浏览器将开始接收两次事件。如果我刷新n次,浏览器会收到n+1次数据。
在我停止尝试之前,我最多尝试了 16 次(16 次刷新)。
在我的浏览器中查看控制台下面的屏幕截图。刷新后(它尝试进行 AJAX 调用),它将输出“SSE 持久连接建立!”然后它将开始接收事件。
注意事件到达的时间。我在 19:34:07 收到了两次事件(它记录到控制台两次 - 在收到事件并将数据写入屏幕时;因此您可以在那里看到四个日志)。
我也在 19:34:12 获得了两次活动。
这是客户端关闭连接后服务器端的样子(使用 source.close() ):
如您所见,它仍在尝试向客户端发送消息!此外,它尝试发送消息两次(所以我知道这是服务器端问题)!
我知道它尝试发送两次,因为它发送了两次心跳,而它本应每 30 秒发送一次。
当打开 n 个标签时,这个问题会放大。发生的情况是每个打开的选项卡都会收到 n*n 个事件。基本上,我的解释是这样的:
打开第一个选项卡,我订阅了一次服务器。打开第二个选项卡,我再次将两个打开的选项卡订阅到服务器——所以每个选项卡有 2 个订阅。打开第三个选项卡,我再次为所有三个打开的选项卡订阅事件,因此每个选项卡有 3 个订阅,总共 9 个。依此类推...
我无法验证这一点,但我的猜测是,如果我可以创建一个订阅,那么如果满足某些条件(即心跳失败,我必须断开连接),我应该能够取消订阅。发生这种情况的原因仅在于以下几点:
- 我启动了一次 setInterval,它的一个实例将永远运行,除非我停止它,或者
- 服务器仍在尝试向客户端发送数据以保持连接打开?
至于 1.,我已经尝试用 clearInterval 杀死 setInterval,它不起作用。至于2,虽然这可能是不可能的,但我倾向于相信......
这里只是相关部分的服务器端代码sn-ps(根据答案的建议编辑代码):
server = http.createServer(function(req, res){
heartbeatPulse(res);
}).listen(8888, '127.0.0.1', 511);
server.on("request", function(req, res){
console.log("Request Received!");
var route_origin = url.parse(req.url);
if(route_origin.query !== null){
serveAJAX(res);
} else {
serveSSE(res);
//hbTimerID = timerID.hbID;
//msgTimerID = timerID.msgID;
}
req.on("close", function(){
console.log("close the connection!");
res.end();
clearInterval(res['hbID']);
clearInterval(res['msgID']);
console.log(res['hbID']);
console.log(res['msgID']);
});
});
var attachListeners = function(res){
/*
Adding listeners to the server
These events are triggered only when the server communicates by SSE
*/
// this listener listens for file changes -- needs more modification to accommodate which file changed, what to write
server.addListener("file_changed", function(res){
// replace this with a file reader function
writeUpdate = res.write("data: {\"date\": \"" + Date() + "\", \"test\": \"nowsssss\", \"i\": " + i++ + "}\n\n");
if(writeUpdate){
console.log("Sent SSE!");
} else {
console.log("SSE failed to send!");
}
});
// this listener enables the server to send heartbeats
server.addListener("hb", function(res){
if(res.write("event: hb\ndata:\nretry: 5000\n\n")){
console.log("Sent HB!");
} else {
// this fails. We can't just close the response, we need to close the connection
// how to close a connection upon the client closing the browser??
console.log("HB failed! Closing connection to client...");
res.end();
//console.log(http.IncomingMessage.connection);
//http.IncomingMessage.complete = true;
clearInterval(res['hbID']);
clearInterval(res['msgID']);
console.log(res['hbID']);
console.log(res['msgID']);
console.log("\tConnection Closed.");
}
});
}
var heartbeatPulse = function(res){
res['hbID'] = "";
res['msgID'] = "";
res['hbID'] = setInterval(function(){
server.emit("hb", res);
}, HB_period);
res['msgID'] = setInterval(function(){
server.emit("file_changed", res);
}, 5000);
console.log(res['hbID']);
console.log(res['msgID'])
/*return {
hbID: hbID,
msgID: msgID
};*/
}
var serveSSE = function(res){
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Access-Control-Allow-Origin": "*",
"Connection": "keep-alive"
});
console.log("Establishing Persistent Connection...");
if(res.write("event: connector\ndata:\nretry: 5000\n\n")){
// Only upon receiving the first message will the headers be sent
console.log("Established Persistent Connection!");
}
attachListeners(res);
console.log("\tRequested via SSE!");
}
这主要是一个自我学习项目,因此绝对欢迎任何 cmet。
【问题讨论】:
标签: javascript node.js connection keep-alive server-sent-events