Comment puis-je mettre en œuvre le "sondage de base"?

je peux trouver beaucoup d'informations sur la durée du sondage (par exemple, this , et this ), mais pas simple exemples de la façon de mettre en œuvre cela dans le code.

Tout ce que je peux trouver est cometd , qui repose sur le cadre de Dojo JS, et un système de serveur assez complexe..

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

l'exemple n'a pas besoin d'être extensible, sécurisé ou complet, il a juste besoin de fonctionner!

749
demandé sur Sinister Beard 2008-12-02 14:14:47
la source

18 ответов

c'est plus simple que je ne le pensais.. Fondamentalement, vous avez une page qui ne fait rien, jusqu'à ce que les données que vous souhaitez envoyer est disponible (par exemple, un nouveau message arrive).

voici un exemple vraiment basique, qui envoie une chaîne simple après 2-10 secondes. 1 chance sur 3 de retourner une erreur 404 (pour montrer le traitement 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 vrai site, exécuter ceci sur un serveur web ordinaire comme Apache va rapidement immobiliser tous les "worker threads" et le laisser incapable de répondre à d'autres requêtes.. Il y a des moyens de contourner cela, mais il est recommandé d'écrire un "serveur de sondage longue durée" dans quelque chose comme de Python, "tordu , qui ne s'appuie pas sur un seul thread par requête. cometD est un populaire (qui est disponible en plusieurs langues), et Tornade est un nouveau cadre de 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 peut facilement être écrit dans n'importe quelle langue (J'ai choisi Apache/PHP car ils sont très courants, et je les exécutais localement)

puis, en Javascript, vous demandez le fichier ci-dessus ( msg_srv.php ), et attendez une réponse. Lorsque vous obtenez un, vous agissez sur les données. Ensuite, vous demandez le fichier et attendre à nouveau, agir sur les données (et répétition)

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 .. Si elle réussit, nous ajoutons le message au div #messages , puis après 1 seconde nous appelons de nouveau la fonction waitForMsg, ce qui déclenche l'attente.

1 seconde setTimeout() est vraiment un taux de base-limiteur, il fonctionne très bien sans, mais si msgsrv.php toujours renvoie instantanément (avec une erreur de syntaxe, par exemple) - vous d'inondation le navigateur et il peut geler rapidement. Ceci devrait être fait en vérifiant si le fichier contient une réponse JSON valide, et/ou en gardant un total courant de requêtes-par minute/seconde, et en faisant une pause appropriée.

si la page fait une erreur, elle ajoute l'erreur au div #messages , attend 15 secondes et tente à nouveau (identique à la façon dont nous attendons 1 seconde après chaque message)

le nice chose à propos de cette approche est qu'il est très résistant. Si la connexion internet des clients meurt, il va timeout, puis essayer de se reconnecter - c'est inhérent à la durée du sondage, aucune erreur compliquée-manipulation est nécessaire

de toute façon, le code long_poller.htm , en utilisant le cadre 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>
494
répondu dbr 2012-10-01 15:24:47
la source

j'ai un exemple de chat très simple dans le cadre de slosh .

Edit : (puisque tout le monde collait son code ici)

il s'agit du chat multi-utilisateurs complet basé sur JSON utilisant long-polling et slosh . Il s'agit d'une Démo de la façon de faire les appels, donc s'il vous plaît ignorer les problèmes XSS. Personne ne devrait déployer sans désinfection préalable.

notez que le client a toujours une connexion au 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 <[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>
41
répondu Dustin 2012-06-04 12:22:44
la source

Tornado est conçu pour les sondages longs, et comprend un très minime (quelques centaines de lignes de Python) chat app in / exemples/chatdemo , y compris le code du serveur et le code du client JS. Cela fonctionne comme ceci:

  • les Clients utilisent JS pour demander des mises à jour car (nombre de dernier message), le serveur URLHandler les reçoit et ajoute un rappel pour répondre au client à une file d'attente.

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

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

31
répondu mikemaccana 2013-05-16 17:33:59
la source

je pense que le client ressemble à une requête AJAX asynchrone normale, mais vous vous attendez à ce que cela prenne un" long moment " pour revenir.

Le serveur ressemble alors à ceci.

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

outputNewData();

ainsi, la requête AJAX va au serveur, probablement en incluant un horodatage de quand il était la dernière mise à jour de sorte que votre hasNewData() sache quelles données vous avez déjà. Le serveur s'assoit alors dans une boucle de sommeil jusqu'à ce que de nouvelles données soient disponibles. Tout le temps, votre AJAX la requête est toujours connectée, juste accrochée là en attendant les données. Enfin, lorsque de nouvelles données sont disponibles, le serveur Les donne à votre demande AJAX et ferme la connexion.

24
répondu Greg 2008-12-02 14:39:37
la source

ici sont quelques classes que j'utilise pour le scrutin de longue durée dans C#. Il y a essentiellement 6 classes (voir ci-dessous).

  1. Controller : traite les actions nécessaires pour créer une réponse valide (opérations db, etc.)
  2. processeur : gère la communication asynchrone avec la page web (elle-même)
  3. IAsynchProcessor : le service traite les instances qui implémentent cette interface
  4. Service : Processus de demande des objets qui implémentent IAsynchProcessor
  5. Demande : Le IAsynchProcessor l'emballage contenant votre réponse (objet)
  6. Réponse : Contient des objets personnalisés ou des champs
17
répondu Prisoner ZERO 2017-05-23 14:54:41
la source

c'est un beau screencast de 5 minutes sur la façon de faire de longs sondages en utilisant PHP & jQuery: http://screenr.com/SNH

Le Code

est très similaire à l'exemple de dbr ci-dessus.

16
répondu Sean O 2009-10-20 19:41:38
la source

Voici un exemple simple de sondage long en PHP par Erik Dubbelboer en utilisant le Content-type: multipart/x-mixed-replace en-tête:

<?

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

et voici une démo:

http://dubbelboer.com/multipart.php

12
répondu Jasdeep Khalsa 2012-12-08 16:29:21
la source

j'ai utilisé ce pour me familiariser avec Comet, j'ai aussi installé Comet en utilisant le serveur Java Glassfish et j'ai trouvé beaucoup d'autres exemples en m'abonnant à cometdaily.com

11
répondu adam 2008-12-02 14:21:34
la source

ci-dessous est une longue solution de sondage que j'ai développé pour Inform8 Web. Fondamentalement, vous outrepassez la classe et mettez en œuvre la méthode loadData. Lorsque le loadData retourne une valeur ou que l'opération se termine, il affiche le résultat et le renvoie.

si le traitement de votre script peut prendre plus de 30 secondes, vous pouvez avoir besoin de modifier l'appel de set_time_limit() vers quelque chose de plus long.

Licence

Apache 2.0. Dernière version sur 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();

}
9
répondu Ryan Henderson 2011-05-05 14:39:24
la source

Merci pour le code, dbr . Juste une petite erreur dans long_poller.htm autour de la ligne

1000 /* ..after 1 seconds */

je pense qu'il devrait être

"1000"); /* ..after 1 seconds */

pour que ça marche.

pour les intéressés, j'ai essayé un équivalent Django. Démarrer un nouveau projet Django, dire lp pour les longs sondages:

django-admin.py startproject lp

Appelez l'application msgsrv "1519110920 pour le serveur de messages:

python manage.py startapp msgsrv

Ajouter les lignes suivantes à settings.py pour avoir une modèles "151990920 répertoire":

import os.path
PROJECT_DIR = os.path.dirname(__file__)
TEMPLATE_DIRS = (
    os.path.join(PROJECT_DIR, 'templates'),
)

Définissez vos patrons D'URL dans urls.py en tant que telle:

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'}),
)

et msgsrv / views.py devrait ressembler à:

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

enfin, templates / long_poller.htm doit être le même que ci-dessus avec typographie corrigée. Espérons que cette aide.

8
répondu xoblau 2009-09-15 16:30:50
la source

regardez ce billet de blog qui contient le code d'une simple application de chat en Python/Django/ gevent .

8
répondu Denis Bilenko 2010-07-30 04:07:25
la source

C'est l'un des scénarios que PHP est un très mauvais choix pour. Comme mentionné précédemment, vous pouvez attacher tous vos travailleurs Apache très rapidement en faisant quelque chose comme ça. PHP est construit pour démarrer, exécuter, arrêter. Ce n'est pas fait pour démarrer, attends...exécuter, arrêter. Vous dévalerez votre serveur très rapidement et constaterez que vous avez des problèmes incroyables de mise à l'échelle.

cela dit, Vous pouvez toujours faire cela avec PHP et le faire ne pas tuer votre serveur en utilisant le nginx HttpPushStreamModule: http://wiki.nginx.org/HttpPushStreamModule

vous configurez nginx devant Apache (ou n'importe quoi d'autre) et il s'occupera de maintenir ouvertes les connexions simultanées. Vous répondez simplement avec la charge utile en envoyant des données à une adresse interne que vous pourriez faire avec un travail de fond ou tout simplement avoir les messages tirés au loin à des personnes qui attendaient chaque fois que les nouvelles demandes viennent en. Ceci empêche les processus PHP de rester ouverts pendant le temps d'interrogation.

ceci n'est pas exclusif à PHP et peut être fait en utilisant nginx avec n'importe quel langage d'arrière-plan. La charge des connexions ouvertes simultanées est égale à Noeud.js donc le plus gros avantage est qu'il vous sort du noeud de besoin pour quelque chose comme ça.

Vous voyez beaucoup d'autres personnes de mentionner d'autres bibliothèques de langue pour la réalisation d'interrogation et c'est avec raison. PHP n'est tout simplement pas bien construit pour ce type de comportement naturellement.

7
répondu brightball 2014-04-09 21:54:34
la source

voici un noeud.exemple js fourni avec un client jquery. Il y a aussi des instructions pour l'installer sur heroku.

5
répondu Chris Hobbs 2013-03-04 07:37:10
la source

pourquoi ne pas considérer les sockets web au lieu de longs sondages? Ils sont très efficaces et faciles à installer. Cependant, ils ne sont pris en charge que par les navigateurs modernes. Voici un référence rapide .

4
répondu shasi kanth 2017-05-23 15:18:14
la source

le groupe WS-I a publié quelque chose appelé " profil de sécurité fiable " qui a un poisson de verre et .net implementation qui apparemment interfonctionne puits.

avec un peu de chance il y a une implémentation Javascript là aussi.

il y a aussi une implémentation Silverlight qui utilise le Duplex HTTP . vous pouvez connectez javascript à L'objet Silverlight pour obtenir des callbacks en cas de poussée.

Il ya aussi versions commerciales payées ainsi.

3
répondu random65537 2017-05-23 14:47:19
la source

Pour un ASP.NET MVC mise en œuvre, regarder SignalR qui est disponible sur NuGet .. notez que le NuGet est souvent périmé à partir du git source qui obtient des propagations très fréquentes.

plus d'informations à propos de SignalR sur le blog de Scott Hanselman

2
répondu random65537 2012-02-09 21:14:13
la source

vous pouvez essayer icomet ( ) https://github.com/ideawu/icomet ), un serveur comet C1000K C++ construit avec libevent. icomet fournit également une bibliothèque JavaScript, il est facile à utiliser aussi simple que

var comet = new iComet({
    sign_url: 'http://' + app_host + '/sign?obj=' + obj,
    sub_url: 'http://' + icomet_host + '/sub',
    callback: function(msg){
        // on server push
        alert(msg.content);
    }
});

icomet prend en charge un large éventail de navigateurs et D'OS, Y compris Safari(iOS, Mac), IEs(Windows), Firefox, Chrome, etc.

2
répondu ideawu 2013-10-01 06:19:56
la source

NodeJS Les Plus Simples

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

scénario de production sage en Express pour exmaple vous obtiendriez response dans le middleware. Avez-vous ce que vous devez faire, permet à tous de long interrogés méthodes de la Carte ou quelque chose (qui est visible pour les autres flux), et d'invoquer <Response> response.end() chaque fois que vous êtes prêt. Il n'y a rien de spécial dans les connexions long-sondées. Le repos est juste comment vous structurez normalement votre application.

si vous ne vous savez ce que je veux dire par là, cela devrait vous donner une idée

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

comme vous le voyez, vous pouvez vraiment répondre à toutes les connexions, Une, faire ce que vous voulez. Il y a id pour chaque requête donc vous devriez être en mesure d'utiliser la carte et l'accès spécifique à partir de l'appel api.

-1
répondu sp3c1 2018-05-04 13:50:35
la source

Autres questions sur php http comet