Jak zaimplementować podstawowe "Długie ankiety"?

Mogę znaleźć wiele informacji na temat tego, jak długo działa ankieta (na przykład, this i this), ale nie proste przykłady jak zaimplementować to w kodzie.

Jedyne co mogę znaleźć tocometd , który opiera się na frameworku Dojo JS i dość skomplikowanym systemie serwerowym..

Zasadniczo, jak używać Apache do obsługi żądań, i jak napisać prosty skrypt (powiedzmy, w PHP), który "długo ankieta" serwer dla nowych wiadomości?

The przykład nie musi być skalowalny, Bezpieczny lub kompletny, po prostu musi działać!

 749
Author: Sinister Beard, 2008-12-02

18 answers

To prostsze niż początkowo myślałem.. Zasadniczo masz stronę, która nic nie robi, dopóki dane, które chcesz wysłać, nie będą dostępne(powiedzmy, że pojawi się nowa wiadomość).

Oto naprawdę podstawowy przykład, który wysyła prosty ciąg po 2-10 sekundach. 1 na 3 szansa na zwrócenie błędu 404 (aby pokazać obsługę błędów w nadchodzącym przykładzie Javascript)

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

Uwaga: w przypadku prawdziwej strony, uruchomienie tego na zwykłym serwerze internetowym, takim jak Apache, szybko połączy wszystkie "wątki robocze" i pozostawić go w stanie odpowiedzieć na inne żądania.. Istnieją sposoby na obejście tego, ale zaleca się napisanie "serwera long-poll" w czymś takim jak Python twisted, który nie opiera się na jednym wątku na żądanie. cometD jest popularnym frameworkiem (dostępnym w kilku językach), a Tornado jest nowym frameworkiem stworzonym specjalnie do takich zadań (został zbudowany na potrzeby kodu Long-polling FriendFeed)... ale jako prosty przykład, Apache jest czymś więcej niż / align = "left" / Ten skrypt może być łatwo napisany w dowolnym języku (wybrałem Apache/PHP, ponieważ są one bardzo powszechne i zdarzyło mi się uruchamiać je lokalnie)

Następnie, w Javascript, żądasz powyższego pliku (msg_srv.php) i czekasz na odpowiedź. Kiedy je zdobędziesz, działasz na podstawie danych. Następnie żądasz pliku i czekasz ponownie, działaj na dane (i powtarzaj)

Poniżej znajduje się przykład takiej strony.. Po załadowaniu strony wysyła początkowe żądanie pliku msgsrv.php.. Jeśli to jeśli się powiedzie, dodajemy wiadomość do #messages div, po 1 sekundzie ponownie wywołujemy funkcję waitForMsg, która uruchamia wait.

1 sekunda setTimeout() jest naprawdę podstawowym ogranicznikiem szybkości, działa bez tego dobrze, ale jeśli msgsrv.php zawsze powraca natychmiast (np. z błędem składni) - zalewasz przeglądarkę i może ona szybko się zawiesić. Lepiej zrobić to sprawdzając, czy plik zawiera poprawną odpowiedź JSON i / lub zachowując działającą sumę żądania-na minutę/sekundę i odpowiednio wstrzymuje.

Jeśli strona jest błędna, dopisze błąd do #messages div, czeka 15 sekund, a następnie próbuje ponownie (identycznie jak czekamy 1 sekundę po każdej wiadomości)

Fajną rzeczą w tym podejściu jest to, że jest bardzo odporne. Jeśli połączenie internetowe klienta umrze, nastąpi timeout, a następnie spróbuj ponownie się połączyć-jest to nieodłączne od tego, jak długo działa ankieta, nie jest wymagana skomplikowana obsługa błędów]}

W każdym razie, long_poller.htm Kod, korzystanie z frameworka 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
Author: dbr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-01 11:24:47

Mam bardzo prosty przykład czatu w ramach slosh .

Edit : (skoro wszyscy wklejają tu swój kod)

Jest to kompletny chat wieloosobowy oparty na JSON, wykorzystujący long-polling i slosh . To jest demo Jak wykonywać połączenia, więc proszę zignorować problemy z XSS. Nikt nie powinien tego instalować bez uprzedniego dezynfekcji.

Zauważ, że klient zawsze ma połączenie z serwerem i jak tylko ktoś wyśle wiadomość, każdy powinien zobaczyć ją mniej więcej natychmiast.

<?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
Author: Dustin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-04 08:22:44

Tornado jest przeznaczone do długiego sondowania i zawiera bardzo minimalną (kilkaset linii Pythona) aplikację do czatu w / examples / chatdemo , w tym kod serwera i Kod klienta JS. Działa tak:

  • Klienci używają JS, aby poprosić o aktualizację od (liczba ostatniej wiadomości), server URLHandler odbiera te i dodaje callback, aby odpowiedzieć na klienta do kolejki.

  • Gdy serwer otrzyma nową wiadomość, zostanie wywołane zdarzenie onmessage, zapętla połączenia zwrotne i wysyła wiadomości.

  • JS po stronie klienta otrzymuje wiadomość, dodaje ją do strony, a następnie prosi o aktualizacje od tego nowego identyfikatora wiadomości.

 31
Author: mikemaccana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-16 13:33:59

Myślę, że klient wygląda jak normalne asynchroniczne żądanie AJAX, ale spodziewasz się, że powrót zajmie "dużo czasu".

Serwer wygląda więc tak.

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

outputNewData();

Więc, żądanie AJAX idzie do serwera, prawdopodobnie w tym znacznik czasu, kiedy była ostatnia aktualizacja tak, że hasNewData() wie, jakie dane już masz. Następnie serwer siedzi w pętli uśpionej, dopóki nie będą dostępne nowe dane. Cały czas, żądanie AJAX jest nadal podłączony, po prostu wisi tam czeka na data. Wreszcie, gdy nowe dane są dostępne, serwer przekazuje je do żądania AJAX i zamyka połączenie.

 24
Author: Greg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-02 11:39:37

Proszę. czy są jakieś klasy, których używam do długich ankiet w C#. Istnieje zasadniczo 6 klas(patrz poniżej).

  1. Controller : przetwarza akcje wymagane do utworzenia prawidłowej odpowiedzi (operacje db itp.)
  2. Procesor : zarządza komunikacją asynchroniczną ze stroną www (samą w sobie)
  3. IAsynchProcessor : usługa przetwarza instancje implementujące ten interfejs
  4. Sevice : przetwarza obiekty żądające, które implementują IAsynchProcessor
  5. Request : wrapper IAsynchProcessor zawierający twoją odpowiedź (obiekt)
  6. Response: zawiera niestandardowe obiekty lub pola
 17
Author: Prisoner ZERO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:41

To jest fajny 5-minutowy screencast jak zrobić długie ankiety przy użyciu PHP & jQuery: http://screenr.com/SNH

Kod

Jest bardzo podobny do przykładu dbr powyżej.

 16
Author: Sean O,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-20 15:41:38

Oto prosty przykład long-polling w PHP autorstwa Erika Dubbelboera używając nagłówka 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
';

A oto demo:

Http://dubbelboer.com/multipart.php

 12
Author: Jasdeep Khalsa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-08 12:29:21

Użyłem tego aby uporać się z Comet, skonfigurowałem również Comet używając serwera Java Glassfish i znalazłem wiele innych przykładów subskrybując cometdaily.com

 11
Author: adam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-02 11:21:34

Poniżej znajduje się długie rozwiązanie ankietowe, które opracowałem dla Inform8 Web. Zasadniczo nadpisujesz klasę i zaimplementujesz metodę loadData. Gdy loadData zwróci wartość lub czas operacji się skończy, wydrukuje wynik i zwróci.

Jeśli przetwarzanie skryptu może trwać dłużej niż 30 sekund, może być konieczna zmiana wywołania set_time_limit() na coś dłuższego.

Licencja Apache 2.0. Najnowsza wersja na 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
Author: Ryan Henderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-05 10:39:24

Dzięki za kod, dbr . Mała literówka w long_poller.htm wokół linii

1000 /* ..after 1 seconds */

Myślę, że powinno być

"1000"); /* ..after 1 seconds */
Żeby zadziałało. Dla zainteresowanych wypróbowałem odpowiednik Django. Rozpocznij nowy projekt Django, powiedzmy na za długie głosowanie:
django-admin.py startproject lp

Wywołanie aplikacji msgsrv dla serwera wiadomości:

python manage.py startapp msgsrv

Dodaj następujące wiersze do settings.py aby mieć szablony katalog:

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

Zdefiniuj wzorce URL w urls.py jako takie:

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

I msgsrv / views.py powinno wyglądać tak:

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

Wreszcie, templates / long_poller.htm powinien być taki sam jak powyżej z poprawką literówki. Mam nadzieję, że to pomoże.

 8
Author: xoblau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-15 12:30:50

Spójrz na ten wpis na blogu , który zawiera kod dla prostej aplikacji do czatu w Pythonie/Django/gevent .

 8
Author: Denis Bilenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-30 00:07:25

Jest to jeden ze scenariuszy, dla których PHP jest bardzo złym wyborem. Jak wcześniej wspomniano, możesz bardzo szybko związać wszystkich swoich pracowników Apache, robiąc coś takiego. PHP jest zbudowany dla start, execute, stop. Nie jest zbudowany na początek, czekaj...wykonać, zatrzymać się. Bardzo szybko zniszczysz swój serwer i przekonasz się, że masz niesamowite problemy ze skalowaniem.

To powiedziawszy, nadal możesz to zrobić z PHP i nie zabić serwera za pomocą nginx HttpPushStreamModule: http://wiki.nginx.org/HttpPushStreamModule

Skonfigurujesz nginx przed Apache ' em (czy czymkolwiek innym), a on zajmie się utrzymywaniem połączeń współbieżnych. Po prostu odpowiadasz ładunkiem, wysyłając dane na adres wewnętrzny, który możesz zrobić w tle lub po prostu odpalać Wiadomości do osób, które czekały, gdy pojawią się nowe żądania. Dzięki temu procesy PHP nie są otwarte podczas długiego sondowania.

To nie jest wyłącznie PHP i można to zrobić za pomocą nginx z dowolnym językiem zaplecza. Obciążenie współbieżnych połączeń otwartych jest równe Node.js więc największym plusem jest to, że wyciąga cię z konieczności węzła do czegoś takiego.

Widzisz wiele innych osób, które wspominają o innych bibliotekach językowych dla przeprowadzenia długich ankiet i to nie bez powodu. PHP nie jest po prostu dobrze zbudowany dla tego typu zachowań naturalnie.

 7
Author: brightball,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-09 17:54:34

Tutaj jest węzeł.przykład js dostarczany z klientem jquery. Jest też instrukcja ustawienia go na heroku.

 5
Author: Chris Hobbs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-04 03:37:10

Dlaczego nie wziąć pod uwagę gniazd sieciowych zamiast długich sondaży? Są bardzo wydajne i łatwe w konfiguracji. Jednak są one obsługiwane tylko w nowoczesnych przeglądarkach. Oto szybka Referencja.

 4
Author: shasi kanth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:14

Grupa ws-i opublikowała coś o nazwie "niezawodny bezpieczny Profil", który ma szklaną rybę i implementację.NET, która najwyraźniej współdziała dobrze.

Przy odrobinie szczęścia istnieje również implementacjaJavascript .

Istnieje również implementacja Silverlight, która używa dupleksu HTTP. możesz podłączyć javascript do obiektu Silverlight , aby uzyskać wywołania zwrotne, gdy wystąpi push.

Są też komercyjne płatne wersje jak również.

 3
Author: random65537,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:19

Dla a ASP.NET implementacja MVC, spójrz na SignalR , który jest dostępny na NuGet.. zauważ, że NuGet jest często Nieaktualny ze źródła Git , które otrzymuje bardzo częste commity.

Więcej o SignalR przeczytasz na blogu Scotta Hanselmana
 2
Author: random65537,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-09 17:14:13

Możesz spróbować icomet ( https://github.com/ideawu/icomet ), c1000k C++ Comet server zbudowany z libevent. icomet zapewnia również bibliotekę JavaScript, jest łatwy w użyciu tak proste jak

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 obsługuje szeroką gamę przeglądarek i systemów operacyjnych, w tym Safari( iOS, Mac), IEs(Windows), Firefox, Chrome itp.
 2
Author: ideawu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-01 02:19:56

Najprostsze NodeJS

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

Scenariusz produkcji w Expressie dla exmaple otrzymałbyś response w middleware. Zrób to, co musisz zrobić, możesz sprawdzić wszystkie długo ankietowane metody mapowania lub coś (co jest widoczne dla innych przepływów) i wywołać <Response> response.end(), Gdy jesteś gotowy. Nie ma nic specjalnego w długich ankietowanych połączeniach. Reszta jest taka, jak zwykle struktura aplikacji.

Jeśli nie wiesz, o co mi chodzi, to powinno dać ci idea

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);
Jak widzisz, możesz naprawdę reagować na wszystkie połączenia, po pierwsze, robić, co chcesz. Istnieje id dla każdego żądania, więc powinieneś być w stanie korzystać z mapy i dostępu określonego z wywołania api.
 -1
Author: sp3c1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-04 10:50:35