Flex+PHP反向推送(長輪詢)

來源:互聯網
上載者:User

反向推送技術現在非常流行, 而長輪詢是實現反向推送的關鍵技術之一.

//如果要轉載本文請註明出處,免的出現版權紛爭,我不喜歡看到那種轉載了我的作品卻不註明出處的人 Seven{See7di#Gmail.com}
HTTP 協議的成功毋庸置疑。它是 Internet上大部分資訊交換的基礎。然而,它也有一些局限性。特別是,它是無狀態、單向的協議。請求被發送到
Web 服務器,伺服器處理請求並發回一個響應 —僅此而已。請求必須由客戶機發出,而伺服器則只能在對請求的響應中發送資料。這至少會影響很多類型的
Web應用程式的實用性。典型的例子就是聊天程式。另外還有一些例子,例如比賽的比分或電子郵件程式。

HTTP的這些局限性也是它取得一定成功的原因。請求/響應周期使它成為了經典的模型,即每個串連使用一個線程。只要能夠快速為請求提供服務,這種方法就
有巨大的延展性。每秒鐘可以處理大量的請求,只需使用少量的伺服器就可以處理很大數量的使用者。對於很多經典的
Web應用程式,例如內容管理系統、搜尋應用程式和電子商務站 點等等而言,這非常適合。在以上任何一種
Web應用程式中,伺服器提供使用者請求的資料,然後關閉串連,並釋放那個線程,使之可以為其他請求服務。如果提供初始資料之後仍可能存在互動,那麼將串連
保持為開啟狀態,因此線程就不能釋放出來,伺服器也就不能為很多使用者服務。

但是,如果想在對請求做出響應並發送初始資料之後,仍然保持與使用者的互動呢?在 Web 早期,這一點常使用
meta重新整理實現。這將自動指示瀏覽器在指定秒數之後重新裝載頁面,從而支援簡陋的輪詢(polling)。這不僅是一種糟糕的使用者體驗,而且通常效率非
常低下。如果沒有新的資料要顯示在頁面上呢?這時不得不重新呈現同樣的頁面。如果對頁面的更改很少,並且頁面的大部分沒有變化呢?同樣,不管是否有必要,
都得重新請求和擷取頁面上的一切內容。

Ajax
的發明和流行改變了上述狀況。現在,伺服器可以非同步通訊,因此不必重新請求整個頁面。現在可以進行增量式的更新。只需使用XMLHttpRequest
輪詢伺服器。這項技術通常被稱作Comet。這項技術存在一些變體,每種變體具有不同的效能和延展性。我們來看看這些不同風格的 Comet。

Comet 風格

Ajax 的出現使 Comet 成為可能。HTTP的單向性質可以有效地加以規避。實際上有一些不同的方法可以繞過這一點。您可能已經猜到,支援
Comet 的最容易的方式是輪詢(poll)。使用XMLHttpRequest 向伺服器發出調用,返回後,等待一段固定的時間(通常使用
JavaScript 的 setTimeout函數),然後再次調用。這是一項非常常見的技術。例如,大多數 webmail
應用程式就是通過這種技術在電子郵件到達時顯示電子郵件的。

這項技術有優點也有缺點。在這種情況下,您期望快速返迴響應,就像任何其他
Ajax請求一樣。在請求之間必須有一段暫停。否則,連續不斷的請求會衝垮伺服器,並且這種情況下顯然不具有延展性。這段暫停使應用程式產生一個延時。
暫停時間越長,伺服器上的新資料就需要越多的時間才能到達客戶機。如果縮短暫停時間,又將重新面臨衝垮伺服器的風險。但是另一方面,這顯然是最簡單的實
現Comet 的方式。

現在應該指出,很多人認為輪詢並不屬於 Comet。相反,他們認為 Comet 是對輪詢的局限性的一個解決方 案。最常見的 “真正的”Comet
技術是輪詢的一種變體,即長輪詢(longpolling)。輪詢與長輪詢之間的主要區別在於伺服器花多長的時間作出響應。長輪詢通常將串連保持一段較長
的時間 —通常是數秒鐘,但是也可能是一分鐘甚至更長。當伺服器上發生某個事件時,響應被發送並隨即關閉,輪詢立即重新開始。

長輪詢相對於一般輪詢的優點在於,資料一旦可用,便立即從伺服器發送到客戶機。請求可能等待較長的時間,期間沒有任何資料返回,但是一旦有了新的資料,它
將立即被發送到客戶機。因此沒有延時。如果您使用過基於 Web 的聊天程式,或者聲稱 “即時” 的任何程式,那麼它很可能就是使用了這種技術。

下面我就Flex和PHP來舉例說明一下,常輪詢如何?.

首先將一下PHP端的代碼,很簡單 

<?php
//timeout in seconds
$timeout = 60;

// log start time
$start_time = time();

// get messge from local file
function get_msg(){
    return file_get_contents('msg.txt');
}

// get message
$last_msg = get_msg();

// start the loop
while (true){
    // get current time
    $current_time = time();
    
    // check if we are timed out
    if ($current_time - $start_time > $timeout){
        echo 'timeout! no new message!';
        break;
    }
    
    // get latest message
    $current_msg = get_msg();
    
    // check if the message has been changed
    if ($last_msg != $current_msg){
        echo $current_msg;
        break;
    }
    // sleep 1 sec
    sleep(1);
}

分析上面的代碼, 其實原理就是在php中執行一個迴圈, 每次迴圈的時候去訪問伺服器上的資料,
可以是資料庫也可以是一個文字檔,這裡採用文字檔, 然後判斷文字檔的內容是否更新過,如果更新過則將資料返回給用戶端. 在迴圈當中我們放入了
sleep(1)函數讓代碼每次迴圈的時候暫停1秒鐘,這樣不至於過度損耗伺服器CPU的資源.

下面是flex端的代碼

<?xml version="1.0" encoding="utf-8"?>
        <![CDATA[
            // request object
            private var req:URLRequest;
            
            // loader object
            private var loader:URLLoader;
            
            // timer
            private var timer:Timer;
            
            [Bindable]
            private var count:Number = 0;            
            
            // do some initializing
            private function init():void{
                req = new URLRequest('http://127.0.0.1:8080/long_polling.php');
                loader = new URLLoader();
                
                loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onStatusChange);
                loader.addEventListener(Event.COMPLETE, onComplete);
                loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
                
                timer = new Timer(1000);
                timer.addEventListener(TimerEvent.TIMER, onTimer, false, 0, true);
                
            }
            
            // update the second count
            private function onTimer(event:TimerEvent):void{
                count ++;
            }
            
            // start the request
            private function startRequest():void{
                count = 0;
                loader.load(req);
                timer.start();
                this.txtLogs.text += 'Request started!\n';
            }
            
            // status changed
            private function onStatusChange(event:HTTPStatusEvent):void{
                this.txtLogs.text += 'Status changed to '+ String(event.status)+'\n';
            }            
            
            // result returned
            private function onComplete(event:Event):void{
                this.txtLogs.text += 'Completed! Result is '+String(event.currentTarget.data)+'\n';
                this.txtLogs.text += 'Start another request!\n';
                this.startRequest();
            }
            
            // error handler
            private function onIOError(event:IOErrorEvent):void{
                this.txtLogs.text += 'IO Error: '+String(event)+'\n';
                this.stopRequest();
            }
            
            // stop the request
            private function stopRequest():void{
                count = 0;
                this.txtLogs.text += 'Request stopped!\n';
                this.txtLogs.text += '<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>\n'
                this.timer.stop();
                this.loader.close();
                
            }
        ]]>

我們建立一個flex項目, 然後在介面上放置4個空間, 兩個按鈕, 一個是開始請求, 一個是結束請求, 還有一個label用來顯示當前請求所花的時間, 最後再放一個TextArea來顯示log.

請求的原理也同樣簡單, 通過URLLoader和URLRequest來實現,
和普通的http請求並無兩樣.不過在一次請求擷取到結果的時候(走到onComplete()函數),
我們需要重新開始另外一次查詢,通過這種方式我們就可以類比出一個類似與C/S架構的網路連接, 而服務端上的任何更新則會自動推送到用戶端了.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.