php实现实时通信
来源:互联网 发布:jdk源码 视频教程 编辑:程序博客网 时间:2024/04/30 23:39
如果英文ok的话,可以直接看这篇文章
实现实时通信一般有两种方式:
socket或comet。socket是比较好的解决方案,问题在于不是所有的浏览器都兼容,服务器端实现起来也稍微有点麻烦。相比之下,comet(基于HTTP长连接的"服务器推")实现起来更加方便,而且兼容所有的浏览器。所以这次就来说说comet的php实现。
comet也有好几种实现方式,如iframe, http long request,二者的区别可以参考这篇文章。本文主要探讨http long request实现实时通信。
先说说http长链接是怎么回事,通俗点讲就是服务器不是一收到请求就直接吐数据,而是在那憋啊憋,一直憋到憋不住了,才告诉你执行结果。
<?php$count = 10;for($i=0; $i<$count; $i++){// do something ...sleep(2);}echo '憋死我了';
至于憋多长时间,就看具体应用了,如果憋太久的话,服务器资源的占用也会是个问题。
现在我们就要通过这种方法来实现实时通信(其实是准实时),先说一下原理:
1. 客户端发起一个ajax长链接查询,然后服务端就开始执行代码,主要是检查某个文件是否被更新,如果没有,睡一会(sleep),醒来接着检查
2. 如果客户端又发起了一个查询链接(正常请求),服务端收到后,处理请求,处理完毕后更新某个特定文件的modify time
3. 这时第一次ajax查询的后台代码还在执行,发现某个文件被更新,说明来了新请求,输出对应的结果
4. 第一次ajax查询的callback被触发,更新页面,然后再发起一个新的ajax长链接
实战
客户端
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Comet Test</title> </head> <body> <p><a class='customAlert' href="#">publish customAlert</a></p> <p><a class='customAlert2' href="#">publish customAlert2</a></p> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js" type="text/javascript"></script> <script src="NovComet.js" type="text/javascript"></script> <script type="text/javascript">NovComet.subscribe('customAlert', function(data){console.log('customAlert');//console.log(data);}).subscribe('customAlert2', function(data){console.log('customAlert2');//console.log(data);});$(document).ready(function() {$("a.customAlert").click(function(event) {NovComet.publish('customAlert');});$("a.customAlert2").click(function(event) {NovComet.publish('customAlert2');});NovComet.run();}); </script> </body></html>
这段代码说的是,有个NovComet的Object,注册了customAlert和customAlert2事件,当页面载入完成时,对两个按钮又加了监听事件,当点击时NovComet会发布customAlert或customAlert2事件,然后NovComet执行了run方法。
NovComet
//NovComet.jsNovComet = { sleepTime: 1000, _subscribed: {}, _timeout: undefined, _baseurl: "comet.php", _args: '', _urlParam: 'subscribed', subscribe: function(id, callback) { NovComet._subscribed[id] = { cbk: callback, timestamp: NovComet._getCurrentTimestamp() }; return NovComet; }, _refresh: function() { NovComet._timeout = setTimeout(function() { NovComet.run() }, NovComet.sleepTime); }, init: function(baseurl) { if (baseurl!=undefined) { NovComet._baseurl = baseurl; } }, _getCurrentTimestamp: function() { return Math.round(new Date().getTime() / 1000); }, run: function() { var cometCheckUrl = NovComet._baseurl + '?' + NovComet._args; for (var id in NovComet._subscribed) { var currentTimestamp = NovComet._subscribed[id]['timestamp']; cometCheckUrl += '&' + NovComet._urlParam+ '[' + id + ']=' + currentTimestamp; } cometCheckUrl += '&' + NovComet._getCurrentTimestamp(); $.getJSON(cometCheckUrl, function(data){ switch(data.s) { case 0: // sin cambios NovComet._refresh(); break; case 1: // trigger for (var id in data['k']) { NovComet._subscribed[id]['timestamp'] = data['k'][id]; NovComet._subscribed[id].cbk(data.k); } NovComet._refresh(); break; } }); }, publish: function(id) { var cometPublishUrl = NovComet._baseurl + '?' + NovComet._args; cometPublishUrl += '&publish=' + id; $.getJSON(cometPublishUrl); }};
NovComet的run方法首先把之前注册的几个事件串成一个url,并且很狡猾地使用了"[]",类似:?subscribed[customAlert]=1300016814&subscribed[customAlert2]=1300016814&1300016825,这样php收到后,就会得到$_GET[subscribed]数组,最后那个时间戳是为了避免请求被缓存。如果收到后台传过来的数据data的s值为0,说明什么也没发生,隔1秒后继续执行;如果data.s的值为1,说明NovComet的publish事件被触发,则调用对应的callback。publish方法执行后,会构造一个类似: ?publish=customAlert 这样一个url发送到后台。后台检测到pubish参数,则获取该参数的值,并更新对应文件的mtime。
服务端
<?php// comet.phpinclude('NovComet.php');$comet = new NovComet();$publish = filter_input(INPUT_GET, 'publish', FILTER_SANITIZE_STRING);if ($publish != '') { echo $comet->publish($publish);} else { foreach (filter_var_array($_GET['subscribed'], FILTER_SANITIZE_NUMBER_INT) as $key => $value) { $comet->setVar($key, $value); } echo $comet->run();}
如果收到publish参数,直接输出,否则执行run方法,至于run是怎么回事,且看下码。
<?php// NovComet.phpclass NovComet { const COMET_OK = 0; const COMET_CHANGED = 1; private $_tries; private $_var; private $_sleep; private $_ids = array(); private $_callback = null; public function __construct($tries = 20, $sleep = 2) { $this->_tries = $tries; $this->_sleep = $sleep; } public function setVar($key, $value) { $this->_vars[$key] = $value; } public function setTries($tries) { $this->_tries = $tries; } public function setSleepTime($sleep) { $this->_sleep = $sleep; } public function setCallbackCheck($callback) { $this->_callback = $callback; } const DEFAULT_COMET_PATH = "/dev/shm/%s.comet"; public function run() { if (is_null($this->_callback)) { $defaultCometPAth = self::DEFAULT_COMET_PATH; $callback = function($id) use ($defaultCometPAth) { $cometFile = sprintf($defaultCometPAth, $id); return (is_file($cometFile)) ? filemtime($cometFile) : 0; }; } else { $callback = $this->_callback; } for ($i = 0; $i < $this->_tries; $i++) { foreach ($this->_vars as $id => $timestamp) { if ((integer) $timestamp == 0) { $timestamp = time(); } $fileTimestamp = $callback($id); if ($fileTimestamp > $timestamp) { $out[$id] = $fileTimestamp; } clearstatcache(); } if (count($out) > 0) { return json_encode(array('s' => self::COMET_CHANGED, 'k' => $out)); } sleep($this->_sleep); } return json_encode(array('s' => self::COMET_OK)); } public function publish($id) { return json_encode(touch(sprintf(self::DEFAULT_COMET_PATH, $id))); }}
可以看到publish时,创建了一个以$id命名的文件。run时,如果发现该$id文件存在,且时间戳大于之前保存的该$id对应的时间戳(通过setVar设置的),说明$id事件被触发,处理完后把$id放到$out数组中,然后判断一下$out数组是否为空,如果不为空,则输出一段json。
如果一段时间内都没有触发事件(for循环执行完毕),也输出一段json,告诉前端执行完了,但是没有任何新情况。
说明
- 可以在客户端监听/发布多个事件
- 监听事件时,可以传一个callback,这样收到数据时就会出发该callback
- 当监听事件时,会传一个时间戳
- 当事件被publish时,会向后台发一个请求,并传递一个新的时间戳
- 服务端不会一直执行,如果指定时间内,没有任何请求被触发,则结束运行
- 客户端会重复上述过程(setTimeout & NovComet.run())
最后来一张图说明一下
1. 运行一段时间后,没有收到任何publish事件,服务端结束执行
2. 服务端返回一段json
3. 客户端触发了一个事件,服务端收到事件,返回一段新的json
4. callback被触发
5. 客户端进入下一次的ajax长链接查询
转自:http://blog.leezhong.com/tech/2011/03/21/php-comet.html
- php实现实时通信
- websocket+php实现前后端实时通信
- Php实现实时时间
- PHP实现实时输出
- 使用Oracle实现实时通信
- 实时语音通信的实现
- 使用Oracle实现实时通信
- 实时语音通信的实现
- 实现PHP,.NET, C#, JAVA, Python, Ruby,C(++) 与websocket 实时通信
- PHP+swoole+websocket聊天实时通信
- 实时语音通信的实现(VC++)
- socket简单实现实时通信案例
- 实现远程实时通信 Html5:Canvas+WebSocket
- H5 WebSocket实现简单的实时通信
- 使用微软的实时通信API实现客户端通信
- PHP for 循环如何实现实时输出
- js+php实现实时网页聊天功能
- 网页实时聊天之PHP实现websocket
- jsonData的若干细节问题
- Rtsp交互流程
- 紧急审核。
- delphi读取xml文件
- windows 下的一些基本操作
- php实现实时通信
- Three things you should never put in your database
- x86系统引导(2)
- 苹果公司联系邮箱大全
- 第十二章
- gfgffdsafasdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff反复反复反复反复反复反复反复反复反复反复反复反
- 2012年 上半年 第十三周 C++程序设计 (三十七)
- android优化实战(二)-BigInteger
- 编译器自动优化