php 如何实现基本的“长轮询”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/333664/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I implement basic "Long Polling"?
提问by dbr
I can find lots of information on how Long Polling works (For example, this, and this), but no simpleexamples of how to implement this in code.
我可以找到很多关于长轮询如何工作的信息(例如this和this),但没有关于如何在代码中实现它的简单示例。
All I can find is cometd, which relies on the Dojo JS framework, and a fairly complex server system..
我能找到的只有cometd,它依赖于 Dojo JS 框架,以及一个相当复杂的服务器系统。
Basically, how would I use Apache to serve the requests, and how would I write a simple script (say, in PHP) which would "long-poll" the server for new messages?
基本上,我将如何使用 Apache 来处理请求,以及如何编写一个简单的脚本(例如,在 PHP 中)来“长轮询”服务器以获取新消息?
The example doesn't have to be scaleable, secure or complete, it just needs to work!
该示例不必是可扩展的、安全的或完整的,它只需要工作即可!
采纳答案by dbr
It's simpler than I initially thought.. Basically you have a page that does nothing, until the data you want to send is available (say, a new message arrives).
它比我最初想象的要简单......基本上你有一个什么都不做的页面,直到你想要发送的数据可用(比如,一条新消息到达)。
Here is a really basic example, which sends a simple string after 2-10 seconds. 1 in 3 chance of returning an error 404 (to show error handling in the coming Javascript example)
这是一个非常基本的示例,它在 2-10 秒后发送一个简单的字符串。有三分之一的机会返回错误 404(在即将到来的 Javascript 示例中显示错误处理)
msgsrv.php
msgsrv.php
<?php
if(rand(1,3) == 1){
/* Fake an error */
header("HTTP/1.0 404 Not Found");
die();
}
/* Send a string after a random number of seconds (2-10) */
sleep(rand(2,10));
echo("Hi! Have a random number: " . rand(1,10));
?>
Note: With a real site, running this on a regular web-server like Apache will quickly tie up all the "worker threads" and leave it unable to respond to other requests.. There are ways around this, but it is recommended to write a "long-poll server" in something like Python's twisted, which does not rely on one thread per request. cometDis an popular one (which is available in several languages), and Tornadois a new framework made specifically for such tasks (it was built for FriendFeed's long-polling code)... but as a simple example, Apache is more than adequate! This script could easily be written in any language (I chose Apache/PHP as they are very common, and I happened to be running them locally)
注意:对于真实站点,在像 Apache 这样的常规 Web 服务器上运行它会很快占用所有“工作线程”并使其无法响应其他请求.. 有办法解决这个问题,但建议编写类似于 Python 的twisted的“长轮询服务器” ,它不依赖于每个请求的一个线程。CometD是一个流行的框架(有多种语言版本),Tornado是一个专门为此类任务而设计的新框架(它是为 FriendFeed 的长轮询代码而构建的)……但作为一个简单的例子,Apache 就足够了!这个脚本可以很容易地用任何语言编写(我选择了 Apache/PHP,因为它们很常见,而且我碰巧在本地运行它们)
Then, in Javascript, you request the above file (msg_srv.php), and wait for a response. When you get one, you act upon the data. Then you request the file and wait again, act upon the data (and repeat)
然后,在 Javascript 中,您请求上述文件 ( msg_srv.php),并等待响应。当你得到一个时,你就对数据采取行动。然后您请求文件并再次等待,对数据采取行动(并重复)
What follows is an example of such a page.. When the page is loaded, it sends the initial request for the msgsrv.phpfile.. If it succeeds, we append the message to the #messagesdiv, then after 1 second we call the waitForMsg function again, which triggers the wait.
下面是这样一个页面的例子..当页面加载时,它发送对msgsrv.php文件的初始请求..如果成功,我们将消息附加到#messagesdiv,然后1秒后我们再次调用waitForMsg函数,这触发了等待。
The 1 second setTimeout()is a really basic rate-limiter, it works fine without this, but if msgsrv.phpalwaysreturns instantly (with a syntax error, for example) - you flood the browser and it can quickly freeze up. This would better be done checking if the file contains a valid JSON response, and/or keeping a running total of requests-per-minute/second, and pausing appropriately.
1 秒setTimeout()是一个非常基本的速率限制器,没有它它也能正常工作,但如果msgsrv.php总是立即返回(例如,出现语法错误)——你淹没了浏览器,它会很快冻结。最好检查文件是否包含有效的 JSON 响应,和/或保持每分钟/秒的请求总数,并适当暂停。
If the page errors, it appends the error to the #messagesdiv, waits 15 seconds and then tries again (identical to how we wait 1 second after each message)
如果页面出错,它会将错误附加到#messagesdiv,等待 15 秒,然后再试一次(与我们在每条消息后等待 1 秒的方式相同)
The nice thing about this approach is it is very resilient. If the clients internet connection dies, it will timeout, then try and reconnect - this is inherent in how long polling works, no complicated error-handling is required
这种方法的好处是它非常有弹性。如果客户端的 Internet 连接中断,它将超时,然后尝试重新连接 - 这是轮询工作多长时间所固有的,不需要复杂的错误处理
Anyway, the long_poller.htmcode, using the jQuery framework:
无论如何,long_poller.htm代码,使用jQuery框架:
<html>
<head>
<title>BargePoller</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css" media="screen">
body{ background:#000;color:#fff;font-size:.9em; }
.msg{ background:#aaa;padding:.2em; border-bottom:1px #000 solid}
.old{ background-color:#246499;}
.new{ background-color:#3B9957;}
.error{ background-color:#992E36;}
</style>
<script type="text/javascript" charset="utf-8">
function addmsg(type, msg){
/* Simple helper to add a div.
type is the name of a CSS class (old/new/error).
msg is the contents of the div */
$("#messages").append(
"<div class='msg "+ type +"'>"+ msg +"</div>"
);
}
function waitForMsg(){
/* This requests the url "msgsrv.php"
When it complete (or errors)*/
$.ajax({
type: "GET",
url: "msgsrv.php",
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
success: function(data){ /* called when request to barge.php completes */
addmsg("new", data); /* Add response to a .msg div (with the "new" class)*/
setTimeout(
waitForMsg, /* Request next message */
1000 /* ..after 1 seconds */
);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
addmsg("error", textStatus + " (" + errorThrown + ")");
setTimeout(
waitForMsg, /* Try again after.. */
15000); /* milliseconds (15seconds) */
}
});
};
$(document).ready(function(){
waitForMsg(); /* Start the inital request */
});
</script>
</head>
<body>
<div id="messages">
<div class="msg old">
BargePoll message requester!
</div>
</div>
</body>
</html>
回答by Dustin
I've got a really simple chat example as part of slosh.
我有一个非常简单的聊天示例作为slosh 的一部分。
Edit: (since everyone's pasting their code in here)
编辑:(因为每个人都在这里粘贴他们的代码)
This is the complete JSON-based multi-user chat using long-polling and slosh. This is a demoof how to do the calls, so please ignore the XSS problems. Nobody should deploy this without sanitizing it first.
这是使用长轮询和slosh的完整的基于 JSON 的多用户聊天。这是如何进行调用的演示,因此请忽略 XSS 问题。任何人都不应在没有先对其进行消毒的情况下部署它。
Notice that the client alwayshas a connection to the server, and as soon as anyone sends a message, everyone should see it roughly instantly.
请注意,客户端始终与服务器有连接,并且只要有人发送消息,每个人都会大致立即看到它。
<?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 <[email protected]> -->
<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>
回答by mikemaccana
Tornadois designed for long-polling, and includes a very minimal (few hundred lines of Python) chat appin /examples/chatdemo, including server code and JS client code. It works like this:
Tornado是为长轮询而设计的,在 / examples/chatdemo 中包含一个非常小的(几百行 Python 代码)聊天应用程序,包括服务器代码和 JS 客户端代码。它是这样工作的:
Clients use JS to ask for an updates since (number of last message), server URLHandler receives these and adds a callback to respond to the client to a queue.
When the server gets a new message, the onmessage event fires, loops through the callbacks, and sends the messages.
The client-side JS receives the message, adds it to the page, then asks for updates since this new message ID.
客户端使用 JS 来请求更新(最后一条消息的数量),服务器 URLHandler 接收这些并添加回调以响应客户端到队列。
当服务器收到一条新消息时,onmessage 事件会触发,通过回调循环并发送消息。
客户端 JS 接收消息,将其添加到页面,然后请求自此新消息 ID 以来的更新。
回答by Greg
I think the client looks like a normal asynchronous AJAX request, but you expect it to take a "long time" to come back.
我认为客户端看起来像一个普通的异步 AJAX 请求,但您希望它需要“很长时间”才能返回。
The server then looks like this.
服务器看起来像这样。
while (!hasNewData())
usleep(50);
outputNewData();
So, the AJAX request goes to the server, probably including a timestamp of when it was last update so that your hasNewData()knows what data you have already got.
The server then sits in a loop sleeping until new data is available. All the while, your AJAX request is still connected, just hanging there waiting for data.
Finally, when new data is available, the server gives it to your AJAX request and closes the connection.
因此,AJAX 请求发送到服务器,可能包括上次更新时间的时间戳,以便您hasNewData()知道您已经获得了哪些数据。然后服务器处于循环睡眠状态,直到有新数据可用。一直以来,您的 AJAX 请求仍处于连接状态,只是挂在那里等待数据。最后,当新数据可用时,服务器将其提供给您的 AJAX 请求并关闭连接。
回答by Prisoner ZERO
Hereare some classes I use for long-polling in C#. There are basically 6 classes (see below).
以下是我在 C# 中用于长轮询的一些类。基本上有 6 个类(见下文)。
- Controller: Processes actions required to create a valid response (db operations etc.)
- Processor: Manages asynch communication with the web page (itself)
- IAsynchProcessor: The service processes instances that implement this interface
- Sevice: Processes request objects that implement IAsynchProcessor
- Request: The IAsynchProcessor wrapper containing your response (object)
- Response: Contains custom objects or fields
- 控制器:处理创建有效响应所需的操作(数据库操作等)
- 处理器:管理与网页(本身)的异步通信
- IAsynchProcessor:实现此接口的服务进程实例
- 服务:处理实现 IAsynchProcessor 的请求对象
- 请求:包含您的响应(对象)的 IAsynchProcessor 包装器
- 响应:包含自定义对象或字段
回答by Sean O
This is a nice 5-minute screencast on how to do long polling using PHP & jQuery: http://screenr.com/SNH
这是关于如何使用 PHP 和 jQuery 进行长轮询的 5 分钟截屏视频:http: //screenr.com/SNH
Code is quite similar to dbr's example above.
代码与上面的dbr示例非常相似。
回答by Jasdeep Khalsa
Here is a simple long-polling example in PHP by Erik Dubbelboerusing the Content-type: multipart/x-mixed-replaceheader:
这是Erik Dubbelboer 在 PHP 中使用Content-type: multipart/x-mixed-replace标头编写的一个简单的长轮询示例:
<?
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
';
And here is a demo:
这是一个演示:
回答by adam
回答by Denis Bilenko
Take a look at this blog postwhich has code for a simple chat app in Python/Django/gevent.
回答by Ryan Henderson
Below is a long polling solution I have developed for Inform8 Web. Basically you override the class and implement the loadData method. When the loadData returns a value or the operation times out it will print the result and return.
下面是我为 Inform8 Web 开发的长轮询解决方案。基本上,您覆盖该类并实现 loadData 方法。当 loadData 返回一个值或操作超时时,它将打印结果并返回。
If the processing of your script may take longer than 30 seconds you may need to alter the set_time_limit() call to something longer.
如果您的脚本处理时间可能超过 30 秒,您可能需要将 set_time_limit() 调用更改为更长的时间。
Apache 2.0 license. Latest version on github https://github.com/ryanhend/Inform8/blob/master/Inform8-web/src/config/lib/Inform8/longpoll/LongPoller.php
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();
}

