採用PHP實現"服務器推"技術的聊天室

· 作者:laruence(http://www.laruence.com/)
· 本文地址: http://www.laruence.com/2008/04/16/118.html
· 轉載請註明出處

  傳統的B/S結構的應用程序,都是採用"客戶端拉"結束來實現客戶端和服務器端的數據交換。
  本文將通過結合Ticks(可以參看我的另外一篇文章:關於PHP你可能不知道的-PHP的事件驅動化設計 ),來實現一個服務器推的PHP聊天室簡單構想。

  PHPer,尤其是用過set_cookie, header的,一定見過這樣的提示信息:"Warning: Cannot modify header information - headers already sent by.....", 這是因爲通過HTTP協議通信,數據包會包含倆個部分,一個是Header,一個是data。一般來說,都是先Header部分,在Heaer部分指明瞭Data部分的長度,然後使用/r/n/r/n來表示header部分結束,接下來是Data部分。

  當我們有任何輸出的時候,Header部分就發送了,這個時候,你再想header函數來改變一些Header部分的域信息,就會得到上面的提示信息。
  
   一個簡單的辦法就是使用output_buffering。讓它來緩存服務器的輸出,不要太早將Header部分發給客戶端。

   那麼,如果不使用output_buffering,是不是就可以實現,每當服務器有輸出,就立即發送給客戶端呢?

   做個如下試驗:
//設置php.ini中output_buffering=0 或者使用ob_end_flush()關閉緩存

set_time_limit(0);
for($i=0;$i<10;$i++){
  
echo "Now Index is :". $i;
  
sleep(1);
}

  結果我們發現,還是要等到腳本全部執行完以後,才能一次看到所有的結果。。
  爲什麼呢?
  這是因爲我們只是解決了緩存問題,但是還有一個緩衝問題,PHP會緩衝程序的輸出。所以,這個時候,我們還需要調用,flush(), 來強制使得PHP將所有的程序輸出發送給客戶端。
//設置php.ini中output_buffering=0
ob_end_flush();//關閉緩存

set_time_limit(0);
for($i=0;$i<10;$i++){
  
echo "Now Index is :". $i;
  
flush();
  
sleep(1);
}

    現在是不是看到了,不斷有服務器的數據顯示出來?

    有幾個概念之間的關係,我這裏補充以下:
    在代碼中使用ob_start(), 就相當於在php.ini中使用output_buffering=on一樣,使用服務器緩存。
    在代碼中使用ob_end_flush() 就相當於在php.ini中使用output_buffering = false一樣,關閉服務器緩存。
   
     基於前面的討論,我們就有可能使用Ticks來實現,一個無刷新,無ajax的聊天室: 頁面中包含倆個iframe,一個是不斷獲取聊天室的聊天內容,一個包含用戶發表聊天內容的form. 這樣,在第一個frame的腳本中:

ob_end_clear();
//關閉緩存
set_time_limit(0);
ob_implicit_flush(); 
//這個語句將強制每當有輸出就自動刷新,相當於在每個echo後,調用flush()
$new_mesg = NULL;
register_tick_function("getNewMesg");
declare(ticks=1){
  
while(1){
     
if(!is_null($new_mesg)){
          
foreach($new_mesg as $msg){
                
echo $msg;
          }
          
$new_mesg = null;
     }     
  }
}

function getNewMesg(){
//通過查詢數據庫,或者共享內存,來獲取現在的聊天室大廳的內容。
//返回一個數組,包含所有的新的聊天內容

}

 這樣就實現了一個簡單的使用服務器推技術的聊天室的框架。
 當然,關於實時輸出,還有一些其他的限制,比如在PHP5手冊中講到的:
個別web服務器程序,特別是Win32下的web服務器程序,在發送結果到瀏覽器之前,仍然會緩存腳本的輸出,直到程序結束爲止。

有些Apache的模塊,比如mod_gzip,可能自己進行輸出緩存,這將導致flush()函數產生的結果不會立即被髮送到客戶端瀏覽器。

甚至瀏覽器也會在顯示之前,緩存接收到的內容。例如 Netscape 瀏覽器會在接受到換行或 html 標記的開頭之前緩存內容,並且在接受到 
</table> 標記之前,不會顯示出整個表格。

一些版本的 Microsoft Internet Explorer 只有當接受到的256個字節以後纔開始顯示該頁面,所以必須發送一些額外的空格來讓這些瀏覽器顯示頁面內容。

 接下來,我貼一個很有趣的代碼,有興趣的同學,可以試試:

header('Content-type: multipart/x-mixed-replace;boundary=endofsection');
print " --endofsection ";
$pmt = array("-", "/", "|", "/" );
for$i = 0$i <10;$i ++ )
{
        
sleep(1);
        
print "Content-type: text/plain ";
        
print "Part $i ".$pmt[$i % 4];
        
print "--endofsection ";
        
ob_flush(); //強制將緩存區的內容輸出
        flush(); //強制將緩衝區的內容發送給客戶端
}
print "Content-type: text/plain ";
print "The end ";
print "--endofsection-- ";
  使用firefox打開,看看你看到了什麼。
  這個例子,使用了ob_flush(), 這樣可以在代碼中控制緩存區內容的輸出時機,更加靈活一些。
發佈了31 篇原創文章 · 獲贊 9 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章