Node.js + Web Socket 打造即时聊天程序

​ 计算机网络技术

​ 学生 李聚


摘要:


JavaScript自2009年诞生以来,一直是前端工程师的专利,其前景获得了技术社区的肯定。JavaScript以独特的事件驱动、异步编程等特性被授予“为网络服务而设计”的美誉。聊天室是基于Node.js 的事件驱动及异步编程原理,就一个基于多人连接事件的聊天室的实例结合HTML5中WebSocket技术来展示JavaScript技术在后端服务器领域的前景。

网络聊天室是一个应用广泛、并且实用性强的应用,虽然它的功能简单,但是基于B\S架构的这个应用可以在开发过程中充分学习并应用网络服务器端开发的知识,如:Web各界面使用的Web前端技术,实现网络功能的Socket、Socket Server 技术,实现数据保存的数据连接和操作技术,并且在开发中能够充分的使用和体会JavaScript事件驱动、异步编程的语言特性。


关键词 : JavaScript 服务器编程 Node.js 异步编程 WebSocket


Abstract:

Javascript has been patented by front-end engineers since it was born in 2009, and its future is recognized by the technology community. avascript is honored as " designed for web services" for its unique event - driven, asynchronous programming features. Based on the event-driven and asynchronous programming principle of node. js, a chat room based on multi-connection event is illustrated with web socket technology in html 5 to show the prospect of JavaScript technology in the field of back-end server.

Network chat room is a widely used and practical application, although its function is simple, but the application based on b \ s architecture can fully learn and apply the knowledge of network server-side development in the development process, such as: the web front-end technology used by each interface of the web, socket, socket server technology to realize the network function, data connection and operation technology of data storage, and can fully use and experience JavaScript event-driven and asynchronous programming language characteristics in development.

Keywords: Javascript server programming node. js asynchronous programming web socket


目录:

一、绪论

​ 1.1聊天室背景

1.2课题意义

二、Node.js

​ 2.1 什么是Node.js?

​ 2.2 Node.js的特点

​ 2.3 Node.js发展史

​ 2.4 Node.js应用案例

三、基于Node.js的聊天室设计

​ 3.1 聊天功能

​ 3.1.1功能描述

​ 3.1.2界面布局设计

​ 3.1.3消息推送远离

​ 3.2程序需求和初始设计

​ 3.2.1 提供HTTP和WebSocket服务

​ 3.2.2 创建程序的文件结构

​ 3.2.3 指明依赖项

​ 3.2.4 安装依赖项

​ 3.3 提供HTNL、CSS和客户端JavaScript的服务

​ 3.3.1 创建静态文件服务器

​ 3.3.2 添加HTML和CSS文件

四.基于Node.js + Web Socket的聊天系统实现

​ 4.1客户端模块

​ 4.1.1登陆

​ 4.1.2注册

​ 4.1.3聊天

​ 4.1.4聊天表情

五、基于Node.js + Web Socket的聊天系统测试


一、绪论

​ Html5技术提升了浏览器做富客户端开发的能力,这种提升不是量的变化而是质的变化,html5让web前端在整个web应用里的作用提升到了前所未有的高度,富客户端将会更加富有。

​ 浏览器端JavaScript在Web应用中盛行,甚至让人们忘掉了JavaScript可以在服务器端运行这码事。但是,服务器端JavaScript现在回来了,因为Node诞生了。Node的诞生离不开上述的历史契机,服务器端JavaScript在漫长的历史中长期停滞留下空白,但Node重新将这个领域激活。Ryan Dahl基于对高性能Web服务器的探索,无意间促成了服务器端JavaScript领域的焕然一新。Node凭借V8的高性能和异步I/O模型将JavaScript重新推向了一个高潮。现在,Node不仅满足JavaScript同时运行在前后端,而且性能还十分高效。与传统印象中的不同,它甚至可比于当前的高效脚本语言。

1.1聊天室背景

​ 我们进入了一个信息高速发展的时代,人与人之间不再只依赖于面对面,或者依靠电话的联系,信息的快速传递不仅能够节省电话产生的费用,而且还有助于人们保持固定长远的联系。目前,我们常用的聊天软件有QQ,微信,微博等。

在移动互联网时代,面对层出不穷的软件开发,我们需要基于node.js开发出新型的聊天软件,来满足大众的需求,我们都熟悉且常用的移动设备终端操作系统有ios,android,symbian等等,最普及的是Android。

本课题采用聊天软件的C/S架构:采用客户端/服务器形式,用户使用过程中需要下载安装客户端软件,典型的代表有:QQ、微信 、微博 、YY、MSN等[1]。

1.2 课题意义

​ 在互联网飞速发展的今天,“网虫”在互联网上聊天已经成为一种习惯,IM程序设计就是一个多点通信程序,C/S体系架构在软件开发中越来越流行,应用软件使用这种架构可以充分发挥数据库系统优势,速度更快,从用户体验的角度来更好的满足人们对聊天软件的需求,有一个前提,频宽充足,大部分IM服务事实上提供实时通讯的能力。实时传讯与电子邮件最大的不同在于不用等候,不需要每隔两分钟就按一次“传送与接收”[2],只要两个人都同时在线,就能像多媒体电话一样,传送文字、档案、声音、影像给对方,只要有网络,无论对方隔得多远都没有距离。


2.1 什么是Node.js?

  • Node.js是一个基于Chrome的V8引擎的Javascript运行环境

我们使用 Js写的Node代码,就本质而言,只是单纯的字符串而已,不仅是node,其他语言也是这样.计算机并不能直接的运行。、

而如果想要执行这些代码,就需要有一个解析执行环境,就Node而言, 这个环境就是Chrome的V8引擎。


  • Node.js使用了一个事件驱动,非阻塞式I/O的模型,使其轻量又高效

  • 事件驱动:当触发某个指定的事件时,才会执行其指定的代码,否则永远不会执行。

  • 非阻塞式I/O的模型:采用非阻塞的操作,可以有效的提高代码的执行效率。

  • Node.js的包管理器npm,是全球最大的开源生态系统。

  • npm的使用流程:
  1. 使用npm init -y初始化一下把管理文件package.json,将来所有安装的包都会记录到package.json文件当中去.
  2. 使用npm install [包名] --save/--save-dev安装你所需要的包.其中,install可以简写成i.
  3. --save表示把包安装到部署依赖当中(在开发和部署上线都需要使用的包).--save-dev表示把包安装到开发依赖当中(只有在开发时才会使用到的包).
  4. --save可以简写成-S.--save-dev可以简写成-D.
  5. 使用npm uninstall [包名] --save/--save-devnpm remove [包名] --save/--save-dev即可卸载指定的包.
  6. 使用npm i [包名] -g即可将包安装到全局环境中.这样在任何地方都可以使用.


2.2 Node.js的特点

​ Node.js的性能很好,按照创始人Ryan DahI的说法, 性能上Node.js考虑的重要因素,选择C++和V8而不是Ruby或者其他虚拟机也是基于性能的目的。Node.js在设计上也是比较大胆, 它以单进程、单线程 模式运行(和Javascript的运行方式一致),事件驱动制是Node.js通过内部单线程高效率地维护事件循环队列实现的,没有多线程的资源占用和上下文切换,这意味着面对大规模的http 请求,Node.js凭借事件驱动搞定一切,习惯了传统语言的网络服务开发人员可能对多线程并发和协作非常熟悉, 但是面对Node.js,我们需要接受和理解它的特点。 由此我们是否可以推测出这样的设计会导致负载的压力集中在CPU(事件循环处理)而不是内存。

Node.js的 性能测试:

  • 物理机配置:RHEL 5.2、CPU 2.2GHz、内存4G
  • Node.js应用场景:MemCache代理,每次取100字节数据
  • 连接池大小:50
  • 并发用户数:100
  • 测试结果(socket模式):内存(30M)、QPS(16700)、CPU(95%)


从上面的结果, 我们可以看到在这样的测试场景下, qps能够达到16700次, 内存仅占用20M(其中V8堆占用22M),CPU则达到95%, 可能成为瓶颈。 此外,还有不少实践者对 Node.js做了性能分析,总的来说,它的性能让人信服, 也是受欢迎的重要原因。 既然Node.js采用单进程、单线程模式, 那么在如今多核硬件流行的环境中,,单核性能出色的Node.js如何利用多核CPU呢?创始人Ryan Dahl建议,运行多个Node.js进程,利用某些通信机制来协调各项任务。目前,已经有不少第三方的Node.js多进程支持模块发布。

Node.js的另一个特点是它支持的编程语言是Javascript。关于动态语言和静态语言的优缺点比较在这里只说三点:


var hostRequest = http.request(requestOptions,function(response) {
    var responseHTML ='';
    response.on('data', function (chunk) {
        responseHTML = responseHTML + chunk;
    });
    response.on('end',function(){
        console.log(responseHTML);
        // do something useful
   });
});



在上面的代码中,我们需要在end事件中处理responseHTML变量,由于Javascript的闭包特性,我们可以在两个回调函数之外定义responseHTML变量,然后在data事件对应的回调函数中不断修改其值,并最终在end事件中访问处理。

  1. Javascript作为前端工程师的主力语言,在技术社区中有相当的号召力。而且,随着Web技术的不断发展,特别是前端的重要性增加,不少前端工程师开始试水”后台应用“,在许多采用Node.js的企业中,工程师都表示因为习惯了Javascript,所以选择Node.js。
  2. Javascript的匿名函数和闭包特性非常适合事件驱动、异步编程,从helloworld例子中我们可以看到回调函数采用了匿名函数的形式来实现,很方便。闭包的作用则更大,看下面的代码示例:
  3. Javascript在动态语言中性能较好,有开发人员对Javacript、Python、Ruby等动态语言做了性能分析,发现Javascript的性能要好于其他语言,再加上V8引擎也是同类的佼佼者,所以Node.js的性能也受益其中。


2.3 Node.js 发展简史

​ 2009年2月,Ryan Dahl在博客上宣布准备基于V8创建一个轻量级的Web服务器并提供一套库。

​ 2009年5月,Ryan Dahl在GitHub上发布了最初版本的部分Node.js包,随后几个月里,有人开始使用Node.js开发应用。

​ 2009年11月和2010年4月,两届JSConf大会都安排了Node.js的讲座。

​ 2010年年底,Node.js获得云计算服务商Joyent资助,创始人Ryan Dahl加入Joyent全职负责Node.js的发展。

​ 2011年7月,Node.js在微软的支持下发布Windows版本。


2.4 Node.js应用案例

虽然Node.js诞生刚刚两年多,但是其发展势头逐渐赶超Ruby/Rails,在社交网站LinkedIn最新发布的移动应用中,NodeJS是该移动应用的后台基础。LinkedIn移动开发主管Kiran Prasad对媒体表示,其整个移动软件平台都由NodeJS构建而成:

LinkedIn内部使用了大量的技术,但是在移动服务器这一块,我们完全基于Node。

(使用它的原因)第一,是因为其灵活性。第二,如果你了解Node,就会发现它最擅长的事情是与其他服务通信。移动应用必须与我们的平台API和数据库交互。我们没有做太多数据分析。相比之前采用的Ruby on Rails技术,开发团队发现Node在性能方面提高很多。他们在每台物理机上跑了15个虚拟服务器(15个实例),其中4个实例即可处理双倍流量。容量评估基于负载测试的结果。


企业社会化服务网站Yammer则利用Node创建了针对其自身平台的跨域代理服务器,第三方的开发人员可以通过该服务器实现从自身域托管的Javascript代码与Yammer平台API的AJAX通信。Yammer平台技术主管Jim Patterson对Node的优点和缺点提出了自己的看法

优点)因为Node是基于事件驱动和无阻塞的,所以非常适合处理并发请求,因此构建在Node上的代理服务器相比其他技术实现(如Ruby)的服务器表现要好得多。此外,与Node代理服务器交互的客户端代码是由javascript语言编写的,因此客户端和服务器端都用同一种语言编写,这是非常美妙的事情。

(缺点)Node是一个相对新的开源项目,所以不太稳定,它总是一直在变,而且缺少足够多的第三方库支持。看起来,就像是Ruby/Rails当年的样子。


GitHub也尝试了Node应用。该Node应用称为NodeLoad,是一个存档下载服务器(每当你下载某个存储分支的tarball或者zip文件时就会用到它)。GitHub之前的存档下载服务器采用Ruby编写。在旧系统中,下载存档的请求会创建一个Resque任务。该任务实际上在存档服务器上运行一个git archive命令,从某个文件服务器中取出数据。然后,初始的请求分配给你一个小型Ruby Sinatra应用等待该任务。它其实只是在检查memcache flag是否存在,然后再重定向到最终的下载地址上。旧系统运行大约3个Sinatra实例和3个Resque worker。GitHub的开发人员觉得这是Node应用的好机会。Node基于事件驱动,相比Ruby的阻塞模型,Node能够更好地处理git存档。在编写新下载服务器过程中,开发人员觉得Node非常适合该功能,此外,他们还里利用了Node库socket.io来监控下载状态。

不仅在国外,Node的优点也同样吸引了国内开发人员的注意,淘宝就实际应用了Node技术:

MyFOX 是一个数据处理中间件,负责从一个MySQL集群中提取数据、计算并输出统计结果。用户提交一段SQL语句,MyFOX根据该SQL命令的语义,生成各个数据库分片所需要执行的查询语句,并发送至各个分片,再将结果进行汇总和计算。 MyFOX的特点是CPU密集,无文件IO,并只处理只读数据。起初MyFOX使用PHP编写,但遇到许多问题。例如PHP是单线程的,MySQL又需要阻塞查询,因此很难并发请求数据,后来的解决方案是使用nginx和dirzzle,并基于HTTP协议实现接口,并通过curl_multi_get命 令进行请求。不过MyFOX项目组最终还是决定使用Node.js来实现MyFOX。

选择Node.js有许多方面的原因,比如考虑了兴趣及社区发展,同时也希望可以提高并发能力,榨干CPU。例如,频繁地打开和关闭连接会让大量端口处于等待状态,当并发数量上去之后,时常会因为端口不够用(处于TIME_WAIT状态)而导致连接失败。之前往往是通过修改系统设置来减少等待时间以绕开这个错误,然而使用连接池便可以很好地解决这个问题。此外,以前MyFOX会在某些缓存失效的情况下出现十分密集的访问压力,使用 Node.js便可以共享查询状态,让某些请求“等待片刻”,以便系统重新填充缓存内容。


3.基于Node.js的聊天室设计

3.1聊天功能

3.1.1功能描述

​ 支持表情发送

​ 支持键盘回车发送信息

​ 支持在线用户统计和用户列表

​ 支持在线用户搜索

​ 支持在线用户的私聊

​ 支持消息声音提示


3.1.2界面布局设计

​ 界面布局是一个活动条目中对于用户界面的架构设计,它通过存储全部显示给用户的元素来定义界面布局结构。客户端的用户界面布局文件存放/res/layout 中, 用XML 格式的布局文件定义应用程序默认的布局,包括将显示在屏幕的元素和属性。可以通过编程修改客户端界面上对象的状态,这种布局设计是非常重要的,其中也包括了系统定义在XML文件中的元素。


3.2 程序需求及初始设置

将要创建的聊天程序需要完成如下任务:

​ 提供静态文件 (比如HTML、CSS和客户端JavaScript)

​ 在服务器上处理与聊天相关的消息;

​ 在用户的浏览器中处理与聊天相关的消息

为了提供静态文件,需要使用Node内置的http模块。但通过HTTP提供文件时,通常不能只是发送文件中的内容,还应该有所发文件的类型。也就是说用正确的MIME类型设置HTTP头的Content-Type。 为了查找这些MIME类型。会用到第三方的模块mime。

​ MIME类型 MIME类型在维基百科上的文章en.wikipedia.org/wiki/M中有 详细论述。

为了处理与聊天相关的消息,需要用Ajax轮询服务器。但为了让这个程序能尽可能快地做出响应,我们不会用传统的Ajax发送消息。Ajax用HTTP作为传输机制,并且HTTP本来就不是做实时通信的。在用HTTP发送消息时,必须用一个新的TCP/IP连接。打开和关闭连接需要时间。此外,因为每次请求都要发送HTTP头,所以传输的数据量也比较大。这个程序没用依赖于HTTP 7的方案,而是采用了 WebSocket(en.wikipedia.org/wiki/W),这是一个为支持实时通讯而设计的轻量的双向通信协议。

因为在大多数情况下,只有兼容HTML5的浏览器才支持WebSocket,所以这个程序会使用流行的Socket.IO库(socket.io/),它给不能使用WebSocket的浏览器提供了一些后备措施,包括使用Flash。Socket.IO对后备功能的处理是透明的,不需要额外的代码或配置。


3.2.1 提供HTTP和WebSocket服务

尽管这个程序不会用Ajax发送和接收聊天消息, 但它仍要用HTTP发送用在用户浏览器中的HTML、CSS和客户端Javascript。

​ 如下图所示, Node用一个端口就可以轻松地提供HTTP和WebSocket两种服务。Node带有一个可以提供HTTP服务功能的模块。还有一些第三方的Node模块,比如构建在Node内置功能上的Express, 它让Web服务变得更加容易了。



3.2.2 创建程序的文件结构

我们先创建一个项目目录。主程序会直接放在这个目录下。添加一个ImChat子目录,用来放服务端逻辑。还需要创建一个static子目录,用来放客户端文件。

Node对目录结构没有任何特殊要求


现在已经确定了程序的目录结构,接下来该指明它的依赖项了。

程序的依赖项,在这里说指通过安装,来提供程序所需功能的模块。比如正在创建的程序需要访问在MySQL数据库中的数据,可Node中没有可以访问MYSQL的内置模块, 所以只能装一个第三方模块,这个模快就是依赖项。


3.2.3 指明依赖项

尽管不正式指明依赖项也可以创建Node程序,但是我还是使用了,这样,如果其他人用我的程序,或者我计划在多个地方运行它时,设置起来就要简单直接的多。

​ 程序的依赖项是在package.json文件中指明的。这个文件总是被放在程序的根目录下。package.json文件用于描述应用程序,它包含一些Json表达式,并遵循CommonJs包描述标准(wiki.commonjs.org/wiki/)。在package.json文件中可以定义很多事情,但最重要的是程序的名称、版本号、对程序的描述,以及程序的依赖项。



3.2.4 安装依赖项

定义还package.json文件之后,安装程序的依赖项就很简单了。Node包管理器(npm)是Node自带的工具,它有很多功能,可以轻松安装第三方Node模块,可以吧你自己创建的任何Node模块向全球发布。它用一行命令就能从package.json文件中读出依赖项,把他们都装好。

​ 在终端中输入下面这条命令

npm install

​ 在看这个目录,你应该能看到一个新创建的node_modules目录,下图。


​ 目录结构已经确立了,依赖项也安装好了,可以开始填充程序逻辑了。


3.3 提供HTNL、CSS和客户端JavaScript的服务

聊天程序需要三个基本功能:

​ 给用户的Web浏览器提供静态文件;

​ 在服务器端处理与聊天相关的消息;

​ 在用户的Web浏览器中处理与聊天相关的消息。

程序的逻辑是由一些文件实现的, 有些运行在服务器上,有些运行在客户端,下图所示。

在客户端运行的JavaScript需要作为静态资源发给浏览器, 而不是在Node上执行。


​ 这个聊天程序中既有客户端JavaScrip逻辑, 也有服务端JavaScript逻辑


3.3.1 创建静态文件服务器

​ 创建静态文件服务器既要用到Node内置的功能, 也要用第三方的mime附加模块来确定文件的MIME类型。

​ 先从程序的主文件开始, 请在项目根目录下创建server.js文件, 把下图中的变量声明放到这个文件中。有了这些声明, 就可以使用Node中跟HTTP相关的功能、跟文件系统交互的功能, 以及确定文件MIME类型的功能。 变量cache是用来缓存文件中的数据的。



1.发送文件数据及错误响应

​ 接下俩要添加三个辅助函数以提供静态HTTP文件服务。第一个是在所请求的文件不存在时发送404错误的。把下面的辅助函数加到server.js中:


function send404(response) {
  response.writeHead(404, {'Content-Type': 'text/plain')};
  response.end{};
}



​ 第二个辅助函数提供文件数据服务。这个函数先写出正确的HTTP头,然后发送文件内容。添加下面的代码到server.js中:


function sendFile(response, filsePath, fileContents) {
  response.writeHead(
    200,
    {"content-type":mime.lookup{path.basename(filePath)}}
  );
  response.end(fileContents);
}



访问内存( RAM ) 要比访问文件系统快得多, 所以Node程序通常会把常用的数据缓存到内存里。我们的聊天程序就要吧静态文件缓存到内存中,只有第一次访问的时候才会从文件系统中读取。下一个辅助函数会确定文件是否缓存了,如果是,就返回它。如果文件还没被缓存,他会从硬盘中读取并返回它。如果文件不存在,则返回一个HTTP 404错误作为响应。把这个辅助函数加到server.js中:



2.创建HTTP服务器

​ 在创建HTTP服务器时, 需要给createServer传入一个匿名函数作为回调函数,由它来处理每个HTTP请求。这个回调函数接受两个参数:request和response。在这个回调执行时,HTTP服务器会分别组装这两个参数对象,以便我可以对请求的细节进行处理,并返回一个响应。

​ 创建HTTP服务器的逻辑



3.启动HTTP服务器

​ 现在已经写好了创建HTTP服务器的代码,但还没添加启动它的逻辑。添加下面这些代码,它会启动服务器,压迫起服务器监听TCP/IP端口3000。3000是随便选的。 所有1024以上未用的端口应该都可以(如果在Windows上运行,1024以下的端口也行,或者在Linux及OS X中用“ root ”启动程序)。


server.listen(3000, function() {
      console.log("Server listening on port 3000.");
    });



​ 如果你想看看这个程序现在能做什么, 可以在命令行中输入下面这条命令启动服务器:

node server.js

​ 服务器运行起来后,在浏览器中访问127.0.0.1:3000会激发404错误辅助函数,页面上会显示 “Error 404: resource not found。” 消息。尽管你已经添加了静态文件处理逻辑,但还没添加那些静态文件。记住, 在命令行中按下Ctrl-C可以停止正在运行的服务器。


3.3.2 添加HTML和CSS文件

​ 要加的第一个静态文件是默认的HTML文件。在public目录下创建index.html文件,把下图的HTML放进去。这段HTML会引入一个CSS文件,设置一些显示程序内容的div元素,加载一些客户端Javascript文件。这些JavaScript文件提供了客户端Socket.IO功能、jQuery (用来操作DOM),以及两个该程序特有的文件,用来提供聊天功能



​ 下一个要添加的是定义程序页面样式的CSs文件。创建style.css文件,把下面的CSS代码放进去。


现在已经HTML和CSS都好了



​ 做完的聊天程序


4.1客户端模块

登录功能:建立与服务器的连接并登录,能显示登录错误信息。

界面显示:主界面为好友聊天界面,显示了已经参与会话的好友列表,子界面为好友资料以及删除,加入黑名单的好友界面。

聊天功能:与好友聊天时显示在会话界面。只需要在输入框中输入文本信息点击发送就可以与好友通信,还包括位置,图片,语音的发送。

聊天纪录:保存聊天纪录,也可以加载历史记录,当加载到第一次会话,提示“聊天记录已经加载完了哦!”。

信息提示:系统弹出信息提示到来信息,并播放音乐来提示。

后台运行:当用户将客户端进行后台运行时,如果有消息传输过来,系统会直接通过广播的形势显示消息的来源以及内容。


4.1.1 注册/登录界面




关键代码:


var login=Vue.extend({
        template:"#login",
        data:function () {
            var images=[
                'http://q.qlogo.cn/headimg_dl?dst_uin=705597001&spec=100',
                'http://q.qlogo.cn/headimg_dl?dst_uin=956411241&spec=100',
                'http://q.qlogo.cn/headimg_dl?dst_uin=1361514346&spec=100',
                'http://q.qlogo.cn/headimg_dl?dst_uin=624748513&spec=100',
                'http://q.qlogo.cn/headimg_dl?dst_uin=1741841217&spec=100',
                'http://q.qlogo.cn/headimg_dl?dst_uin=157509895&spec=100',
                'http://q.qlogo.cn/headimg_dl?dst_uin=453079985&spec=100',
                'http://q.qlogo.cn/headimg_dl?dst_uin=753678776&spec=100',
            ];
            return {
                avatarUrl:images[0],
                imgList:images,
                name:"",
                isShow:false,
                errorMsg:""
            }
        },
        created:function () {
            var _this=this;
            document.addEventListener("click",function (e) {
                _this.isShow=false;
            })
        },
        methods:{
            userLogin:function () {
                this.name=this.name.replace(/(^\s*)|(\s*$)/g, "");
                if(this.name!=""){
                    this.$emit("user-login",this.avatarUrl)
                }else {
                    this.showError("请输入用户昵称!")
                }
            },
            showError:function (err) {
                var _this=this;
                if(this.interval){
                    clearTimeout(_this.interval)
                }
                this.errorMsg=err;
                this.interval=setTimeout(function () {
                    _this.errorMsg="";
                },3000)
            }
        }
    })



4.1.2聊天界面

​ 在客户端,通过创建Socket 来实现和服务器的连接,通过一个按钮来实现客户端向服务器发送消息,在接通后 ,TextView显示来自服务器的消息。



关键代码:


new Vue({
        el:"#app",
        template:"#tpl",
        components:{
            "ui-message":message,
            "ui-user":user,
            "ui-login":login
        },
        data:function () {
            return {
                user:{
                    id:"705597001",
                    name:"似水流年",
                    avatarUrl:"static/images/img.jpg"
                },
                users:[{
                    id:"group",
                    name:"群聊天室",
                    avatarUrl:"static/images/group-icon.png",
                    messages:[]
                },{
                    id:"a1101",
                    name:"熊本熊",
                    avatarUrl:"static/images/10.jpg",
                    messages:[]
                }],
                channel:"group",
                text:"",
                keywords:"",
                showMenu:false,
                isLogin:true
            }
        },
        created:function () {
            var _this=this;
            document.addEventListener("click",function (e) {
                _this.showMenu=false;
            })
            _this.initBg()
        },
        computed:{
            messageList:function () {
                var msgList=[];
                var _this=this;
                this.users.forEach(function (item) {
                    if(item.id==_this.channel){
                        msgList=item.messages?item.messages:[]
                    }
                })
                return msgList
            },
            channelUser:function (){
                var _this=this;
                var user={};
                this.users.forEach(function (item) {
                    if(item.id==_this.channel){
                        user={
                            id:item.id,
                            name:item.name,
                            avatarUrl:item.avatarUrl
                        };
                    }
                })
                return user;
            }
        },



4.1.4聊天表情




实现代码:


filterData:function (data) {
                switch(data.code) {
                    case 100000://文本类
                        return data.text
                        break;
                    case 200000://链接类
                        return data.text+"<a href='"+data.url+"' class='res-link' target='_blank'>打开页页面</a>"
                        break;
                    case 302000://新闻类
                        var html=data.text+"<ul class='res-list'>";
                        var len=3;
                        if(data.list.length<3){
                            len=data.list.length
                        }
                        for(var i=0;i<len;i++){
                            var item=data.list[i];
                            html+="<li><a href='"+item.detailurl+"' target='_blank'>"+(i+1)+".&nbsp;"+item.article+"</a></li>"
                        }
                        html+='</li>';
                        return html;
                        break;
                    case 308000://菜谱类
                        var html=data.text+"<ul class='res-list'>";
                        var len=3;
                        if(data.list.length<3){
                            len=data.list.length
                        }
                        for(var i=0;i<len;i++){
                            var item=data.list[i];
                            html+="<li><a href='"+item.detailurl+"' target='_blank'>"+item.name+"</a></li>"
                        }
                        html+='</li>';
                        return html;
                        break;
                    default:
                        return data.text
                }
            },
            filterName:function () {
                var arr=[];
                var self=this
                this.users.forEach(function (item ){
                    if(item.name.indexOf(self.keywords)!=-1){
                        arr.push(item)
                    }
                })
                return arr;
            },
            initBg:function () {
                this.$http.jsonp("https://api.asilu.com/bg")
                    .then(function (data) {
                        var images=data.body.images;
                        document.body.style.backgroundImage="url('static/images/a1 (10).jpg')";
                        setInterval(function () {
                            var index=parseInt(Math.random()*images.length);
                            var img=new Image();
                            img.addEventListener('load',function () {
                                document.body.style.backgroundImage="url("+images[index].url+")";
                            })
                            img.src=images[index].url;
                        },30000)
                    })
            },



五、基于Node.js + Web Socket的聊天系统测试

​ 系统测试是应用软件最后的一个过程。应用程序运行时需要多次的测试来发现程序中存在的BUG,从而不断的完善系统,服务更加人性化。经过对系统对服务端和客户端的测试,首先服务器端软件测试,测试服务器端服务是否能开启,服务器端是否能对客户端传来的数据进行正确的解析,客户端应用程序进行测试,移动智能终端设备界面是否能正确显示,数据是否能准确收发,相应客户端功能是否存在不足,最后结合客户端与服务端一起测试,测试客户端是否能与服务端连接,登录,注册,聊天等功能是否正常实现。

编辑于 2018-05-08