我可以找到很多关于长轮询如何工作的信息(例如,这个和这个),但没有简单的例子说明如何在代码中实现这一点。
我能找到的只有cometd,它依赖DojoJS框架和一个相当复杂的服务器系统。。
基本上,我将如何使用Apache来服务请求,以及我将如何编写一个简单的脚本(例如,用PHP)来“长轮询”服务器以获取新消息?
该示例不一定是可扩展的、安全的或完整的,它只需要工作即可!
我可以找到很多关于长轮询如何工作的信息(例如,这个和这个),但没有简单的例子说明如何在代码中实现这一点。
我能找到的只有cometd,它依赖DojoJS框架和一个相当复杂的服务器系统。。
基本上,我将如何使用Apache来服务请求,以及我将如何编写一个简单的脚本(例如,用PHP)来“长轮询”服务器以获取新消息?
该示例不一定是可扩展的、安全的或完整的,它只需要工作即可!
当前回答
这里是Erik Dubelboer使用Content类型:multipart/x-mixed-replace-header在PHP中的一个简单的长轮询示例:
<?
header('Content-type: multipart/x-mixed-replace; boundary=endofsection');
// Keep in mind that the empty line is important to separate the headers
// from the content.
echo 'Content-type: text/plain
After 5 seconds this will go away and a cat will appear...
--endofsection
';
flush(); // Don't forget to flush the content to the browser.
sleep(5);
echo 'Content-type: image/jpg
';
$stream = fopen('cat.jpg', 'rb');
fpassthru($stream);
fclose($stream);
echo '
--endofsection
';
下面是一个演示:
http://dubbelboer.com/multipart.php
其他回答
谢谢你的代码,dbr。只有long_poller.htm中的一个小错别字
1000 /* ..after 1 seconds */
我想应该是
"1000"); /* ..after 1 seconds */
让它发挥作用。
对于那些感兴趣的人,我尝试了Django的等效版本。开始一个新的Django项目,比如说长轮询:
django-admin.py startproject lp
调用消息服务器的应用程序msgsrv:
python manage.py startapp msgsrv
将以下行添加到settings.py以创建模板目录:
import os.path
PROJECT_DIR = os.path.dirname(__file__)
TEMPLATE_DIRS = (
os.path.join(PROJECT_DIR, 'templates'),
)
在urls.py中定义URL模式如下:
from django.views.generic.simple import direct_to_template
from lp.msgsrv.views import retmsg
urlpatterns = patterns('',
(r'^msgsrv\.php$', retmsg),
(r'^long_poller\.htm$', direct_to_template, {'template': 'long_poller.htm'}),
)
msgsrv/views.py应该如下所示:
from random import randint
from time import sleep
from django.http import HttpResponse, HttpResponseNotFound
def retmsg(request):
if randint(1,3) == 1:
return HttpResponseNotFound('<h1>Page not found</h1>')
else:
sleep(randint(2,10))
return HttpResponse('Hi! Have a random number: %s' % str(randint(1,10)))
最后,templates/long_poller.htm应与上面相同,并更正了拼写错误。希望这有帮助。
我有一个非常简单的聊天示例作为晃动的一部分。
编辑:(因为每个人都在这里粘贴代码)
这是使用长轮询和晃动的完整的基于JSON的多用户聊天。这是一个如何进行调用的演示,因此请忽略XSS问题。任何人都不应该在不首先清理它的情况下部署它。
请注意,客户端始终与服务器保持连接,一旦有人发送消息,每个人都应该立即大致看到。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- Copyright (c) 2008 Dustin Sallings <dustin+html@spy.net> -->
<html lang="en">
<head>
<title>slosh chat</title>
<script type="text/javascript"
src="http://code.jquery.com/jquery-latest.js"></script>
<link title="Default" rel="stylesheet" media="screen" href="style.css" />
</head>
<body>
<h1>Welcome to Slosh Chat</h1>
<div id="messages">
<div>
<span class="from">First!:</span>
<span class="msg">Welcome to chat. Please don't hurt each other.</span>
</div>
</div>
<form method="post" action="#">
<div>Nick: <input id='from' type="text" name="from"/></div>
<div>Message:</div>
<div><textarea id='msg' name="msg"></textarea></div>
<div><input type="submit" value="Say it" id="submit"/></div>
</form>
<script type="text/javascript">
function gotData(json, st) {
var msgs=$('#messages');
$.each(json.res, function(idx, p) {
var from = p.from[0]
var msg = p.msg[0]
msgs.append("<div><span class='from'>" + from + ":</span>" +
" <span class='msg'>" + msg + "</span></div>");
});
// The jQuery wrapped msgs above does not work here.
var msgs=document.getElementById("messages");
msgs.scrollTop = msgs.scrollHeight;
}
function getNewComments() {
$.getJSON('/topics/chat.json', gotData);
}
$(document).ready(function() {
$(document).ajaxStop(getNewComments);
$("form").submit(function() {
$.post('/topics/chat', $('form').serialize());
return false;
});
getNewComments();
});
</script>
</body>
</html>
这里是Erik Dubelboer使用Content类型:multipart/x-mixed-replace-header在PHP中的一个简单的长轮询示例:
<?
header('Content-type: multipart/x-mixed-replace; boundary=endofsection');
// Keep in mind that the empty line is important to separate the headers
// from the content.
echo 'Content-type: text/plain
After 5 seconds this will go away and a cat will appear...
--endofsection
';
flush(); // Don't forget to flush the content to the browser.
sleep(5);
echo 'Content-type: image/jpg
';
$stream = fopen('cat.jpg', 'rb');
fpassthru($stream);
fclose($stream);
echo '
--endofsection
';
下面是一个演示:
http://dubbelboer.com/multipart.php
最简单的NodeJS
const http = require('http');
const server = http.createServer((req, res) => {
SomeVeryLongAction(res);
});
server.on('clientError', (err, socket) => {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.listen(8000);
// the long running task - simplified to setTimeout here
// but can be async, wait from websocket service - whatever really
function SomeVeryLongAction(response) {
setTimeout(response.end, 10000);
}
在Express for exmaple的生产方面的场景中,您将在中间件中得到响应。做您需要做的事情,可以将所有长轮询方法的范围扩展到Map或其他流(对其他流可见),并在准备就绪时调用<Response>Response.end()。长轮询连接没有什么特别之处。休息只是您通常构建应用程序的方式。
如果你不知道我所说的范围界定是什么意思,这会让你明白
const http = require('http');
var responsesArray = [];
const server = http.createServer((req, res) => {
// not dealing with connection
// put it on stack (array in this case)
responsesArray.push(res);
// end this is where normal api flow ends
});
server.on('clientError', (err, socket) => {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
// and eventually when we are ready to resolve
// that if is there just to ensure you actually
// called endpoint before the timeout kicks in
function SomeVeryLongAction() {
if ( responsesArray.length ) {
let localResponse = responsesArray.shift();
localResponse.end();
}
}
// simulate some action out of endpoint flow
setTimeout(SomeVeryLongAction, 10000);
server.listen(8000);
正如你所看到的,你真的可以对所有的联系做出反应,一是做任何你想做的事。每个请求都有id,因此您应该能够使用map和访问特定的api外调用。
下面是我为Inform8 Web开发的一个长轮询解决方案。基本上,重写类并实现loadData方法。当loadData返回值或操作超时时,它将打印结果并返回。
如果脚本的处理时间可能超过30秒,则可能需要将set_time_limit()调用更改为更长的时间。
Apache 2.0许可证。github上的最新版本https://github.com/ryanhend/Inform8/blob/master/Inform8-web/src/config/lib/Inform8/longpoll/LongPoller.php
Ryan
abstract class LongPoller {
protected $sleepTime = 5;
protected $timeoutTime = 30;
function __construct() {
}
function setTimeout($timeout) {
$this->timeoutTime = $timeout;
}
function setSleep($sleep) {
$this->sleepTime = $sleepTime;
}
public function run() {
$data = NULL;
$timeout = 0;
set_time_limit($this->timeoutTime + $this->sleepTime + 15);
//Query database for data
while($data == NULL && $timeout < $this->timeoutTime) {
$data = $this->loadData();
if($data == NULL){
//No new orders, flush to notify php still alive
flush();
//Wait for new Messages
sleep($this->sleepTime);
$timeout += $this->sleepTime;
}else{
echo $data;
flush();
}
}
}
protected abstract function loadData();
}