- 本文地址: https://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将所有的程序输出发送给客户端。
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打开,看看你看到了什么。
try to use NodeJs
试了下,谷歌浏览器,还是一次性全输出,没成功。
[…] 本文地址: https://www.laruence.com/2008/04/16/118.html […]
我也没有测试成功。为何
我没有测试成功,还是一次性全部出来
聊天本质上是要长连接
web聊天基本上就离不开web Socket
关于Websocket的介绍请见
http://log4geek.cc/2016/12/spring-websocket%e7%ae%80%e5%8d%95%e5%85%a5%e9%97%a8%e6%b5%8b%e8%af%95demo%e7%bd%91%e9%a1%b5%e5%8f%8a%e6%97%b6%e8%81%8a%e5%a4%a9/
如果用socket发送消息中用sleep,消息是在最后才一块发送,怎么办?我的大鸟哥
最后一段代码,真是让我大吃一惊!!!!!!!!
[…] 上面的代码在火狐和ie上面是一样的效果,表达能力有限,参考下面两篇文章吧http://www.laruence.com/2008/04/16/118.htmlhttp://blog.csdn.net/gmstart/article/details/7064034 回答: php 没有浏览器差异 […]
那一般网站的消息及时提醒,是采用什么样的方法呢?
聊天功能貌似还是用XMPP协议比较好
在Heaer部分指明了 Data部分的长度,然后使用\r\n\r\n来表示header部分结束,接下来是Data部分。
记得是”然后用两个空行表示header部分结束“,在windows中,才是\r\n表示新行的吧。
[…] 原文地址: http://www.laruence.com/2008/04/16/118.html 转载请注明出处 […]
没有测试成功- –
nginx关了gzip还是没成功
rh5.3e
php5.2.17
nginx0.9.5
抱歉 gtalk
gtaik是基于xmpp协议的, win下测试, 没有实现
为何不用NodeJs
看了很多的服务器退要么轮查要么iframe但是怎么感觉就是不对呢,
呵呵~~前段时间我也看了这个comet技术~~
但是没整明白~~
这次看完了~~仍然没明白~哎~我太菜了~~
@wqo php服务器? 实时报警? 呵呵, 愿闻其详.
我现在想用c写个服务向php服务器发送一个报警,用户登陆网站时,可以实时报警。
最近又一次提到这个技术,呵呵, 再来看看, 回头写个更详细的出来.
做为研究很是不错哦,当然如果要实际应用的话,要考虑的东西就多了。
不过,现在基于这样的思想的应用已经不少了。gmail中的gtalk就是基于服务器push的技术
这只是个创新的尝试,呵呵,如果要在实际中使用,那还需要服务器的特定支持。
如果在实际应用中用这种推送的技术的话,服务器能受得了吗?