上篇文章发出来后,有人留言说到HTTP新版的RFC的问题,WebSocket和Socket的区别。本文将先回答这两个问题,然后继续展开WebSocket。在这篇文章将看到:
- 关于新的HTTP规范
- WebSocket和Socket的区别
- WebSocket中数据帧的格式
- WebSocket的实现:socket.IO
- 使用socket.IO实现一个在线直播系统
关于新的HTTP规范
HTTP 1.1在2014年(15年后)被更新为6个单独的「协议说明」,如果去读这6个文档,又是很大的工作量,庆幸有很多文章总结了它们相对于RFC2616的不同之处,RFC7230中也列举了这些feature,所以只是大致把这个看了看。对于这个系列有影响的可能有这两点:
Upgrade头域功能的扩展
Upgrade
是HTTP中用来进行协议升级的头域,在扩展的协议内容中,客户端发起的协议转换的方式更多,同时服务器也可以选择不接受客户端的协议升级请求;服务端也可以发起协议升级。Uri的格式
#fragment
的引入,现在越来越多的人愿意使用fragment来标识网站中的位置了。
完整的变化列表可以在这里找到。这次变化可以算是一个补丁版,把原来漏掉的小东西补上,版本号无变化。
WebSocket和Socket的关系
关于这两者的区别,我写了一篇专门的文章来讨论,可以在WebSocket和Socket的区别看到,简短的答案也放在这里,如果你不想去看另一篇的话。
- 命名方面,Socket是一个深入人心的概念,WebSocket借用了这一概念;
- 使用方面,完全两个东西。
可以把WebSocket想象成HTTP,想一想HTTP和Socket什么关系,WebSocket和Socket就是什么关系。
WebSocket中数据帧的格式
本文是此系列的最后一篇了,可能是因为懒,很多想写的东西最终都放弃了。关于WebSocket的帧传输这里就不做详细的讲解了,之前的文章试图把很多细节都展开,但发现那样最后只是变成了规则的罗列,如果有兴趣,可以去这里看文档。
WebSocket的实现:socket.IO
socket.IO是一个基于node的实时通信引擎(FEATURING THE FASTEST AND MOST RELIABLE REAL-TIME ENGINE),第一次看到这个框架时,就想到它的底层应该就是WebSocket吧,实时全双工通信,不就是WebSocket的设计目标吗?于是开始对WebSocket底层的实现进行简单的探索。
socket.IO的底层引擎是engine.io,engine.io的实现中使用了HTTP和WebSocket两种方式来构建自己的服务端,如果客户端不支持WebSocket协议,则使用轮询的方式来实现实时传输,如果客户端支持WebSocket协议,则使用另一个模块ws,ws是一个优秀的WebSocket的JS实现,在ws的README中他们自称为「可能是node平台上最快的WebSocket实现」。
对于普通产品的开发,我们可能不可避免的要照顾到那些从不支持WebSocket的客户端(一般情况下是浏览器)发出的请求,或者有的服务器中不支持部署WebSocket协议的服务端,所以socket.IO这种妥协的方法不失为一个向下兼容的好办法。
如果可以正常建立连接,则会停掉轮询的方式,使用WebSocket进行接下来的连接。
ws
socket.IO在JS领域算是大名鼎鼎了,截止到今天socket.IO在github上的star数量是27543,而作为socket.IO的核心模块,ws在github上的star数量却只有4005。
一件有趣的小事
ws的源码中可以搜到hixie
这个单词,在上一篇关于WebSocket名字由来的文章中介绍过,这个人是WebSocket的命名者。
使用socket.IO实现一个在线直播系统
前段时间当时的老板要求做一个「简单的微信上的文字直播程序」,一接到这个需求,我想到的就是koa + WebSocket + mongoDB
,不得不说,基于node的服务端开发极大的拉低了开发一个服务的门槛,尤其是ES6之后,感觉JS这个以前看起来很随意的语言变得愈加性感起来了 。语法越来越现代化,性能越来越好,而随着npm上的模块越来越多,开发一个业务系统变得空前的简单。
书归正传,经过分析之后,一个「简单的微信上的文字直播程序」大约有3个功能:
- 鉴权
发布消息的人需要登录之后才能进行操作。 - 发布消息
直播的播主进行直播,播主的客户端与服务器建立WebSocket连接,在发送消息的同时也作为一个接收消息的客户端。 - 接收消息
所有打开直播页面的客户端都与服务器建立一个WebSocket连接,第一次进入时拉取部分历史消息,之后每条播主的消息都会同步推送到客户端。
由于时间仓促,并没有严格保证代码质量,所以就不在这里贴太多的代码了,在这次实现中并没有直接使用socket.IO,而是使用了一个加壳的版本koa-socket,这个库已经很久没有更新了,它接受的函数并不是generator,所以在操作数据库时遇到了一些问题。其中处理发布消息的函数如下:
controllers/socket.js
exports.postedHandler = ( ctx, data ) => {
const createdAt = new Date();
let newPost = {content: data.content, createdAt: createdAt};
if (data.image) {
newPost.image = data.image;
}
newPost.date = moment(createdAt).format("HH:mm");
Posts.insert(newPost);
if (!app) {
app = require('../app')
}
app.io.broadcast('posts', [newPost] )
}
app.js
const IO = require( 'koa-socket' )
...
/**some other code**/
...
const io = new IO()
io.attach( app )
io.on('post', socket.postedHandler)
后记
最近由于各种各样的原因,原定的计划严重delay了。关于帧传输的一节本来设计了很多,可是写出来就变得索然无味了;关于在线直播系统的介绍,本以为可以写很多,但是写时才发现除了代码并没有什么好共享的。无论如何都算是给这个系列结了个尾,就像那句歌词,every new beginning comes from some other beginning's end.