V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
cheshirecat
V2EX  ›  分享创造

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

  •  
  •   cheshirecat ·
    cheshirecats · 2012-08-28 14:35:40 +08:00 · 4697 次点击
    这是一个创建于 4507 天前的主题,其中的信息可能已经有所发展或是发生改变。
    纯测试。不要注册用户,进去直接说话。左边的主题列表实时刷新。

    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);
    };

    嗯,就是这样了。
    3 条回复    1970-01-01 08:00:00 +08:00
    cheshirecat
        1
    cheshirecat  
    OP
       2012-08-28 14:40:49 +08:00
    不行。时不时就 Error while sending QUERY packet 然后奔溃了... debug 中。
    linuz
        2
    linuz  
       2012-08-29 21:52:32 +08:00
    403 Forbidden
    forest520
        3
    forest520  
       2012-09-10 17:02:26 +08:00
    meteor是不是就是干这个的?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1725 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 16:30 · PVG 00:30 · LAX 08:30 · JFK 11:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.