785 votes

Exemple de code simple "Long Polling" ?

Je peux trouver beaucoup d'informations sur le fonctionnement de Long Polling (par exemple, ce et ce ), mais pas de simple des exemples de mise en œuvre dans le code.

Tout ce que je peux trouver est cometd qui s'appuie sur le framework Dojo JS, et un système de serveur assez complexe

En gros, comment utiliser Apache pour servir les requêtes, et comment écrire un simple script (disons, en PHP) qui "interrogerait longuement" le serveur pour les nouveaux messages ?

L'exemple ne doit pas être évolutif, sécurisé ou complet, il doit simplement fonctionner !

516voto

dbr Points 66401

C'est plus simple que je ne le pensais au départ En gros, vous avez une page qui ne fait rien, jusqu'à ce que les données que vous voulez envoyer soient disponibles (disons, un nouveau message arrive).

Voici un exemple vraiment basique, qui envoie une simple chaîne de caractères après 2 à 10 secondes. 1 chance sur 3 de renvoyer une erreur 404 (pour montrer la gestion des erreurs dans l'exemple Javascript à venir).

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 : Avec un site réel, l'exécution de cette opération sur un serveur web ordinaire comme Apache va rapidement bloquer tous les "fils de travail" et le laisser incapable de répondre à d'autres demandes Il existe des moyens de contourner ce problème, mais il est recommandé d'écrire un "serveur de sondage long" dans un langage comme Python. tordu qui ne repose pas sur un thread par requête. cometD est populaire (il est disponible en plusieurs langues), et Tornade est un nouveau framework fait spécifiquement pour de telles tâches (il a été construit pour le code de sondage long de FriendFeed)... mais comme exemple simple, Apache est plus qu'adéquat ! Ce script pourrait facilement être écrit dans n'importe quel langage (j'ai choisi Apache/PHP car ils sont très courants, et il se trouve que je les utilisais localement).

Ensuite, en Javascript, vous demandez le fichier ci-dessus ( msg_srv.php ), et attendez une réponse. Lorsque vous en recevez une, vous agissez sur les données. Ensuite, vous demandez le fichier, attendez à nouveau, traitez les données (et répétez).

Ce qui suit est un exemple d'une telle page Lorsque la page est chargée, elle envoie la requête initiale pour le fichier msgsrv.php fichier.. S'il réussit, nous ajoutons le message au fichier #messages puis, après une seconde, nous appelons à nouveau la fonction waitForMsg, ce qui déclenche l'attente.

La 1 seconde setTimeout() est un limiteur de débit très basique, il fonctionne bien sans cela, mais si msgsrv.php toujours retourne instantanément (avec une erreur de syntaxe, par exemple) - vous inondez le navigateur et il peut rapidement se figer. Il serait préférable de vérifier si le fichier contient une réponse JSON valide, et/ou de garder un total de requêtes par minute/seconde, et de mettre en pause de manière appropriée.

Si la page est erronée, elle ajoute l'erreur à l'élément #messages div, attend 15 secondes puis réessaie (identique à la façon dont nous attendons 1 seconde après chaque message).

L'avantage de cette approche est qu'elle est très résistante. Si la connexion internet du client est interrompue, il y aura un délai d'attente, puis il essaiera de se reconnecter - c'est inhérent au fonctionnement de l'interrogation à long terme, aucune gestion d'erreur compliquée n'est nécessaire.

Quoi qu'il en soit, le long_poller.htm en utilisant le framework 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>

7 votes

Certains messages ne pourraient-ils pas passer en utilisant cette idée ? Dans ce délai d'une seconde, disons que 1000 messages de chat ont été envoyés, comment le serveur saurait-il qu'il faut envoyer les 1000 messages spécifiquement à ce client ?

15 votes

Probablement. Il s'agit d'un exemple très simplifié, pour démontrer le concept Pour mieux faire cela, vous auriez besoin d'un code plus élaboré côté serveur, où il stockerait ces 1000 messages pour ce client spécifique, et les enverrait en un seul morceau. Vous pourriez également réduire le délai d'attente (waitForMsg) en toute sécurité.

0 votes

Excellente solution. Elle fonctionne dès la sortie de la boîte. Il semble répondre à ma question ici : stackoverflow.com/questions/3002213/ . Excellent travail.

41voto

Dustin Points 35205

J'ai un exemple de chat vraiment simple dans le cadre de slosh .

Modifier : (puisque tout le monde colle son code ici)

Il s'agit d'un chat multi-utilisateurs complet basé sur JSON, qui utilise le sondage long et le système d'alerte. slosh . Il s'agit d'un Démonstration de comment faire les appels, donc s'il vous plaît ignorez les problèmes XSS. Personne ne devrait déployer ce code sans l'assainir au préalable.

Remarquez que le client toujours a une connexion avec le serveur, et dès que quelqu'un envoie un message, tout le monde devrait le voir à peu près instantanément.

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

1 votes

Puis-je savoir comment cela est toujours connecté ? Désolé si je demande quelque chose de stupide, mais je veux le savoir.

5 votes

Il effectue un GET HTTP et le serveur bloque le GET jusqu'à ce que des données soient disponibles. Lorsque les données arrivent sur le serveur, celui-ci les renvoie au client, met en file d'attente les autres données qui pourraient arriver, puis le client se reconnecte et récupère les messages manquants s'il y en a, sinon il se bloque à nouveau.

4 votes

Ce n'est peut-être pas évident au premier abord, mais le responsable de l'état "toujours connecté" est ajaxStop avec getNewComments il y a un callback, donc il le lance juste à la fin de chaque requête ajax sans fin.

32voto

mikemaccana Points 7470

Tornade est conçu pour les longs sondages, et inclut un système très minimal (quelques centaines de lignes de Python). application de chat dans / exemples/chatdemo y compris le code du serveur et le code du client JS. Cela fonctionne comme suit :

  • Les clients utilisent JS pour demander une mise à jour depuis (numéro du dernier message), l'URLHandler du serveur les reçoit et ajoute un callback pour répondre au client dans une file d'attente.

  • Lorsque le serveur reçoit un nouveau message, l'événement onmessage se déclenche, passe en boucle les callbacks et envoie les messages.

  • Le JS côté client reçoit le message, l'ajoute à la page, puis demande les mises à jour depuis ce nouvel identifiant de message.

25voto

Greg Points 132247

Je pense que le client ressemble à une demande AJAX asynchrone normale, mais vous vous attendez à ce qu'il prenne un "long moment" pour revenir.

Le serveur ressemble alors à ceci.

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

outputNewData();

Ainsi, la requête AJAX est envoyée au serveur, incluant probablement un horodatage de la dernière mise à jour afin que votre hasNewData() sait quelles données vous avez déjà. Le serveur reste alors en boucle et attend que de nouvelles données soient disponibles. Pendant ce temps, votre requête AJAX est toujours connectée, attendant les données. Enfin, lorsque de nouvelles données sont disponibles, le serveur les transmet à votre requête AJAX et ferme la connexion.

10 votes

Il s'agit d'une attente occupée qui bloque votre fil de discussion actuel. Cela n'est pas du tout adapté.

12 votes

Non, usleep n'est pas une attente occupée. Et le but de "l'attente" est de bloquer votre thread pendant un certain temps. Il voulait probablement dire 50 millisecondes (usleep(50000)), pas 50 microsecondes ! Quoi qu'il en soit, avec une configuration typique d'Apache/PHP, est-ce qu'il y a tout autre manière de le faire ?

0 votes

En principe, vous ne pouvez pas créer une fonction de blocage pour les messages de chat sans attente.

15voto

Sean O Points 820

Il s'agit d'un screencast de 5 minutes sur la façon de faire des sondages longs en utilisant PHP et jQuery : http://screenr.com/SNH

Le code est assez similaire à dbr de l'exemple ci-dessus.

3 votes

Je pense que vous ne devriez voir cela que comme une introduction au sondage long car cette implémentation tuera à coup sûr votre serveur avec de nombreux utilisateurs simultanés.

0 votes

Je suis en train d'apprendre tout cela... Dans quelle mesure est-il fiable, ou non, avec quelques utilisateurs... disons 10 qui discutent entre eux ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X