Press "Enter" to skip to content

采用PHP实现"服务器推"技术的聊天室

传统的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将所有的程序输出发送给客户端。

set_time_limit(0);
//设置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_clean();//关闭缓存
set_time_limit(0);
ob_implicit_flush(); //这个语句将强制每当有输出就自动刷新,相当于在每个echo后,调用ob_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 标记的开头之前缓存内容,并且在接受到

标记之前,不会显示出整个表格。
一些版本的 Microsoft Internet Explorer 只有当接受到的256(甚至更多)个字节以后才开始显示该页面,所以必须发送一些额外的空格来让这些浏览器显示页面内容。
接下来,我贴一个很有趣的代码,有兴趣的同学,可以试试:

header("Content-type: multipart/x-mixed-replace;boundary=endofsection");
print "--endofsection\n";
$pmt = array("-", "\\", "|", "/" );
for( $i = 0; $i <10;$i ++ )
{
        sleep(1);
        print "Content-type: text/plain\n\n";
        print "Part $i	".$pmt[$i % 4];
        print "--endofsection\n";
        ob_flush(); //强制将缓存区的内容输出
        flush(); //强制将缓冲区的内容发送给客户端
}
print "Content-type: text/plain\n\n";
print "The end\n";
print "–endofsection–\n";

使用firefox打开,看看你看到了什么。

26 Comments

  1. alashan007
    alashan007 June 18, 2021

    试了下,谷歌浏览器,还是一次性全输出,没成功。

  2. chen
    chen October 29, 2018

    我也没有测试成功。为何

  3. hwanginsitein
    hwanginsitein December 19, 2017

    我没有测试成功,还是一次性全部出来

  4. qianshanwanshui
    qianshanwanshui October 29, 2015

    如果用socket发送消息中用sleep,消息是在最后才一块发送,怎么办?我的大鸟哥

  5. 卢昕
    卢昕 April 7, 2014

    最后一段代码,真是让我大吃一惊!!!!!!!!

  6. xuanskyer
    xuanskyer April 30, 2013

    那一般网站的消息及时提醒,是采用什么样的方法呢?

  7. Mark Jiang
    Mark Jiang April 12, 2013

    聊天功能貌似还是用XMPP协议比较好

  8. xiaoq
    xiaoq February 21, 2013

    在Heaer部分指明了 Data部分的长度,然后使用\r\n\r\n来表示header部分结束,接下来是Data部分。
    记得是”然后用两个空行表示header部分结束“,在windows中,才是\r\n表示新行的吧。

  9. 雪心
    雪心 August 5, 2011

    没有测试成功- –
    nginx关了gzip还是没成功
    rh5.3e
    php5.2.17
    nginx0.9.5

  10. 风起
    风起 August 4, 2011

    抱歉 gtalk

  11. 风起
    风起 August 4, 2011

    gtaik是基于xmpp协议的, win下测试, 没有实现

  12. 小桑
    小桑 May 13, 2011

    为何不用NodeJs

  13. yaclty
    yaclty March 11, 2011

    看了很多的服务器退要么轮查要么iframe但是怎么感觉就是不对呢,

  14. jquery教程
    jquery教程 January 18, 2011

    呵呵~~前段时间我也看了这个comet技术~~
    但是没整明白~~
    这次看完了~~仍然没明白~哎~我太菜了~~

  15. 雪候鸟
    雪候鸟 August 17, 2009

    @wqo php服务器? 实时报警? 呵呵, 愿闻其详.

  16. wqo
    wqo August 17, 2009

    我现在想用c写个服务向php服务器发送一个报警,用户登陆网站时,可以实时报警。

  17. 雪候鸟
    雪候鸟 June 10, 2009

    最近又一次提到这个技术,呵呵, 再来看看, 回头写个更详细的出来.

  18. 3gwind
    3gwind November 6, 2008

    做为研究很是不错哦,当然如果要实际应用的话,要考虑的东西就多了。

    • 雪候鸟
      雪候鸟 November 6, 2008

      不过,现在基于这样的思想的应用已经不少了。gmail中的gtalk就是基于服务器push的技术

  19. 雪候鸟
    雪候鸟 August 24, 2008

    这只是个创新的尝试,呵呵,如果要在实际中使用,那还需要服务器的特定支持。

  20. blankyao
    blankyao August 23, 2008

    如果在实际应用中用这种推送的技术的话,服务器能受得了吗?

Comments are closed.