socket.io的性能瓶颈 的详细工作流程是怎样的

个人网站 欢迎品尝&
下面页面就是使用制作的口袋妖怪游戏(默认小屏下已隐藏,请切换到大分辨率查看)。左边是游戏画面,右边是按键表和聊天室。画面达到红蓝版本的水平了。
前导 &&WebSocket的介绍
&&传统的Web应用采用的是客户端发出请求、服务器端响应的工作方式。在这种情景下,浏览器作为Web应用的前端,自身的处理功能是十分有限的。这种方法不能满足某些应用的实时需求(服务器需要主动更新浏览器端的数据)。不同于服务器端等待HTTP请求,这需要服务器端主动发送数据以给客户端更新。解决方案有两类:一类是基于HTTP的Comet推送技术,另一类是基于套接口(Socket)传送信息实现消息传输。&&而目前使用Comet主要有两种方式,轮询和iframe流。
轮询&polling&&浏览器周期性的发出请求,如果服务器没有新数据需要发送就返回以空响应。这种方法问题很大:首先,大量无意义的请求造成网络压力;其次,请求周期的限制不能及时地获得最新数据。这种方法很快就被淘汰。
长轮询&long polling&&长轮询是在打开一条连接以后保持连接,等待服务器推送来数据再关闭连接。然后浏览器再发出新的请求,这能更好地管理请求数量,也能及时地更新数据。AJAX调用XMLHttpRequest对象发出HTTP请求,JS响应处理函数根据服务器返回的数据更新HTML页面的展示。这个方法一定程度上消除了简单轮询的弊端,但服务器压力也是很大。
iframe流&iframe streaming&&iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间建立一条长链接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。"iframe是很早就存在的一种 HTML 标记,通过在 HTML 页面里嵌入一个隐蔵帧,然后将这个隐蔵帧的 SRC属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。&其不足为:进度条会显示一直,反应在页面上就是浏览器标签页的图标会不停地转动。(当然这也是有解决方法的)
&&另一类方法则是基于WebSocket&&HTML5提供的Websocket不同于上面这些在老的HTML已有框架内的方法,而是在单个TCP连接上进行全双工通讯的协议。目前主流浏览器都已支持。&&1. 初始化过程&&&不同于早期JAVA使用在浏览器安装插件的方法&&-Java Applet 套接口:这种方法不足在于Java Applet再收到服务器返回的消息后,无法通过Javascript去更新HTML页面的内容。而是通过HTTP建立连接(HTTP handshake)。&&2. 开始通讯&&&一旦初始连接建立,浏览器和服务器就打开了一个TCP socket的频道。在这个频道内就能进行双向的数据通信。
然而Websocket依然有一些问题。比如浏览器兼容性问题(随着浏览器的发展,肯定是越来越小的),以及网络中间物(代理服务、防火墙)问题不支持WebSocket,这时Socket.io的出现就是为了完善WebSocket。
&&Guillermo Rauch在2010年开发第一版时,目的很明确地指向Node.js实时应用。在几次版本更新后,重新定义和封装核心功能而分化出一个基础模块 Engine.io&&力求建立更稳定的工具。Engine.IO有着更稳定的连接质量。使得Socket.IO在先打开一个长轮询,再在将连接推至WebSocket频道继续通信。&&在使用Node的http模块创建服务器同时还要Express应用,因为这个服务器对象需要同时充当Express服务和Socket.io服务。(如下)
var app = require('express')();
&&当客户端需要连接服务器时,它需要先建立一个握手。io.处理连接事件,socket 处理断开连接事件。在上面代码里,这套握手机制是完全自动的,我们可以通过也可以io.use()方法来设置这一过程。&&客户端使用js调用socket.io的Client API即可。
&&Socket.IO还要一些系统事件,包括了连接、重连、关闭的事件。我们也可以自定义事件,以及监听方法。
socket.on('customEvent', function(customEventData) {
&&相应地,在对的时间和地方的调用.emit('customEvent', customEventData); 触发事件就行了。不过,事件是无法在客户端之间发送的。&&同一个服务器可以使用namespaces创造不同的Socket连接。Socket.IO使用of()来指定不同的命名空间。
io.of('/someNamespace').on('connection', function(socket){
socket.on('customEvent', function(customEventData) {
&&服务器端则通过在定义Socket对象时传递namespace参数。
&&在每一个namespace中又可以使用room来进一步划分,不过sockets是使用join()、leave()来调用。
&&下面通过书中的例子来实际操作一下。
配置Socket.io服务器&&首先安装安装Socket.IO、connect-mongo、cookie-parser依赖我们先将依赖报引入,然后定义服务器对象。
var http = require('http');
var socketio = require('socket.io');
var app = express();
var server = http.createServer(app);
var io = socketio.listen(server);
配置Socket.io Session&&为了是Socket.io seesion 和Express session一起工作,我们必须让他们信息共享。Express Session 默认是存储在内存,我们需要把它存在mongoDB以便Socket.io能获取。使用connect-mongo来控制session信息的存储,以及使用以前用到过的cookie-parse来解析session cookie信息。&先来修改express.js文件以便connect-mongo能够正常使用。
var mongoStore = new MongoStore({
db: db.connection.db
&这样Session就存到数据库中来,新建配置文件socketio.js来配置socketio
var config = require('./config'),
cookieParser = require('cookie-parser'),
passport = require('passport');
&cookieParser首先解析Express的Session,然后读取sessionId获得数据库中的session数据,填充到user对象中。如果通过passport来验证用户数据是非法的,则跳出Socket.IO的设置,并发出错误提示。接下来只需要建立Socket.IO的后端控制器即可完成后端的开发。
配置chat控制器&&chat功能的控制器统一监听和触发Socket.IO事件来进行数据通信。通过事件处理的回调函数来控制数据格式的建立和分发。
module.exports = function(io, socket){
&确定监听事件规则后,将控制器载入到Socket.IO的连接事件处理函数中即可。
io.on('connection', function(socket){
console.log('a socket is connected');
require('../app/controllers/chat.server.controller')(io, socket);
Angular前端设计&&我们先通过建立ng-resource来封装Socket.IO的方法,再中前端的控制器中调用。&service是懒加载,即只有在请求时才加载。这可以阻止未验证用户调用到service的方法来获得数据,将emit()、on()、removeListenter()一套方法封装成的更相容的服务方法,减少代码的重写。然而ng的数据绑定只有在框架内执行的方法才能实时改变,也就是说第三方事件导致的数据模型的改变是未知的。那么,我们在socket中任何事件被触发时,处理函数对数据的修改可能不会及时地绑定到$scope数据模型上。(这都是抄来的)这里使用$timeout来强制完成数据的绑定。
angular.module('chat').service('Socket', ['Authentication', '$location', '$timeout', function(Authentication, $location, $timeout){
&接着在前端控制器中调用这些方法来处理后端触发的事件和触发后端能处理的事件。
&将ng引入到对应的视图模板,测试一下即可。
以上就是Socket.IO的上手实战。先了解Socket.IO的工作机制,再将整个数据通信的流程走了一遍,在实践上将Socket.IO与Express、Passport整合到一起完成了Web聊天室的功能,也见识到了Node.JS的小组件大组合的哲学。
References:
阅读(...) 评论()一 websocket
WebSocket是html5新增加的一种通信协议,目前流行的浏览器都支持这个协议,例如 Chrome,Safrie,Firefox,Opera,IE等等,对该协议支持最早的应该是chrome,从chrome12就已经开始支持,随着协 议草案的不断变化,各个浏览器对协议的实现也在不停的更新。该协议还是草案,没有成为标准,不过成为标准应该只是时间问题了。
1. WebSocket API
首先看一段简单的javascript代码,该代码调用了WebSockets的API。
var ws = new WebSocket(“ws:
ws.onopen = function(){ws.send(“Test!”); };
ws.onmessage = function(evt){console.log(evt.data);ws.close();};
ws.onclose = function(evt){console.log(“WebSocketClosed!”);};
ws.onerror = function(evt){console.log(“WebSocketError!”);};
这份代码总共只有5行,现在简单概述一下这5行代码的意义。
第一行代码是在申请一个WebSocket对象,参数是需要连接的服务器端的地址,同http协议使用http://开头一样,WebSocket协议的URL使用ws://开头,另外安全的WebSocket协议使用wss://开头。
第二行到第五行为WebSocket对象注册消息的处理函数,WebSocket对象一共支持四个消息 onopen, onmessage, onclose和onerror,当Browser和WebSocketServer连接成功后,会触发onopen消息;如果连接失败,发送、接收数据 失败或者处理数据出现错误,browser会触发onerror消息;当Browser接收到WebSocketServer发送过来的数据时,就会触发 onmessage消息,参数evt中包含server传输过来的数据;当Browser接收到WebSocketServer端发送的关闭连接请求时, 就会触发onclose消息。我们可以看出所有的操作都是采用消息的方式触发的,这样就不会阻塞UI,使得UI有更快的响应时间,得到更好的用户体验。
2 为什么引入WebSocket协议?
Browser已经支持http协议,为什么还要开发一种新的WebSocket协议呢?我们知道http协议是一种单向的网络协议,在建立连接后,它只 允许Browser/UA(UserAgent)向WebServer发出请求资源后,WebServer才能返回相应的数据。而WebServer不能 主动的推送数据给Browser/UA,当初这么设计http协议也是有原因的,假设WebServer能主动的推送数据给Browser/UA,那 Browser/UA就太容易受到攻击,一些广告商也会主动的把一些广告信息在不经意间强行的传输给客户端,这不能不说是一个灾难。那么单向的http协 议给现在的网站或Web应用程序开发带来了哪些问题呢?
让我们来看一个案例,现在假设我们想开发一个基于Web的应用程序去获取当前Web服务器的实时数据,例如股票的实时行情,火车票的剩余票数等等,这就需 要Browser/UA与WebServer端之间反复的进行http通信,Browser不断的发送Get请求,去获取当前的实时数据。下面介绍几种常 见的方式:
1. Polling
这种方式就是通过Browser/UA定时的向Web服务器发送http的Get请求,服务器收到请求后,就把最新的数据发回给客户端(Browser /UA),Browser/UA得到数据后,就将其显示出来,然后再定期的重复这一过程。虽然这样可以满足需求,但是也仍然存在一些问题,例如在某段时间 内Web服务器端没有更新的数据,但是Browser/UA仍然需要定时的发送Get请求过来询问,那么Web服务器就把以前的老数据再传送过 来,Browser/UA把这些没有变化的数据再显示出来,这样显然既浪费了网络带宽,又浪费了CPU的利用率。如果说把Browser发送Get请求的 周期调大一些,就可以缓解这一问题,但是如果在Web服务器端的数据更新很快时,这样又不能保证Web应用程序获取数据的实时性。
2. Long Polling
上面介绍了Polling遇到的问题,现在介绍一下LongPolling,它是对Polling的一种改进。
Browser/UA发送Get请求到Web服务器,这时Web服务器可以做两件事情,第一,如果服务器端有新的数据需要传送,就立即把数据发回给 Browser/UA,Browser/UA收到数据后,立即再发送Get请求给Web Server;第二,如果服务器端没有新的数据需要发送,这里与Polling方法不同的是,服务器不是立即发送回应给Browser/UA,而是把这个 请求保持住,等待有新的数据到来时,再来响应这个请求;当然了,如果服务器的数据长期没有更新,一段时间后,这个Get请求就会超 时,Browser/UA收到超时消息后,再立即发送一个新的Get请求给服务器。然后依次循环这个过程。
这种方式虽然在某种程度上减小了网络带宽和CPU利用率等问题,但是仍然存在缺陷,例如假设服务器端的数据更新速率较快,服务器在传送一个数据包给 Browser后必须等待Browser的下一个Get请求到来,才能传递第二个更新的数据包给Browser,那么这样的话,Browser显示实时数 据最快的时间为2×RTT(往返时间),另外在网络拥塞的情况下,这个应该是不能让用户接受的。另外,由于http数据包的头部数据量往往很大(通常有 400多个字节),但是真正被服务器需要的数据却很少(有时只有10个字节左右),这样的数据包在网络上周期性的传输,难免对网络带宽是一种浪费。
通过上面的分析可知,要是在Browser能有一种新的网络协议,能支持客户端和服务器端的双向通信,而且协议的头部又不那么庞大就好了。WebSocket就是肩负这样一个使命登上舞台的。
3 websocket协议
WebSocket协议是一种双向通信协议,它建立在TCP之上,同http一样通过TCP来传输数据,但是它和http最大的不同有两 点:1.WebSocket是一种双向通信协议,在建立连接后,WebSocket服务器和Browser/UA都能主动的向对方发送或接收数据,就像 Socket一样,不同的是WebSocket是一种建立在Web基础上的一种简单模拟Socket的协议;2.WebSocket需要通过握手连接,类 似于TCP它也需要客户端和服务器端进行握手连接,连接成功后才能相互通信。
下面是一个简单的建立握手的时序图:
这里简单说明一下WebSocket握手的过程。
当Web应用程序调用new WebSocket(url)接口时,Browser就开始了与地址为url的WebServer建立握手连接的过程。
Browser与WebSocket服务器通过TCP三次握手建立连接,如果这个建立连接失败,那么后面的过程就不会执行,Web应用程序将收到错误消息通知。
在TCP建立连接成功后,Browser/UA通过http协议传送WebSocket支持的版本号,协议的字版本号,原始地址,主机地址等等一些列字段给服务器端。
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==
Origin: http:
Sec-WebSocket-Protocol: chat,superchat
Sec-WebSocket-Version: 13
WebSocket服务器收到Browser/UA发送来的握手请求后,如果数据包数据和格式正确,客户端和服务器端的协议版本号匹配等等,就接受本次握手连接,并给出相应的数据回复,同样回复的数据包也是采用http协议HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Browser收到服务器回复的数据包后,如果数据包内容、格式都没有问题的话,就表 示本次连接成功,触发onopen消息,此时Web开发者就可以在此时通过send接口想服务器发送数据。否则,握手连接失败,Web应用程序会收到 onerror消息,并且能知道连接失败的原因。
4 websocket与TCP,HTTP的关系
WebSocket与http协议一样都是基于TCP的,所以他们都是可靠的协议,Web开发者调用的WebSocket的send函数在browser 的实现中最终都是通过TCP的系统接口进行传输的。WebSocket和Http协议一样都属于应用层的协议,那么他们之间有没有什么关系呢?答案是肯定 的,WebSocket在建立握手连接时,数据是通过http协议传输的,正如我们上一节所看到的“GET/chat HTTP/1.1”,这里面用到的只是http协议一些简单的字段。但是在建立连接之后,真正的数据传输阶段是不需要http协议参与的。
具体关系可以参考下图:
5 websocket server
如果要搭建一个Web服务器,我们会有很多选择,市场上也有很多成熟的产品供我们应用,比如开源的Apache,安装后只需简单的配置(或者默认配置)就可 以工作了。但是如果想搭建一个WebSocket服务器就没有那么轻松了,因为WebSocket是一种新的通信协议,目前还是草案,没有成为标准,市场 上也没有成熟的WebSocket服务器或者Library实现WebSocket协议,我们就必须自己动手写代码去解析和组装WebSocket的数据 包。要这样完成一个WebSocket服务器,估计所有的人都想放弃,幸好的是市场上有几款比较好的开源库供我们使用,比如 PyWebSocket,WebSocket-Node, LibWebSockets等等,这些库文件已经实现了WebSocket数据包的封装和解析,我们可以调用这些接口,这在很大程度上减少了我们的工作 量。如
下面就简单介绍一下这些开源的库文件。
1. PyWebSocket
PyWebSocket采用Python语言编写,可以很好的跨平台,扩展起来也比较简单,目前WebKit采用它搭建WebSocket服务器来做LayoutTest。
我们可以获取源码通过下面的命令
svn checkout/svn/trunk/ pywebsocket-read-only
更多的详细信息可以从获取。
2. WebSocket-Node
WebSocket-Node采用JavaScript语言编写,这个库是建立在nodejs之上的,对于熟悉JavaScript的朋友可参考一下,另外Html5和Web应用程序受欢迎的程度越来越高,nodejs也正受到广泛的关注。
我们可以从下面的连接中获取源码
LibWebSockets
LibWebSockets采用C/C++语言编写,可定制化的力度更大,从TCP监听开始到封包的完成我们都可以参与编程。
我们可以从下面的命令获取源代码
git clone git:///libwebsockets
值得一提的是:websocket是可以和http共用监听端口的,也就是它可以公用端口完成socket任务。
二 Socket.io
node.js提供了高效的服务端运行环境,但是由于浏览器端对HTML5的支持不一,为了兼容所有浏览器,提供卓越的实时的用户体验,并且为程序员提供客户端与服务端一致的编程体验,于是socket.io诞生。Socket.io将Websocket和轮询 (Polling)机制以及其它的实时通信方式封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码。也就是说,Websocket仅仅是 Socket.io实现实时通信的一个子集。那么,Socket.io都实现了Polling中的那些通信机制呢?
Adobe(R) Flash(R) Socket
AJAX long polling
AJAX multipart streaming
Forever Iframe
JSONP Polling
Adobe(R) Flash(R) Socket 大部分PC浏览器都支持的socket模式,不过是通过第三方嵌入到浏览器,不在W3C规范内,所以可能将逐步被淘汰,况且,大部分的手机浏览器都不支持这种模式。
AJAX long polling 这个很好理解,所有浏览器都支持这种方式,就是定时的向服务器发送请求,缺点是会给服务器带来压力并且出现信息更新不及时的现象。
AJAX multipart streaming
这是在XMLHttpRequest对象上使用某些浏览器(比如说Firefox)支持的multi-part标志。Ajax请求被发送给服务器端并保 持打开状态(挂起状态),每次需要向客户端发送信息,就寻找一个挂起的的http请求响应给客户端,并且所有的响应都会通过统一连接来写入
var xhr = $.ajaxSettings.xhr();
xhr.multipart =true;
xhr.open('GET', 'ajax', true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    processEvents($.parseJSON(xhr.responseText));
xhr.send(null);
Forever Iframe (永存的Iframe)技术涉及了一个置于页面中的隐藏Iframe标签,该标签的src属性指向返回服务器端事件的servlet路径。 每次在事件到达时,servlet写入并刷新一个新的script标签,该标签内部带有JavaScript代码,iframe的内容被附加上这一 script标签,标签中的内容就会得到执行。这种方式的缺点是接和数据都是由浏览器通过HTML标签来处理的,因此你没有办法知道连接何时在哪一端已被 断开了,并且Iframe标签在浏览器中将被逐步取消使用。
JSONP Polling
JSONP轮询基本上与HTTP轮询一样,不同之处则是JSONP可以发出跨域请求,详细请搜索查询jsonp的内容。
本文已收录于以下专栏:
相关文章推荐
以下转自标点符 即时通讯之Socket.IO的学习:
Web领域的实时推送技术,也被称作Realtime技术。这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新。它有着广泛的应用场景,比...
votefavorite
I currently have a TCP server application written in .Net th...
Web领域的实时推送技术,也被称作Realtime技术。这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新。它有着广泛的应用场景,比如在线聊天室、在线客服系统、评论系统、WebIM等。
Node.js websocket 使用 socket.io库实现实时聊天室
今天在微博上看到朋友们在讨论关于各种web推送技术的话题,心里一阵唏嘘,触景生情,想想觉得自己还真是与这个叫做“web推送”的技术渊源颇深,有必要就这个话题唠叨两句,来回顾自己的青春,若是这篇乱弹,能...
socket.io基本介绍见我的博客 websocket入门(1)——初识socket.io示例源码见我的github:
/qq/Backgamm...
认识websocketWebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duple)。一开始的握手需要借助HTTP请求完成。其实websock...
Derby是一个新的,成熟的MVC框架,作为Express的中间件使用。
Express.js是使用中间件概念增强应用程序功能的一个流行的node框架。
Racer也支持Derby,他是一个数据同...
nodejs+socket.io实现websocket通信初探
他的最新文章
讲师:刘文志
讲师:陈伟
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)使用Node.js+Socket.IO搭建WebSocket实时应用 - 简书
使用Node.js+Socket.IO搭建WebSocket实时应用
Web领域的实时推送技术,也被称作Realtime技术。这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新。它有着广泛的应用场景,比如在线聊天室、在线客服系统、评论系统、WebIM等。
WebSocket简介
谈到Web实时推送,就不得不说WebSocket。在WebSocket出现之前,很多网站为了实现实时推送技术,通常采用的方案是轮询(Polling)和Comet技术,Comet又可细分为两种实现方式,一种是长轮询机制,一种称为流技术,这两种方式实际上是对轮询技术的改进,这些方案带来很明显的缺点,需要由浏览器对服务器发出HTTP request,大量消耗服务器带宽和资源。面对这种状况,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并实现真正意义上的实时推送。
WebSocket协议本质上是一个基于TCP的协议,它由通信协议和编程API组成,WebSocket能够在浏览器和服务器之间建立双向连接,以基于事件的方式,赋予浏览器实时通信能力。既然是双向通信,就意味着服务器端和客户端可以同时发送并响应请求,而不再像HTTP的请求和响应。
为了建立一个WebSocket连接,客户端浏览器首先要向服务器发起一个HTTP请求,这个请求和通常的HTTP请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表明这是一个申请协议升级的HTTP请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的WebSocket连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。
一个典型WebSocket客户端请求头:
前面讲到WebSocket是HTML5中新增的一种通信协议,这意味着一部分老版本浏览器(主要是IE10以下版本)并不具备这个功能, ,IE8目前仍以33%的市场份额占据榜首,好在chrome浏览器市场份额逐年上升,现在以超过26%的市场份额位居第二,同时微软前不久宣布停止对IE6的技术支持并提示用户更新到新版本浏览器,这个曾经让无数前端工程师为之头疼的浏览器有望退出历史舞台,再加上几乎所有的智能手机浏览器都支持HTML5,所以使得WebSocket的实战意义大增,但是无论如何,我们实际的项目中,仍然要考虑低版本浏览器的兼容方案:在支持WebSocket的浏览器中采用新技术,而在不支持WebSocket的浏览器里启用Comet来接收发送消息。
WebSocket实战
本文将以多人在线聊天应用作为实例场景,我们先来确定这个聊天应用的基本需求。
1、兼容不支持WebSocket的低版本浏览器。2、允许客户端有相同的用户名。3、进入聊天室后可以看到当前在线的用户和在线人数。4、用户上线或退出,所有在线的客户端应该实时更新。5、用户发送消息,所有客户端实时收取。
在实际的开发过程中,为了使用WebSocket接口构建Web应用,我们首先需要构建一个实现了 WebSocket规范的服务端,服务端的实现不受平台和开发语言的限制,只需要遵从WebSocket规范即可,目前已经出现了一些比较成熟的WebSocket服务端实现,比如本文使用的Node.js+Socket.IO。为什么选用这个方案呢?先来简单介绍下他们两。
Node.js采用C++语言编写而成,它不是Javascript应用,而是一个Javascript的运行环境,据Node.js创始人Ryan Dahl回忆,他最初希望采用Ruby来写Node.js,但是后来发现Ruby虚拟机的性能不能满足他的要求,后来他尝试采用V8引擎,所以选择了C++语言。
Node.js支持的系统包括*nux、Windows,这意味着程序员可以编写系统级或者服务器端的Javascript代码,交给Node.js来解释执行。Node.js的Web开发框架Express,可以帮助程序员快速建立web站点,从2009年诞生至今,Node.js的成长的速度有目共睹,其发展前景获得了技术社区的充分肯定。
Socket.IO是一个开源的WebSocket库,它通过Node.js实现WebSocket服务端,同时也提供客户端JS库。Socket.IO支持以事件为基础的实时双向通讯,它可以工作在任何平台、浏览器或移动设备。
Socket.IO支持4种协议:WebSocket、htmlfile、xhr-polling、jsonp-polling,它会自动根据浏览器选择适合的通讯方式,从而让开发者可以聚焦到功能的实现而不是平台的兼容性,同时Socket.IO具有不错的稳定性和性能。
先上演示效果图:
可以查看在线演示。整个开发过程非常简单,下面简单记录了开发步骤:
安装Node.js
根据自己的操作系统,去下载安装即可。如果成功安装。在命令行输入node -v和npm -v应该能看到相应的版本号。
搭建WebSocket服务端
这个环节我们尽可能的考虑真实生产环境,把WebSocket后端服务搭建成一个线上可以用域名访问的服务,如果你是在本地开发环境,可以换成本地ip地址,或者使用一个虚拟域名指向本地ip。
先进入到你的工作目录,比如 /workspace/wwwroot/plhwin/,新建一个名为 package.json的文件,内容如下:
"name": "realtime-server",
"version": "0.0.1",
"description": "my first realtime server",
"dependencies": {}
接下来使用npm命令安装express和socket.io
npm install --save express
npm install --save socket.io
安装成功后,应该可以看到工作目录下生成了一个名为`node_modules`的文件夹,里面分别是`express`和`socket.io`,接下来可以开始编写服务端的代码了,新建一个文件:`index.js`
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.send('&h1&Welcome Realtime Server&/h1&');
http.listen(3000, function(){
console.log('listening on *:3000');
命令行运行node index.js,如果一切顺利,你应该会看到返回的listening on *:3000字样,这说明服务已经成功搭建了。此时浏览器中打开http://localhost:3000应该可以看到正常的欢迎页面。
如果你想要让服务运行在线上服务器,并且可以通过域名访问的话,可以使用Nginx做代理,再nginx.conf中添加如下配置,然后将域名(比如:)解析到服务器IP即可。
location / {
proxy_pass http://127.0.0.1:3000;
完成以上步骤,:3000的后端服务就正常搭建了。
服务端代码实现
前面讲到的index.js运行在服务端,之前的代码只是一个简单的WebServer欢迎内容,让我们把WebSocket服务端完整的实现代码加入进去,整个服务端就可以处理客户端的请求了。完整的index.js代码如下:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.send('&h1&Welcome Realtime Server&/h1&');
//在线用户
var onlineUsers = {};
//当前在线人数
var onlineCount = 0;
io.on('connection', function(socket){
console.log('a user connected');
//监听新用户加入
socket.on('login', function(obj){
//将新加入用户的唯一标识当作socket的名称,后面退出的时候会用到
socket.name = obj.
//检查在线列表,如果不在里面就加入
if(!onlineUsers.hasOwnProperty(obj.userid)) {
onlineUsers[obj.userid] = obj.
//在线人数+1
onlineCount++;
//向所有客户端广播用户加入
io.emit('login', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
console.log(obj.username+'加入了聊天室');
//监听用户退出
socket.on('disconnect', function(){
//将退出的用户从在线列表中删除
if(onlineUsers.hasOwnProperty(socket.name)) {
//退出用户的信息
var obj = {userid:socket.name, username:onlineUsers[socket.name]};
delete onlineUsers[socket.name];
//在线人数-1
onlineCount--;
//向所有客户端广播用户退出
io.emit('logout', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
console.log(obj.username+'退出了聊天室');
//监听用户发布聊天内容
socket.on('message', function(obj){
//向所有客户端广播发布的消息
io.emit('message', obj);
console.log(obj.username+'说:'+obj.content);
http.listen(3000, function(){
console.log('listening on *:3000');
客户端代码实现
进入客户端工作目录/workspace/wwwroot/plhwin//chat,新建一个index.html:
&!DOCTYPE html&
&meta charset="utf-8"&
&meta name="format-detection" content="telephone=no"/&
&meta name="format-detection" content="email=no"/&
&meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport"&
&title&多人聊天室&/title&
&link rel="stylesheet" type="text/css" href="./style.css" /&
&!--[if lt IE 8]&&script src="./json3.min.js"&&/script&&![endif]--&
&script src=":3000/socket.io/socket.io.js"&&/script&
&body&&div id="loginbox"&
&div style="width:260margin:200"&
请先输入你在聊天室的昵称&br/&&br/&
&input type="text" style="width:180" placeholder="请输入用户名" id="username" name="username" /&
&input type="button" style="width:50" value="提交" onclick="CHAT.usernameSubmit();"/&
&/div&&div id="chatbox" style="display:"&
&div style="background:#3d3d3d;height: 28 width: 100%;font-size:12"&
&div style="line-height: 28color:#"&
&span style="text-align:margin-left:10"&Websocket多人聊天室&/span&
&span style="float: margin-right:10"&&span id="showusername"&&/span&| &a href="javascript:;" onclick="CHAT.logout()" style="color:#"&退出&/span&
&div id="doc"&
&div id="chat"&
&div id="message" class="message"&
&div id="onlinecount" style="width:background:#EFEFF4; font-size:12 margin-top:10 margin-left:10 color:#666;"&
&div class="input-box"&
&div class="input"&
&input type="text" maxlength="140" placeholder="请输入聊天内容,按Ctrl提交" id="content" name="content"&
&div class="action"&
&button type="button" id="mjr_send" onclick="CHAT.submit();"&
&/div&&script type="text/javascript" src="./client.js"&&/script&
&/body&&/html&
上面的html内容本身没有什么好说的,我们主要看看里面的4个文件请求:
1、:3000/socket.io/socket.io.js2、style.css3、json3.min.js4、client.js
第1个JS是Socket.IO提供的客户端JS文件,在前面安装服务端的步骤中,当npm安装完socket.io并搭建起WebServer后,这个JS文件就可以正常访问了。
第2个style.css文件没什么好说的,就是样式文件而已。
第3个JS只在IE8以下版本的IE浏览器中加载,目的是让这些低版本的IE浏览器也能处理json,这是一个开源的JS,详见:
第4个client.js是完整的客户端的业务逻辑实现代码,它的内容如下:
(function () {
var d = document,
w = window,
p = parseInt,
dd = d.documentElement,
db = d.body,
dc = d.compatMode == 'CSS1Compat',
dx = dc ? dd: db,
ec = encodeURIC
w.CHAT = {
msgObj:d.getElementById("message"),
screenheight:w.innerHeight ? w.innerHeight : dx.clientHeight,
username:null,
userid:null,
socket:null,
//让浏览器滚动条保持在最低部
scrollToBottom:function(){
w.scrollTo(0, this.msgObj.clientHeight);
//退出,本例只是一个简单的刷新
logout:function(){
//this.socket.disconnect();
location.reload();
//提交聊天消息内容
submit:function(){
var content = d.getElementById("content").
if(content != ''){
var obj = {
userid: this.userid,
username: this.username,
content: content
this.socket.emit('message', obj);
d.getElementById("content").value = '';
genUid:function(){
return new Date().getTime()+""+Math.floor(Math.random()*899+100);
//更新系统消息,本例中在用户加入、退出的时候调用
updateSysMsg:function(o, action){
//当前在线用户列表
var onlineUsers = o.onlineU
//当前在线人数
var onlineCount = o.onlineC
//新加入用户的信息
var user = o.
//更新在线人数
var userhtml = '';
var separator = '';
for(key in onlineUsers) {
if(onlineUsers.hasOwnProperty(key)){
userhtml += separator+onlineUsers[key];
separator = '、';
d.getElementById("onlinecount").innerHTML = '当前共有 '+onlineCount+' 人在线,在线列表:'+
//添加系统消息
var html = '';
html += '';
html += user.
html += (action == 'login') ? ' 加入了聊天室' : ' 退出了聊天室';
html += '';
var section = d.createElement('section');
section.className = 'system J-mjrlinkWrap J-cutMsg';
section.innerHTML =
this.msgObj.appendChild(section);
this.scrollToBottom();
//第一个界面用户提交用户名
usernameSubmit:function(){
var username = d.getElementById("username").
if(username != ""){
d.getElementById("username").value = '';
d.getElementById("loginbox").style.display = 'none';
d.getElementById("chatbox").style.display = 'block';
this.init(username);
init:function(username){
//客户端根据时间和随机数生成uid,这样使得聊天室用户名称可以重复。实际项目中,如果是需要用户登录,那么直接采用用户的uid来做标识就可以
this.userid = this.genUid();
this.username =
d.getElementById("showusername").innerHTML = this.
this.msgObj.style.minHeight = (this.screenheight - db.clientHeight + this.msgObj.clientHeight) + "px";
this.scrollToBottom();
//连接websocket后端服务器
this.socket = io.connect('ws://:3000');
//告诉服务器端有用户登录
this.socket.emit('login', {userid:this.userid, username:this.username});
//监听新用户登录
this.socket.on('login', function(o){
CHAT.updateSysMsg(o, 'login');
//监听用户退出
this.socket.on('logout', function(o){
CHAT.updateSysMsg(o, 'logout');
//监听消息发送
this.socket.on('message', function(obj){
var isme = (obj.userid == CHAT.userid) ? true :
var contentDiv = ''+obj.content+'';
var usernameDiv = ''+obj.username+'';
var section = d.createElement('section');
section.className = 'user';
section.innerHTML = contentDiv + usernameD
section.className = 'service';
section.innerHTML = usernameDiv + contentD
CHAT.msgObj.appendChild(section);
CHAT.scrollToBottom();
//通过“回车”提交用户名
d.getElementById("username").onkeydown = function(e) {
if (e.keyCode === 13) {
CHAT.usernameSubmit();
//通过“回车”提交信息
d.getElementById("content").onkeydown = function(e) {
if (e.keyCode === 13) {
CHAT.submit();
至此所有的编码开发工作全部完成了,在浏览器中打开就可以看到效果了,后续我会把演示代码提交到Github上。
本例只是一个简单的Demo,留下2个有关项目扩展的思考:
1、假设是一个在线客服系统,里面有许多的公司使用你的服务,每个公司自己的用户可以通过一个专属URL地址进入该公司的聊天室,聊天是一对一的,每个公司可以新建多个客服人员,每个客服人员可以同时和客户端的多个用户聊天。
2、又假设是一个在线WebIM系统,实现类似微信,qq的功能,客户端可以看到好友在线状态,在线列表,添加好友,删除好友,新建群组等,消息的发送除了支持基本的文字外,还能支持表情、图片和文件。
有兴趣的同学可以继续深入研究。
北漂,有三孩子:JiaThis、友言、友荐}

我要回帖

更多关于 socket.io的用法 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信