복붙노트

기본 "긴 폴링"을 어떻게 구현합니까?

PHP

기본 "긴 폴링"을 어떻게 구현합니까?

긴 폴링이 작동하는 방식에 대한 많은 정보를 찾을 수 있지만 (예 : this, this) 코드에서이를 구현하는 방법에 대한 간단한 예는 없습니다.

필자가 찾을 수있는 것은 모두 Dojo JS 프레임 워크와 상당히 복잡한 서버 시스템에 의존하는 cometd이다.

기본적으로 Apache를 사용하여 요청을 처리하는 방법과 새 메시지에 대해 서버를 "오래 설문 ​​조사"하는 간단한 스크립트 (예 : PHP)를 작성하는 방법은 무엇입니까?

이 예제는 확장 성, 보안 성 또는 완전 성일 필요는 없으며 단지 작동해야합니다!

해결법

  1. ==============================

    1.

    그것은 처음에 생각했던 것보다 간단합니다. 기본적으로 보낼 데이터가 사용 가능할 때까지 (즉, 새 메시지가 도착할 때까지) 아무 것도하지 않는 페이지가 있습니다.

    다음은 2-10 초 후에 간단한 문자열을 보내는 아주 기본적인 예제입니다. 404 오류를 반환하는 3 분의 1 (앞으로 나오는 자바 스크립트 예제에서 오류 처리를 보여주기 위해)

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

    참고 : 실제 사이트에서 Apache와 같은 일반 웹 서버에서이 작업을 실행하면 모든 "작업자 스레드"를 빠르게 묶어 다른 요청에 응답 할 수 없게됩니다.이 문제를 해결할 수있는 방법이 있지만 작성하는 것이 좋습니다 요청 당 하나의 스레드에 의존하지 않는 파이썬의 꼬인 (twisted)과 같은 "long-poll server". 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 초를 기다리는 것과 같습니다)

    이 방법에 대한 좋은 점은 매우 탄력적이라는 것입니다. 클라이언트의 인터넷 연결이 끊어지면 타임 아웃 한 다음 시도하고 다시 연결하십시오. 이는 폴링이 작동하는 시간에 내재되어 있으므로 복잡한 오류 처리가 필요하지 않습니다.

    어쨌든 긴 poller.html 코드는 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>
    
  2. ==============================

    2.

    슬럿의 일환으로 아주 간단한 채팅 예제가 있습니다.

    편집 : (모든 사람이 여기에 코드를 붙여 넣기 때문에)

    이것은 긴 폴링과 슬래시를 사용하는 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>
    
  3. ==============================

    3.

    토네이도는 긴 폴링을 위해 설계되었으며, 서버 코드와 JS 클라이언트 코드를 포함하여 / examples / chatdemo에 매우 적은 (수백 줄의 Python) 채팅 앱이 포함되어 있습니다. 그것은 다음과 같이 작동합니다 :

  4. ==============================

    4.

    나는 클라이언트가 일반적인 비동기 AJAX 요청처럼 보이지만 다시 오려면 "오랜 시간이 걸릴 것으로 예상한다.

    서버는 다음과 같이 보입니다.

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

    따라서 AJAX 요청은 서버로 이동합니다. 아마도 hasNewData ()가 이미 가지고있는 데이터를 알 수 있도록 마지막 업데이트시기의 타임 스탬프를 포함해야합니다. 그런 다음 서버는 새로운 데이터를 사용할 수있을 때까지 루프를 잠자 게합니다. 그동안 AJAX 요청은 여전히 ​​연결되어 있으며 데이터 대기 중입니다. 마지막으로, 새로운 데이터를 사용할 수있게되면 서버는 AJAX 요청에이를 제공하고 연결을 닫습니다.

  5. ==============================

    5.

    다음은 C #에서 long-polling을 위해 사용하는 클래스들입니다. 기본적으로 6 개의 클래스가 있습니다 (아래 참조).

  6. ==============================

    6.

    이것은 PHP 및 jQuery를 사용하여 긴 폴링을 수행하는 방법에 대한 5 분짜리 스크린 캐스트입니다. http://screenr.com/SNH

    코드는 위의 dbr 예제와 매우 비슷합니다.

  7. ==============================

    7.

    다음은 PHP에서 Erik Dubbelboer가 Content-type : multipart / x-mixed-replace 헤더를 사용하여 작성한 간단한 long-polling 예제입니다.

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

    여기에 데모가 있습니다.

    http://dubbelboer.com/multipart.php

  8. ==============================

    8.

    이걸 Comet으로 잡으려고 사용했는데 Java Glassfish 서버를 사용하여 Comet을 설정했으며 cometdaily.com을 구독하여 다른 많은 예제를 발견했습니다.

  9. ==============================

    9.

    아래는 Inform8 Web 용으로 개발 한 긴 폴링 솔루션입니다. 기본적으로 클래스를 오버라이드하고 loadData 메소드를 구현합니다. loadData가 값을 반환하거나 작업 시간이 초과되면 결과를 인쇄하고 반환합니다.

    스크립트 처리가 30 초보다 오래 걸리면 set_time_limit () 호출을 더 긴 것으로 변경해야 할 수도 있습니다.

    Apache 2.0 라이센스. github의 최신 버전 https://github.com/ryanhend/Inform8/blob/master/Inform8-web/src/config/lib/Inform8/longpoll/LongPoller.php

    라이언

    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();
    
    }
    
  10. ==============================

    10.

    코드 dbr을 보내 주셔서 감사합니다. 줄 주위에있는 long_poller.htm의 작은 오타.

    1000 /* ..after 1 seconds */
    

    나는 그것이 있어야한다고 생각한다.

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

    그것을 위해 작동합니다.

    관심이있는 분들을 위해 장고에 상응하는 기능을 시도했습니다. 새로운 Django 프로젝트를 시작합니다. 긴 폴링을 위해 lp라고 말하십시오 :

    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은 오타가 수정 된 위와 동일해야합니다. 희망이 도움이됩니다.

  11. ==============================

    11.

    Python / Django / gevent에서 간단한 채팅 앱용 코드가있는이 블로그 게시물을 살펴보십시오.

  12. ==============================

    12.

    이것은 PHP가 매우 나쁜 선택 인 시나리오 중 하나입니다. 앞에서 언급했듯이 모든 아파치 작업자를 이렇게 빨리 처리 할 수 ​​있습니다. PHP는 시작, 실행, 중지를 위해 빌드됩니다. 시작을 위해 만들어지지 않았고, 기다려 ... 실행, 중지. 서버를 매우 빠르게 습득하여 놀라운 확장 문제가 있음을 알게 될 것입니다.

    즉, PHP로 이것을 할 수 있고 nginx HttpPushStreamModule을 사용하여 서버를 죽이지 않도록 할 수 있습니다 : http://wiki.nginx.org/HttpPushStreamModule

    당신은 아파치 (또는 무엇이든) 앞에 nginx를 설치하면 동시 연결을 유지하는 일을 처리 할 것입니다. 백그라운드 작업으로 할 수있는 내부 주소로 데이터를 보내거나 새로운 요청이 들어올 때마다 대기중인 사람들에게 메시지를 보냄으로써 페이로드로 응답합니다. 이렇게하면 긴 폴링 중에 PHP 프로세스가 열려 있지 않게됩니다.

    이것은 PHP에만 독점적 인 것이 아니며 nginx를 백엔드 언어로 사용할 수 있습니다. 동시 오픈 커넥션로드는 Node.js와 동일하므로 가장 큰 이점은 이와 같이 Node를 NEEDING 노드에서 빠져 나오게한다는 것입니다.

    긴 폴링을 수행하기 위해 다른 언어 라이브러리를 언급하는 많은 사람들이 있습니다. PHP는 자연스럽게 이러한 유형의 행위에 적합하지 않습니다.

  13. ==============================

    13.

    다음은 jquery 클라이언트와 함께 제공되는 node.js 예제입니다. heroku에서 설정하는 방법에 대한 지침도 있습니다.

  14. ==============================

    14.

    긴 폴링 대신 웹 소켓을 고려해보십시오. 그들은 매우 효율적이고 설치하기 쉽습니다. 그러나 이들은 최신 브라우저에서만 지원됩니다. 다음은 빠른 참조입니다.

  15. ==============================

    15.

    WS-I 그룹은 Glass Fish와 .NET을 구현 한 "Reliable Secure Profile"이라는 제품을 발표했습니다.

    행운을 빌어 거기에 자바 스크립트 구현도 있습니다.

    HTTP Duplex를 사용하는 Silverlight 구현도 있습니다. 푸시가 발생할 때 콜백을 얻기 위해 javascript를 Silverlight 객체에 연결할 수 있습니다.

    상업용 유료 버전도 있습니다.

  16. ==============================

    16.

    ASP.NET MVC 구현의 경우, NuGet에서 사용할 수있는 SignalR을 살펴보십시오. NuGet은 커밋이 빈번히 발생하는 Git 소스와 구식 인 경우가 많습니다.

    Scott Hanselman의 블로그에서 SignalR에 대해 더 자세히 읽어보십시오.

  17. ==============================

    17.

    libevent로 구축 된 C1000K C ++ 혜성 서버 인 icomet (https://github.com/ideawu/icomet)을 시험해 볼 수 있습니다. icomet은 또한 JavaScript 라이브러리를 제공하며, 다음과 같이 간단하게 사용할 수 있습니다.

    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은 Safari (iOS, Mac), IE (Windows), Firefox, Chrome 등 다양한 브라우저 및 OS를 지원합니다.

  18. ==============================

    18.

    가장 간단한 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);
    }
    

    익스프레스에서 프로덕션 현명한 시나리오를 사용하면 미들웨어에서 응답을 얻을 수 있습니다. 필요한 모든 작업을 수행하여 긴 폴링 메소드를 Map 또는 무언가 (다른 플로우에서 볼 수 있음)로 범위 지정하고 준비가되면 response.end ()를 호출 할 수 있습니다. 긴 폴링 연결에는 특별한 것이 없습니다. 나머지는 응용 프로그램을 일반적으로 구조화하는 방법입니다.

    내가 범위를 정하는 것이 무엇을 의미하는지 모른다면, 이것은 당신에게 아이디어를 줄 것입니다.

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

    보시다시피, 모든 연결에 실제로 응답 할 수 있습니다. 하나는 원하는대로 수행하십시오. 모든 요청에 ​​대해 ID가 있으므로 API 호출에서 맵과 액세스를 사용할 수 있어야합니다.

  19. from https://stackoverflow.com/questions/333664/how-do-i-implement-basic-long-polling by cc-by-sa and MIT lisence