zoukankan      html  css  js  c++  java
  • 看看node.js chat程序如何实现Ajax longpolling长链接刷新模式

    前言  

      首先,关于node.js,这里就不再细说了。

      node.js chat是node.js作者用JS写的一个多人聊天工具, 源代码公开下载,网址是chat.nodejs.org。作者用这个小例子,来展示如何用nodejs开发高效率的应用程序。对于nodejs的学习者来说,是一个很好的例子。

      废话不多说,开始今天的主题。纵观这个程序,感觉它的最可贵之处,在于展示了,如何用nodejs实现长链接模式的刷新技术。

      (这个程序不详细介绍,重点讲解这个功能)

    Client.js  

    首先看一段核心代码:

     1 function longPoll (data) {
    2
    3 //....此处省略**行
    4
    5 $.ajax({ cache: false
    6 , type: "GET"
    7 , url: "/recv"
    8 , dataType: "json"
    9 , data: { since: CONFIG.last_message_time, id: CONFIG.id }
    10 , error: function () {
    11 addMessage("", "long poll error. trying again...", new Date(), "error");
    12 transmission_errors += 1;
    13 //don't flood the servers on error, wait 10 seconds before retrying
    14 setTimeout(longPoll, 10*1000);
    15 }
    16 , success: function (data) {
    17 transmission_errors = 0;
    18 //if everything went well, begin another request immediately
    19 //the server will take a long time to respond
    20 //how long? well, it will wait until there is another message
    21 //and then it will return it to us and close the connection.
    22 //since the connection is closed when we get data, we longPoll again
    23 longPoll(data);
    24 }
    25 });
    26 }

      这是client.js中的一段代码,一看这段代码,大家应该立马想到两个字——“递归”。在longPoll方法中,再次调用longPoll方法,典型的递归调用。

      根据这段代码的语义,可以看出,第一次加载时,会调用longPoll方法,异步向"/resv"获取值,如果成功了, 执行success的方法,立即再次调用longPoll方法。如果失败了,执行error函数,隔10秒中再次调用longPoll方法。当然,执行error方法有一定的次数限制,由变量transmission_errorsx控制。

      大家可能会有一个疑问,这样一直递归循环获取数据,服务器会不会有很大的负担?在没有数据可获取的时候,也会一直这样循环吗?当然,答案时否定的!并且,nodejs利用自身的特点,很好的处理了这个问题。接着往下看:

    Server.js

    现看server中如何回应上面client的调用,核心代码:

    1 fu.get("/recv", function (req, res) {
    2
    3 //对session的验证和更新......
    4
    5 channel.query(since, function (messages) {
    6 if (session) session.poke();
    7 res.simpleJSON(200, { messages: messages, rss: mem.rss });
    8 });
    9 });

      先不要管这个fu.get()是什么意思,它和本次教程无关。总之知道它能回应client的调用就行了。上面的代码,除了对session的一些操作之外,只是调用了channel的query方法。注意传递的参数:

    • since,它纪录了一个时间;
    • 匿名方法,它接受一个messages参数,两个动作:1 更新session时间,2 返回一个json,即把messages返回给客户端。

      有人可能会有疑问:在这里直接返回messages不行吗,干嘛还得在一个channel中定义一个方法才操作?答案:如果是那样,就成了一个死循环,server和client每时每刻都进行着数据交互,即使没有信息可返回。

      还是接着往下看吧!

      看channel是怎么定义的:

    var MESSAGE_BACKLOG = 200,
    SESSION_TIMEOUT = 60 * 1000;

    var channel = new function () {
    var messages = [],
    callbacks = [];

    this.appendMessage = function (nick, type, text) {
    var m = { nick: nick
    , type: type // "msg", "join", "part"
    , text: text
    , timestamp: (new Date()).getTime()
    };

    switch (type) {
    case "msg":
    sys.puts("<" + nick + "> " + text);
    break;
    case "join":
    sys.puts(nick + " join");
    break;
    case "part":
    sys.puts(nick + " part");
    break;
    }

    messages.push( m );

    while (callbacks.length > 0) {
    //shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
    callbacks.shift().callback([m]);
    }

    while (messages.length > MESSAGE_BACKLOG)
    messages.shift();
    };

    this.query = function (since, callback) {
    var matching = [];
    for (var i = 0; i < messages.length; i++) {
    var message = messages[i];
    if (message.timestamp > since)
    matching.push(message)
    }

    if (matching.length != 0) {
    callback(matching);
    } else {
    callbacks.push({ timestamp: new Date(), callback: callback });
    }
    };

    // clear old callbacks
    // they can hang around for at most 30 seconds.
    setInterval(function () {
    var now = new Date();
    while (callbacks.length > 0 && now - callbacks[0].timestamp > 30*1000) {
    callbacks.shift().callback([]);
    }
    }, 3000);
    };

      channel中定义了两个变量,两个方法,还有一个每隔3秒执行一次的setInterval函数。

      首先看query方法,

      query方法接收两个参数:

    • since:纪录一个时间
    • callback:即上面讲调用channel.query方法时传入的那个匿名函数(JS中,函数可以当参数传递,接收之后可直接调用。不会赶快补课啊。。。)

      messages里存的时当前的聊天纪录队列,query方法会查找符合条件的聊天纪录,把他们放在matching队列中。如果matching.length>0,则调用callback接收的函数,即把matching以json格式返回client。但是。。。接下来是重点!!!

    if (matching.length != 0) {
    callback(matching);
    } else {
    callbacks.push({ timestamp: new Date(), callback: callback });
    }

      如果matching.length<=0,程序会把callback和当前时间,以json格式,存入一个callbacks队列中。对!不执行了,因为没有符合条件的聊天消息,不需要在client显示,所以先把这个函数存起来,先不执行。

      那也不能这样一直存着啊,万一下一秒有人发聊天消息怎么办?

      接着看appendMessage(添加聊天消息)方法:

      该方法中,前段部分很好理解,无非是接收传入的参数,组合成一个m集合,然后用sys.puts在终端显示一下,再把m插入到messages聊天消息队列中。接下来又是重点:

    while (callbacks.length > 0) {
    //shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
    callbacks.shift().callback([m]);
    }

      现在要判断callbacks有没有储存,如果有,就执行一个,删除一个,知道执行完了为止。因为之前在没有聊天消息可返回的时候,有人发出了请求,然后系统没有执行这些请求,都把他们放在callbacks列表中了。

      现在有人发送了聊天消息,执行添加方法的时候,要再次把那些没执行的请求都执行一遍。

      通俗理解可以是:你给我发送请求了,我现在还没有新消息可以答复你,一旦有人发了新消息,我会立刻答复你。

      不知道大家理解了没。。。

      这一步完了,最后一步,是每隔3秒钟,清空过期的callbacks,是一个setInterval函数,这个不难理解。

    总结

      nodejs用自身基于事件驱动的语言特点,实现了长链接刷新功能,让我们打开眼界。自我感觉受益匪浅。特此花时间写个教程跟大家分享,同时也加深我自己的理解。

    当前标签: nodejs

     
    看看node.js chat程序如何实现Ajax long-polling长链接刷新模式 王福朋 2012-03-13 22:00 阅读:874 评论:4  
     
    不完全接触Node.js 王福朋 2012-03-01 21:05 阅读:1811 评论:3  
  • 相关阅读:
    【C语言】C语言static和extern区别
    【C语言】C语言外部变量和内部变量
    【C语言】C语言局部变量和全局变量
    【C语言】C语言常量和变量
    【C语言】C语言数据类型
    【C语言】C语言标识符
    【C语言】C语言关键字
    【C语言】外部函数和内部函数
    【C语言】C语言函数
    Android 测试 Appium、Robotium、monkey等框架或者工具对比
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2399766.html
Copyright © 2011-2022 走看看