在《闪电提速》的博客中我提到了用socket.io 替换原来的连接方式,本章就如何引入socket.io的细节做进一步阐述。
主要的内容如下:
1、WebSocket 简介
2、为什么要用socket.io
3、如何用socket.io
4、后续安排
一、WebSocket简介
如果您对websocket协议不太熟悉,可以通过这两个链接先了解一下。
WebSocket详解(一):初步认识WebSocket技术
WebSocket 是什么原理?为什么可以实现持久连接?
我简单提一些吧。Ajax之所以重要,从本质来说,它避免了许多原本需要在Web应用中处理的数据传递和渲染开销。但它有一个先天的缺陷,就是无法控制服务器端接受请求的先后顺序!同时,在TCP传输信息时,信息头的数据量有时远远超过了真正要发送的数据量。
HTML5给我们一个解决方案,就是WebSocket!WebSocket是web下的TCP,一个底层双向的socket,容许用户对信息传递进行控制。
WebSocket是建立在HTML协议之上,但它和HTML的区别是,在握手之后,客户端和服务器就建立了类似TCP Socket通道,为了更好地理解,看下面的图示:
node.js实现WebSocket的demo很多,如果有时间,可以简单搜一下。不看也没什么大不了的,记住塔哥这句话,websocket就是个双向数据通道,看懂下一章没问题的。
二、为什么要用socket.io
WebSocket有它诱人的一面,但现实中浏览器的使用情况给前端开发带来巨大的难题。塔哥写了好多年C++,只要动态库的依赖关系整明白了,几乎所有的windows系统都没问题,但在前端开发中就没那么幸运了。
Socket.IO的出现就是为了解决这个问题,它的特征之一就是消息的传递是基于传输的,而非全部依赖WebSocket,也就是说,Socket.IO可以在绝大多数浏览器上运行,尤其是对iOS的支持。这也是我选择Socket.IO的原因。
在Web世界,更多的通信方式是依赖请求与应答。但在协同领域,更多的是基于“事件”的传输。Socket.IO通过:
• 分发(emit)
• 监听(listen)
事件来进行数据的传输,我们抓一下石墨文档(这是我非常喜欢的一个国内产品),能看到它的协同通信方式就是用的socket.io。
好了,我的铺垫也不少了,该回答为什么我在together.js中引入socket.io:
- 因为使用socket.io后,这个协同产品能在更多的平台下运行;
- Socket.IO在git上有4K多个Star,运行稳定、文档齐全;
三、怎么引入socket.IO
打铁还需自身硬,多见识多练,坑踩多了,闯江湖就没什么可怕的了。这一章我写之前还是犹豫过的,因为socket.IO我也是第一次用,写不好露怯是小事,主要还是怕把您给耽误了。后来觉得,还是写吧,写的过程也是一个成长的过程。还是那句话,如果有不妥的地方,还望您指证。我先行谢过 :)
引入socket.IO的工作我分两部分介绍,先服务端再客户端。
3.1 TJS的服务器端改造
together.js 的服务器端代码是在hub目录下,它实现了:
• websocket的创建与生命周期管理;
• 状态维护与消息的派发;
写的怎么说呢,也许是我始终没理解作者的思路,端详好几回也不知道从哪下手,干脆重新写吧。所以说,在优化的过程中,websocket的服务器端代码我放在了“devserver.js”里,node直接启动,抛弃了原有hub/server.js。
主要内容是:
3.2 TJS的客户端改造
客户端引入socket.io就容易多了,还是以Tinymce这个demo来说,下图就是index.html中引入socket.io.js的方法。
然后替换channels.js中原有websocket实现方式,具体内容有两步:
1) _setupConnection()
_setupConnection: function () {
if (this.closed) {
return;
}
//connect socket.IO
this.hSocket = io();
//设置缺省用户状态
this.hSocket.emit('add user', "zout" , "001");
//各种事件接收
this.hSocket.on('app.draw', (data) => this._incoming(data));
this.hSocket.on('form-update', (data) => this._incoming(data));
this.hSocket.on('form-init', (data) => this._incoming(data));
this.hSocket.on('app.init', (data) => this._incoming(data));
this.hSocket.on('hello', (data) => this._incoming(data));
}
2) _setupConnection()
_send: function (vType , data) {
this.hSocket.emit(vType ,data);
},
这样一来,socket.io就融入TJS,同时给TJS带来速度上的飞跃是非常非常明显的。
四、关于后续安排
在我看来,替换socket.io以后的维护主要还是服务端消息的分发,如果按照现有的逻辑,每增加一个事件都要做相应的修改,太low了!
我的想法是结合“指令精简”,把事件进行分类,不同类型的事件有自己的消息中转。这样的话事件相互是独立的,对研发与部署都有利。