809 Stimmen

Wie führe ich ein einfaches "Long Polling" durch?

Ich kann viele Informationen darüber finden, wie Long Polling funktioniert (zum Beispiel, este y este ), aber keine einfach Beispiele, wie dies in Code umgesetzt werden kann.

Alles, was ich finden kann, ist cometd die auf dem Dojo JS-Framework und einem ziemlich komplexen Serversystem basiert.

Wie würde ich Apache verwenden, um die Anfragen zu bedienen, und wie würde ich ein einfaches Skript (z. B. in PHP) schreiben, das den Server nach neuen Nachrichten "abfragt"?

Das Beispiel muss nicht skalierbar, sicher oder vollständig sein, es muss nur funktionieren!

529voto

dbr Punkte 158949

Es ist einfacher, als ich anfangs dachte. Im Grunde haben Sie eine Seite, die nichts tut, bis die Daten, die Sie senden möchten, verfügbar sind (z. B. eine neue Nachricht eintrifft).

Hier ist ein sehr einfaches Beispiel, das nach 2-10 Sekunden eine einfache Zeichenkette sendet. 1 zu 3 Chance, einen Fehler 404 zurückzugeben (um die Fehlerbehandlung im kommenden Javascript-Beispiel zu zeigen)

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));
?>

Hinweis: Bei einer echten Website wird die Ausführung auf einem normalen Webserver wie Apache schnell alle "Worker-Threads" blockieren und ihn unfähig machen, auf andere Anfragen zu reagieren. Es gibt Möglichkeiten, dies zu umgehen, aber es wird empfohlen, einen "Long-Poll-Server" in etwas wie Python zu schreiben verdrillt die nicht auf einen Thread pro Anfrage angewiesen ist. cometD ist ein beliebtes Programm (das in mehreren Sprachen verfügbar ist), und Tornado ist ein neues Framework, das speziell für solche Aufgaben entwickelt wurde (es wurde für den Long-Polling-Code von FriendFeed entwickelt)... aber als einfaches Beispiel ist Apache mehr als ausreichend! Dieses Skript könnte leicht in jeder beliebigen Sprache geschrieben werden (ich habe mich für Apache/PHP entschieden, da sie sehr weit verbreitet sind und ich sie zufällig lokal ausgeführt habe)

Dann fordern Sie in Javascript die oben genannte Datei an ( msg_srv.php ), und warten Sie auf eine Antwort. Wenn Sie eine erhalten, verarbeiten Sie die Daten. Dann fordern Sie die Datei an, warten wieder, verarbeiten die Daten (und wiederholen)

Im Folgenden finden Sie ein Beispiel für eine solche Seite Wenn die Seite geladen wird, sendet sie die erste Anfrage für die msgsrv.php Datei. Wenn dies gelingt, wird die Nachricht an die Datei #messages div, dann rufen wir nach 1 Sekunde erneut die Funktion waitForMsg auf, die das Warten auslöst.

Die 1 Sekunde setTimeout() ein wirklich einfacher Ratenbegrenzer ist, funktioniert er auch ohne dies, aber wenn msgsrv.php immer sofort zurückkehrt (z. B. mit einem Syntaxfehler), überfluten Sie den Browser und er kann schnell einfrieren. Besser wäre es, zu prüfen, ob die Datei eine gültige JSON-Antwort enthält, und/oder eine laufende Gesamtzahl von Anfragen pro Minute/Sekunde zu erfassen und entsprechend zu pausieren.

Wenn die Seite fehlerhaft ist, wird der Fehler an die #messages div, wartet 15 Sekunden und versucht es dann erneut (genauso wie wir nach jeder Nachricht 1 Sekunde warten)

Das Schöne an diesem Ansatz ist, dass er sehr widerstandsfähig ist. Wenn die Internetverbindung des Clients abbricht, wird eine Zeitüberschreitung ausgelöst, dann wird versucht, die Verbindung wiederherzustellen - dies ist inhärent in der Art und Weise, wie Long Polling funktioniert, es ist keine komplizierte Fehlerbehandlung erforderlich

Wie auch immer, die long_poller.htm Code, der das jQuery-Framework verwendet:

<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>

41voto

Dustin Punkte 85400

Ich habe ein wirklich einfaches Chat-Beispiel als Teil von schwappt .

Editar (da jeder seinen Code hier einfügt)

Dies ist der komplette JSON-basierte Multi-User-Chat mit Long-Polling und schwappt . Dies ist eine Demo wie die Aufrufe zu machen sind, also ignorieren Sie bitte die XSS-Probleme. Niemand sollte dies einsetzen, ohne es vorher zu säubern.

Beachten Sie, dass der Kunde immer hat eine Verbindung zum Server, und sobald jemand eine Nachricht sendet, sollte jeder sie ungefähr sofort sehen.

<?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>

32voto

mikemaccana Punkte 93077

Tornado ist für Long-Polling konzipiert und enthält eine sehr minimale (einige hundert Zeilen Python) Chat-App in / Beispiele/Chatdemo einschließlich Server-Code und JS-Client-Code. Es funktioniert wie folgt:

  • Clients verwenden JS, um nach einer Aktualisierung zu fragen (Nummer der letzten Nachricht), der Server-URLHandler empfängt diese und fügt einen Callback hinzu, um dem Client in einer Warteschlange zu antworten.

  • Wenn der Server eine neue Nachricht erhält, wird das Ereignis onmessage ausgelöst, durchläuft die Callbacks und sendet die Nachrichten.

  • Das clientseitige JS empfängt die Nachricht, fügt sie der Seite hinzu und fragt dann nach Aktualisierungen seit dieser neuen Nachrichten-ID.

25voto

Greg Punkte 306033

Ich denke, der Client sieht aus wie eine normale asynchrone AJAX-Anfrage, aber Sie erwarten, dass es eine "lange Zeit" dauert, bis er zurückkommt.

Der Server sieht dann wie folgt aus.

while (!hasNewData())
    usleep(50);

outputNewData();

Die AJAX-Anfrage geht also an den Server und enthält wahrscheinlich einen Zeitstempel für die letzte Aktualisierung, so dass Ihre hasNewData() weiß, welche Daten Sie bereits haben. Der Server befindet sich dann in einer Schleife und wartet, bis neue Daten verfügbar sind. Die ganze Zeit über ist Ihre AJAX-Anfrage noch verbunden und wartet auf Daten. Wenn schließlich neue Daten verfügbar sind, gibt der Server sie an Ihre AJAX-Anfrage weiter und schließt die Verbindung.

17voto

Prisoner ZERO Punkte 13458

Hier sind einige Klassen, die ich für Long-Polling in C# verwende. Es gibt grundsätzlich 6 Klassen (siehe unten).

  1. Controller : Verarbeitet Aktionen, die zur Erstellung einer gültigen Antwort erforderlich sind (db-Operationen usw.)
  2. Prozessor : Verwaltet die asynchrone Kommunikation mit der Web-Seite (selbst)
  3. IAsynchProzessor : Der Dienst verarbeitet Instanzen, die diese Schnittstelle implementieren
  4. Dienst : Verarbeitet Anfrageobjekte, die IAsynchProcessor implementieren
  5. Anfrage : Die IAsynchProcessor-Verschalung mit Ihrer Antwort (Objekt)
  6. Antwort : Enthält benutzerdefinierte Objekte oder Felder

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X