3

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

 2 years ago
source link: https://www.laruence.com/2008/04/16/118.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

采用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()关闭缓存

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

结果我们发现,还是要等到脚本全部执行完以后,才能一次看到所有的结果。。
为什么呢?
这是因为我们只是解决了缓存问题,但是还有一个缓冲问题,PHP会缓冲程序的输出。所以,这个时候,我们还需要调用,flush(), 来强制使得PHP将所有的程序输出发送给客户端。

  1. set_time_limit(0);
  2. //设置php.ini中output_buffering=0
  3. ob_end_flush();//关闭缓存
  4. set_time_limit(0);
  5. for($i=0;$i<10;$i++){
  6.   echo "Now Index is :". $i;
  7.   flush();
  8.   sleep(1);

现在是不是看到了,不断有服务器的数据显示出来(如果看不到, 可以在输出前填充相当数量的占位字符)?
有几个概念之间的关系,我这里补充以下:
在代码中使用ob_start(), 就相当于在php.ini中使用output_buffering=on一样,使用服务器缓存。
在代码中使用ob_end_flush() 就相当于在php.ini中使用output_buffering = false一样,关闭服务器缓存.
基于前面的讨论,我们就有可能使用Ticks来实现,一个无刷新,无ajax的聊天室: 页面中包含俩个iframe,一个是不断获取聊天室的聊天内容,一个包含用户发表聊天内容的form. 这样,在第一个frame的脚本中:

  1. ob_end_clean();//关闭缓存
  2. set_time_limit(0);
  3. ob_implicit_flush(); //这个语句将强制每当有输出就自动刷新,相当于在每个echo后,调用ob_flush()
  4. $new_mesg = NULL;
  5. register_tick_function("getNewMesg");
  6. declare(ticks=1){
  7.   while(1){
  8.      if(!is_null($new_mesg)){
  9.           foreach($new_mesg as $msg){
  10.                 echo $msg;
  11.           $new_mesg = null;
  12. function getNewMesg(){
  13. //通过查询数据库,或者共享内存,来获取现在的聊天室大厅的内容。
  14. //返回一个数组,包含所有的新的聊天内容

这样就实现了一个简单的使用服务器推技术的聊天室的框架。
当然,关于实时输出,还有一些其他的限制,比如在PHP5手册中讲到的:
个别web服务器程序,特别是Win32下的web服务器程序,在发送结果到浏览器之前,仍然会缓存脚本的输出,直到程序结束为止。
有些Apache的模块,比如mod_gzip,可能自己进行输出缓存,这将导致flush()函数产生的结果不会立即被发送到客户端浏览器。
甚至浏览器也会在显示之前,缓存接收到的内容。例如 Netscape 浏览器会在接受到换行或 html 标记的开头之前缓存内容,并且在接受到

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK