一、长轮询是与服务器保持持久连接的最简单的方式,它不使用任何特定的协议,例如WebSocket或者Server Sent Event。
定义
一、页面向服务器发起一个请求,服务器一直保持tcp连接打开,知道有数据可发送。发送完数据后,页面关闭该连接,随即又发起一个新的服务器请求,在这一过程中循环。
流程
一、流程
1、请求发送到服务器
2、服务器在有消息之前不会关闭连接
3、当消息出现时:服务器将对其请求作出响应
4、浏览器立即发出一个新的请求。
二、对于此方法,浏览器发出一个请求并与服务器之间建立起一个挂起的(pending)连接的情况是标准的。仅在有消息被传递时,才会重新建立连接。
1、如果消息比较频繁,那么上面描绘的请求-接收(requesting-reveiving)消息的图标就会变成锯状(saw-like)
三、如果连接丢失,可能是因为网络错误,浏览器会立即发送一个新请求。
| 【示例】实现长轮询的客户端subscribe函数```javascript /**
subscribe函数发起了一个fetch,然后等待响应,处理它,并再次调用自身。 */ async function subscribe() { let response = await fetch(‘/subscribe’);
if (response.status == 502) {
// 状态502是发生连接超时错误
// 连接挂起时间过长时可能会发生 // 远程服务器或代理会关闭它 // 让我们重新连接 await subscribe(); } else if (response.status != 200) {
// 一个error:让我们显示它
showMessage(response.statusText); // 一秒后重新连接 await new Promise(resolve => setTimeout(resolve, 1000)); await subscribe(); } else {
// 获取并显示信息
let message = await response.text(); showMessage(message); // 再次调用subscribe()以获取下一条消息 await subscribe(); } }
subscribe();
|
| --- |
<a name="jOwqV"></a>
# 优缺点
一、优点
- “长轮询”很容易实现,并且可以无延迟地传递消息
二、缺点
- 每个消息都是单独的请求,并带有header,身份验证开销(authentication overheader)等。
- 在这种情况下,首选另一种方法,如Websocket或Server Sent Events
- 服务器架构必须能够处理许多挂起的连接。
> 1、某些服务器架构是每个连接对应一个进程,导致进程数和连接数一样多,而每个进程都会消耗相当多的内存。因此,过多的连接会消耗掉全部内存。
> 2、使用像PHP和Ruby语言编写的后端程序会经常遇到这个问题。
> 3、使用Node.js编写的服务端程序通常不会出现此类问题。
> 4、这不是编程语言的问题。大多数现代编程语言,包括PHP和Ruby,都允许实现更适当的后端程序。只是请确保你的服务器架构在同时有很多连接的情况下能够正常工作。
<a name="P4F3K"></a>
# 使用场景
一、在消息很少的情况下,长轮询很有效。
| 【示例】聊天演示<br />1、index.html```html
<!DOCTYPE html>
<script src="browser.js"></script>
All visitors of this page will see messages of each other.
<form name="publish">
<input type="text" name="message" />
<input type="submit" value="Send" />
</form>
<div id="subscribe">
</div>
<script>
new PublishForm(document.forms.publish, 'publish');
// random url parameter to avoid any caching issues
new SubscribePane(document.getElementById('subscribe'), 'subscribe?random=' + Math.random());
</script>
2、browser.js```javascript // Sending messages, a simple POST function PublishForm(form, url) {
function sendMessage(message) { fetch(url, { method: ‘POST’, body: message }); }
form.onsubmit = function() { let message = form.message.value; if (message) { form.message.value = ‘’; sendMessage(message); } return false; }; }
// Receiving messages with long polling function SubscribePane(elem, url) {
function showMessage(message) { let messageElem = document.createElement(‘div’); messageElem.append(message); elem.append(messageElem); }
async function subscribe() { let response = await fetch(url);
if (response.status == 502) {
// Connection timeout
// happens when the connection was pending for too long
// let's reconnect
await subscribe();
} else if (response.status != 200) {
// Show Error
showMessage(response.statusText);
// Reconnect in one second
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// Got message
let message = await response.text();
showMessage(message);
await subscribe();
}
}
subscribe();
}
3、server.js```javascript
let http = require('http');
let url = require('url');
let querystring = require('querystring');
let static = require('node-static');
let fileServer = new static.Server('.');
let subscribers = Object.create(null);
function onSubscribe(req, res) {
let id = Math.random();
res.setHeader('Content-Type', 'text/plain;charset=utf-8');
res.setHeader("Cache-Control", "no-cache, must-revalidate");
subscribers[id] = res;
req.on('close', function() {
delete subscribers[id];
});
}
function publish(message) {
for (let id in subscribers) {
let res = subscribers[id];
res.end(message);
}
subscribers = Object.create(null);
}
function accept(req, res) {
let urlParsed = url.parse(req.url, true);
// new client wants messages
if (urlParsed.pathname == '/subscribe') {
onSubscribe(req, res);
return;
}
// sending a message
if (urlParsed.pathname == '/publish' && req.method == 'POST') {
// accept POST
req.setEncoding('utf8');
let message = '';
req.on('data', function(chunk) {
message += chunk;
}).on('end', function() {
publish(message); // publish it to everyone
res.end("ok");
});
return;
}
// the rest is static
fileServer.serve(req, res);
}
function close() {
for (let id in subscribers) {
let res = subscribers[id];
res.end();
}
}
// -----------------------------------
if (!module.parent) {
http.createServer(accept).listen(8080);
console.log('Server running on port 8080');
} else {
exports.accept = accept;
if (process.send) {
process.on('message', (msg) => {
if (msg === 'shutdown') {
close();
}
});
}
process.on('SIGINT', close);
}
| | —- |