实时刷新的论坛,帮忙测试一下,感谢,顺便谈 Ajax 往 WebSocket 的一种快速迁移办法

2012-08-28 14:35:40 +08:00
 cheshirecat
纯测试。不要注册用户,进去直接说话。左边的主题列表实时刷新。

http://geekav.com

支持 Chrome / Firefox / Safari。

=======================================

先看 Ajax,假设我们会用 $.post 某字串 s 给 a.php,然后回调某函数 f ( )。

换成 WebSocket 后,你先需要在服务器上运行一个独立的 WS server,假设开在 8080 端口上。

这个 WS server 可以用任何办法做,node.js 之类都可以,我比较怕麻烦就还是用 php,可以用 Ratchet 给 php 加 WS 支持。

未来所有 client 也连接到这个 WS server 上,然后可以方便地做双向通讯和广播。例如 client 端代码:

$socket = new WebSocket('ws://xxx.xxx.xxx.xxx:8080');
$socket.onerror = function(e) {
console.log('WebSocket error: ' + e.data);
};
$socket.onopen = function(e) {
};
$socket.onmessage = function(e) {
$socket.send('client received the msg');
console.log(e.data);
};

server 端代码,假设名字是 ws.php:

class WS_SERVER implements MessageComponentInterface {
protected $clients;

public function __construct() {
$this->clients = new \SplObjectStorage;
}

public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
$conn->send('hello');
}

public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
}

public function onError(ConnectionInterface $conn, \Exception $e) {
$conn->close();
}

public function onMessage(ConnectionInterface $conn, $msg) {
echo $msg;
}
}

$server = IoServer::factory(new WsServer(new WS_SERVER()), 8080);
$server->run();

然后在 server 运行 php ws.php。

这里有一个问题,原始的 php 代码怎么和这个 ws.php 通讯?例如我们怎么知道应该在什么时候把什么信息广播给所有 client?

这个可以在服务器本机自连接一下,未来 scaling 可能也更好做。

客户 == [Ajax] ==> a.php == [WS] ==> ws.php == [WS] ==> 客户

或者也可以更彻底一点,把 Ajax 调用和所有逻辑都改成走 WS。

客户 == [WS] ==> ws.php == [WS] ==> 客户

还有一个问题是传 session。这个想了想可以用 ws 的路径解决。client 这样连接:$socket = new WebSocket('ws://xxx.xxx.xxx.xxx:8080/' + session_id()); 这里 session_id() 函数从 document.cookie 里面读出来 session id。

然后 server 用 $mcache->get('memc.sess.key.'.substr($conn->WebSocket->request->getPath(), 1)) 就可以从 memcached 里面把对应的 session 数据读出来,再自己 parse 成 array。如果是用数据库存 session 可以照样做。

下面看转换 Ajax 调用。client 上写三个全局变量:var $socket; var $socket_uid = 0; var $socket_callback = new Array();

然后 client 上写一个

function ws_post(post_msg, post_callback) {
post_msg.id = $socket_uid;
$socket.send(JSON.stringify(post_msg));
$socket_callback[$socket_uid] = function(msg) {post_callback(msg)};
$socket_uid++;
}

然后 server 上就可以处理并且把正确的东西返回去,并且同时通知所有 client:

public function onMessage(ConnectionInterface $conn, $msg) {
$msg = json_decode($msg, true);
$conn->send($msg['id'].','.process($conn, $msg));
foreach ($this->clients as $client) {
if ($client != $conn) // 这个信息发给其他用户
$client->send('-1,'.$conn->remoteAddress);
}
}

然后 client 上可以正确地调用回调函数:

$socket.onmessage = function(e) {
var x = e.data.indexOf(',');
var n = e.data.substr(0, x);
var d = e.data.substr(x+1);
if (n == '-1')
console.log('a guy from ' + d + ' sent a msg to server');
else
$socket_callback[n](d);
};

嗯,就是这样了。
4745 次点击
所在节点    分享创造
3 条回复
cheshirecat
2012-08-28 14:40:49 +08:00
不行。时不时就 Error while sending QUERY packet 然后奔溃了... debug 中。
linuz
2012-08-29 21:52:32 +08:00
403 Forbidden
forest520
2012-09-10 17:02:26 +08:00
meteor是不是就是干这个的?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/46327

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX