我可以找到很多关于长轮询如何工作的信息(例如,这个和这个),但没有简单的例子说明如何在代码中实现这一点。

我能找到的只有cometd,它依赖DojoJS框架和一个相当复杂的服务器系统。。

基本上,我将如何使用Apache来服务请求,以及我将如何编写一个简单的脚本(例如,用PHP)来“长轮询”服务器以获取新消息?

该示例不一定是可扩展的、安全的或完整的,它只需要工作即可!


当前回答

为什么不考虑web套接字而不是长轮询?它们非常高效且易于设置。然而,它们仅在现代浏览器中受支持。这里是一个快速参考。

其他回答

谢谢你的代码,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>

这比我最初想的要简单。。基本上,在您要发送的数据可用(例如,新消息到达)之前,您的页面什么都不做。

这里是一个非常基本的示例,它在2-10秒后发送一个简单的字符串。三分之一的几率返回错误404(在接下来的Javascript示例中显示错误处理)

消息srv.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));
?>

注意:对于一个真实的站点,在像Apache这样的常规web服务器上运行它将很快占用所有的“工作线程”,使其无法响应其他请求。。有很多方法可以解决这个问题,但建议使用Python的twisted编写一个“长轮询服务器”,它不依赖于每个请求一个线程。cometD是一个流行的框架(有多种语言可供使用),Tornado是专门为此类任务制作的新框架(它是为FriendFeed的长轮询代码构建的)。。。但作为一个简单的例子,Apache已经足够了!这个脚本可以很容易地用任何语言编写(我选择了Apache/PHP,因为它们非常常见,我碰巧在本地运行)

然后,在Javascript中,请求上述文件(msg_srv.php),并等待响应。当你得到一个时,你会根据数据采取行动。然后请求文件并再次等待,对数据进行操作(并重复)

下面是这样一个页面的示例。。加载页面后,它将发送对msgsrv.php文件的初始请求。。如果成功,我们将消息附加到#messages div,然后在1秒后再次调用waitForMsg函数,这将触发等待。

1秒的setTimeout()是一个非常基本的速率限制器,如果没有这个功能,它可以正常工作,但如果msgsrv.php总是立即返回(例如语法错误)-你会淹没浏览器,它会很快冻结。最好检查文件是否包含有效的JSON响应,和/或保持每分钟/秒的请求总数,并适当暂停。

如果页面出错,它会将错误附加到#messages div,等待15秒,然后重试(与我们在每条消息后等待1秒的方式相同)

这种方法的好处是它非常有弹性。如果客户端的互联网连接失效,它将超时,然后尝试重新连接-这是轮询工作时间的固有特征,不需要复杂的错误处理

无论如何,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>

看看这篇博文,里面有Python/Django/gevent中一个简单聊天应用程序的代码。

对于ASP.NET MVC实现,请查看NuGet上提供的SignalR。。请注意,NuGet与Git源代码相比常常是过时的,因为Git源经常提交。

在Scott Hanselman的博客上阅读有关SignalR的更多信息