node.js chat程序如何实现Ajax long-polling长链接刷新模式

废话不多说,开始今天的主题。纵观这个程序,感觉它的最可贵之处,在于展示了,如何用nodejs实现长链接模式的刷新技术。
  (这个程序不详细介绍,重点讲解这个功能)
Client.js  
首先看一段核心代码:


代码如下:

function longPoll (data) {
//....此处省略**行
$.ajax({ cache: false
, type: "GET"
, url: "/recv"
, dataType: "json"
, data: { since: CONFIG.last_message_time, id: CONFIG.id }
, error: function () {
addMessage("", "long poll error. trying again...", new Date(), "error");
transmission_errors += 1;
//don't flood the servers on error, wait 10 seconds before retrying
setTimeout(longPoll, 10*1000);
}
, success: function (data) {
transmission_errors = 0;
//if everything went well, begin another request immediately
//the server will take a long time to respond
//how long? well, it will wait until there is another message
//and then it will return it to us and close the connection.
//since the connection is closed when we get data, we longPoll again
longPoll(data);
}
});
}

这是client.js中的一段代码,一看这段代码,大家应该立马想到两个字——“递归”。在longPoll方法中,再次调用longPoll方法,典型的递归调用。
  根据这段代码的语义,可以看出,第一次加载时,会调用longPoll方法,异步向"/resv"获取值,如果成功了, 执行success的方法,立即再次调用longPoll方法。如果失败了,执行error函数,隔10秒中再次调用longPoll方法。当然,执行error方法有一定的次数限制,由变量transmission_errorsx控制。
  大家可能会有一个疑问,这样一直递归循环获取数据,服务器会不会有很大的负担?在没有数据可获取的时候,也会一直这样循环吗?当然,答案时否定的!并且,nodejs利用自身的特点,很好的处理了这个问题。接着往下看:
Server.js
现看server中如何回应上面client的调用,核心代码:


代码如下:

fu.get("/recv", function (req, res) {
//对session的验证和更新......
channel.query(since, function (messages) {
if (session) session.poke();
res.simpleJSON(200, { messages: messages, rss: mem.rss });
});
});

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

(0)

相关推荐

  • 深入理解Node.js中通用基础设计模式

    谈到设计模式,你可能会想到 singletons, observers(观察者) 或 factories(工厂方法).本文不并专门探讨他们.只是探讨Node.JS一些基础模式的实现,像依赖注入或中间件. 什么是设计模式? 设计模式是用来解决一般的,普遍发生的问题,且可重复使用的解决方案. Singletons (单例) Singletons模式限制了"类",只有一个实例.在Node.js的创建单例是非常简单的,比如下面这个require. //area.js var PI = Math

  • 剖析Node.js异步编程中的回调与代码设计模式

    NodeJS 最大的卖点--事件机制和异步 IO,对开发者并不是透明的.开发者需要按异步方式编写代码才用得上这个卖点,而这一点也遭到了一些 NodeJS 反对者的抨击.但不管怎样,异步编程确实是 NodeJS 最大的特点,没有掌握异步编程就不能说是真正学会了 NodeJS.本章将介绍与异步编程相关的各种知识. 在代码中,异步编程的直接体现就是回调.异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了.我们首先可以看看以下代码. function heavyCompute(n, callb

  • Node.js中使用事件发射器模式实现事件绑定详解

    在Node里,很多对象都会发射事件.比如,一个TCP服务器,每当有客户端请求连接就会发射"connect"事件,又比如,每当读取一整块数据,文件系统就会发射一个"data"事件.这些对象在Node里被称为事件发射器(event emitter).事件发射器允许程序员订阅他们感兴趣的事件,并将回调函数绑定到相关的事件上,这样每当事件发射器发射事件时回调函数就会被调用.发布/订阅模式非常类似传统的GUI模式,比如按钮被点击时程序就会收到相应的通知.使用这种模式,服务端程

  • node.js chat程序如何实现Ajax long-polling长链接刷新模式

    废话不多说,开始今天的主题.纵观这个程序,感觉它的最可贵之处,在于展示了,如何用nodejs实现长链接模式的刷新技术. (这个程序不详细介绍,重点讲解这个功能) Client.js 首先看一段核心代码: 复制代码 代码如下: function longPoll (data) { //....此处省略**行 $.ajax({ cache: false , type: "GET" , url: "/recv" , dataType: "json" ,

  • 使用npm发布Node.JS程序包教程

    npm是Node.JS的程序包管理器.进行Node.JS开发时,经常使用它安装/卸载程序包.实际上,发布程序包的工作也是由它来完成的. 配置package.json 要打包程序,首先要配好各项设置,这些设置都由程序包根目录下的package.json指定.package.json的内容必须是严格的JSON格式,也就是说: 1.字符串要用双引号括起来,而不能用单引号: 2.属性名一定要加双引号: 3.最后一个属性后千万不要多加一个逗号. 配置对象的属性很多,具体可以参阅这里,这里列一下常用的项目:

  • TypeScript开发Node.js程序的方法

    当我第一次发现 TypeScript 时,就把它用到了自己的 JavaScript 程序中.使用 TypeScript 有很多好处,现在你要让我在用原生 JavaScript 写任何东西的话,需要给我一个令人信服的理由. 在本文中,我将向你展示如何设置一个简单的开发环境,以便使用 TypeScript 编写 Node.js 应用程序. 首先在 TypeScript 中可能有一千种或更多种不同的方法去创建 Node.js 程序.我只是想展示自己喜欢的方式. 另外你可以在此处找到我的入门项目:htt

  • Node.js中环境变量process.env的一些事详解

    前言 最近这两天在和运维GG搞部署项目的事儿.碰到一个问题就是,咱们的dev,uat,product环境的问题. 因为是前后端分离,所以在开发和部署的过程中会有对后端接口的域名的切换问题.折腾了一下午,查询了各种资料这才把这Node环境变量process.env给弄明白. 下面这就做个问题解决的记录.希望能对这个不明白的人有所帮助.话不多说了,来一起看看详细的介绍吧. Node环境变量 首先,咱们在做react.vue的单页应用开发的时候,相信大家对配置文件里的process.env并不眼生.

  • Node.js 的模块知识汇总

    在写 Node.js 应用程序的时候,你确实可以把所有代码放在巨大原 index.js 文件中,不在乎你的应用程序会变得多大多复杂.Node.js 解释器不会在意这个事情.但在代码组织方面,你很快就会陷入混乱,不能理解代码,而且难以调试.所以,作为一个人,你应该关心代码的结构.这就是使用模块的原因. 你可以把 Node.js 模块当作 JavaScript 库 -- 是整体代码中你想放在一起的某个部分(比如,函数集),你会想把这部分代码相对独立于代码库中的其它部分,可以把事情区分清楚. 就们我们

  • 在Ubuntu系统上安装Node.JS的教程

    Node.js 在实时的 Web应用上采用了基于 WebSocket 的推送技术.这意味着什么样的革命性?Well,在经过了20多年的基于无状态的请求-返机制的无状态交互之后,我们终于有了实时的,双向连接的web应用,客户端和服务器端都可以发起通信,能够自由地交换数据.与此形成鲜明对比的是传统的 web响应模式,客户端总是主动发起通信而服务端被动返回.此外,这些都是基于运行在标准80端口上的开放Web组件(HTML.CSS和JS). 可能有人会说,我们已经使用 Flash 和 Java Appl

  • 我的Node.js学习之路(一)

    一,node.js介绍   这些网上一大堆信息,我只精简 1,什么是node.js 从核心上说:Node.js是个事件驱动的服务器端javascript环境,也就是说,我们可以像使用PHP,Ruby和Python语言那样,使用javascript创建服务器端的应用程序.对于网络以及创建与网络交互的软件,它尤为专注. 2,使用Node.js能做什么 它既可以创建对文件系统进行操作的小段脚本,也可以创建大规模的Web应用程序来运行整个业务.由于Node.js的独特设计,他非常适合于多人游戏,实时系统

  • node.js不得不说的12点内容

    1.node.js,服务器端的javascript,它允许在后端(脱离浏览器环境)运行javascript代码. 2.事件驱动.异步式I/O的编程模式(单线程)是其核心. 3.node.js的javascript引擎是v8,来自google chrome项目.V8号称是目前世界上最快的javascript引擎. 4.node.js内建了http服务器支持,也就是说你可以轻而易举地实现一个网站和服务器的组合. 5.commonnjs试图定义一套普通应用程序使用的api,从而填补javascript

  • 在Node.js中使用HTTP上传文件的方法

    开发环境 我们将使用 Visual Studio Express 2013 for Web 作为开发环境, 不过它还不能被用来做 Node.js 开发.为此我们需要安装 Node.js Tools for Visual Studio.  装好后 Visual Studio Express 2013 for Web 就会转变成一个 Node.js IDE 环境,提供创建这个应用所需要的所有东西..而基于这里提供的指导,我们需要: 下载安装 Node.js  Windows 版,选择适用你系统平台的

  • 10个最优秀的Node.js MVC框架

    Node.js 是最流行的 JavaScript 服务端平台,它允许建立可扩展的 Web 应用程序.Node.js 包含不同类型的框架,如 MVC 框架.全栈框架.REST API 以及大量的服务器库,使它能够快速构建 Web 服务器,而无需使用外部软件(如 Apache 和 Lighttpd 等).这些框架使得它更加用户友好,易于使用,还支持众多的特性和功能,只要按照几个步骤就可以开发出庞大的 Web 应用程序. 1)Sails js Sails 是一款优秀的框架,可以很容易地开发定制的,企业

随机推荐