阅读 3732

带你揭开WebSocket的神秘面纱!

写在前面

在揭开webSocket的神秘面纱之前,有言在先,我在写文章之前对webSocket一无所知,由于公司业务用到,故此研究记录一下,班门弄斧之处,请大佬批评指正!

废话少说,老规矩,从前世今生讲起

什么是webSocket

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议 。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以建立持久性的连接,并进行双向数据传输。

听了官方的解释,你是不是又像在读文言文,下面我们给他赋予一种通俗的解释

WebSocket 是一种网络通信协议,他可以让服务器将数据主动推送给客户端

说白了,他就是能给数据主动推送给客户端,接下来我们又有疑问了,为啥我们需要他呢?

为什么需要webSocket

我们不是有了http这种万能协议,为啥还需要这玩意嘞!因为http不能完成服务端推送啊,有人又要问了,新出来的HTTP/2不是可以吗?HTTP/2 只能推送静态资源,无法推送即使的信息。啥意思呢? 经过多方探寻发现HTTP/2 所谓的server push其实是当服务器接收一个请求时,可以响应多个资源。

举个例子:当我想要去请求服务器中的a.html,服务器不仅把a.html给我们,还可以把js、css等文件也一并扔给我们。最直观的好处就是,浏览器不用解析页面再发起请求来获取数据,节约了页面加载时间。 所以HTTP/2并不是webSocket的替代方案,如果我们要实现即时通讯,那么webSocket当然不可代替

怎么用webSocket

在知道怎么用webSocket之前我们现来研究一下他的原理

原理

如果上图所示: http和webSocket其实是个交集,他们的使用都是建立在tcp链接之上。

要想使用webSocket与server建立链接,首先需要去建立一个tcp链接,然后握手阶段采用的是 HTTP/1.1 协议,实际上我认为他就是借用一部分http协议的东西来达到他的能力

下面我们看来看看他是怎么搞的呢?

如上图所示,我们在ws中就能看到这个持久的webSocket
点开一看,是个这玩意,下面我们来喜喜探究一下

//请求消息
GET wss://webchat-bj-test5.clink.cn&province= HTTP/1.1 //请求地址
Host: webchat-bj-test5.clink.cn  //域名
Connection: Upgrade
Pragma: no-cache  
Cache-Control: no-cache  //缓存相关
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.9 Safari/537.36 //ua
Upgrade: websocket  //下面这些就是websocket这些东西了
Origin: http://clink2.clink.cn:5050
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Sec-WebSocket-Key: O5GLCYKZVQi2jTLENobvtg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
复制代码
//响应消息
HTTP/1.1 101 // 响应行,告诉你版本相关
Server: nginx/1.13.9  //服务器用的啥
Date: Mon, 30 Mar 2020 09:21:00 GMT   
Connection: upgrade
Vary: Origin
Vary: Access-Control-Request-Method  //跨域相关
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Upgrade: websocket    //下面这些就是websocket的东西了
Sec-WebSocket-Accept: uZpmP+PDDvSeKsEg9vkAsWcqPzE=
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
复制代码

上面两段代码你就会发现,他是在http的基础上加了点东西,告诉服务器,我是个websocket请求 服务器做了解析和处理以后就能结结实实的通信了!

接下来介绍请求和相应消息的知识点了

  • 1、协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
  • 2、Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,用于验证跟服务器是不是能对眼
  • 3、Sec—WebSocket-Protocol 是一个列出的客户端请求的子协议,服务端应按照优先顺序排列
  • 4、Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本)
  • 5、Upgrade 是告诉客户端我们成功切换成websocket协议了
  • 6、Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key(我觉得底层用的是非对称加密)

如此之后算是建立了一个websocket链接

如何使用

用法方面相当简单,我们照着mdn一顿操作即可

//首先new一个websocket对象,
var ws = new WebSocket("wss://webchat-bj-test5.clink.cn");
//实例对象的onopen属性,用于指定连接成功后的回调函数。
ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  //实例对象的send()方法用于向服务器发送数据。
  ws.send("Hello WebSockets!");
};
//实例对象的onmessage属性,用于指定收到服务器数据后的回调函数。
ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};
//实例对象的onclose属性,用于指定连接关闭后的回调函数。
ws.onclose = function(evt) {
  console.log("Connection closed.");
};     
复制代码

以上阮一峰大佬的例子,基本涵盖了常用的使用场景,如果好奇,请mdn找寻答案

当然,原生的东西是相当不好用的,因为你虽然建立了链接,后端处理器起来却不是那么得心应手,所以在这个时候牛逼的库又诞生了,再次我隆重推荐 socketIO

SocketIO

SocketIO将WebSocket、AJAX和其它的通信方式全部封装成了统一的通信接口,也就是说,我们在使用SocketIO时,不用担心兼容问题,底层会自动选用最佳的通信方式。因此说,WebSocket是SocketIO的一个子集。

废话少说上代码

node端
//引入koa
var app = require('koa')();
//创建http服务
var server = require('http').createServer(app.callback());
//给http封装成io对象
var io = require('socket.io')(server);
// 建立链接
io.on('connection', function(socket){
// io.emit代表广播,socket.emit代表私发
socket.on('eventB', function(socket){ /* */ });

socket.emit('eventA', /* */);
});
server.listen(3000);
复制代码
//前端
<script src="http://localhost:3000/socket.io/socket.io.js"></script>
<script>
    //创建个服务
    var socket = new io()
        用on监听
        socket.on('eventA', function (res) {
        console.log('⽤户1接收到信息了了')
    })
    socket.emit('eventB', data)
</script>
复制代码

到此就算用框架实现了完整的即使消息

兼容问题

在2020年的今天这个兼容已经相当乐观了,请放心使用!

websocket的特点(重点:面试要考)

  • 1、建立在 TCP 协议之上,服务器端的实现比较容易。
  • 2、与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  • 3、数据格式比较轻量,性能开销小,通信高效。
  • 4、可以发送文本,也可以发送二进制数据。
  • 5、没有同源限制,客户端可以与任意服务器通信。

目前web即时通讯的方案

1、ajax轮询

ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,问问服务器有没有新消息。这个访问,对后端非常友好,无限调用接口即可,但是有个非常致命的缺点,浪费带宽和服务器资源。小型应用还扛得住,但是万一又成千上万的人想要即时消息呢?这就不能满足了

2、long poll(长轮询)

其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。这样做是解决了无限调用接口的弊端,但是却一直维护了一个长链接,会让一个http请求一直处在pedding状态!服务器压力也是相当大,所以,还是不可取

3、websocket

这就是我们今天的主角了,难道他就没缺点吗?

当然有了,由于传输数据需要进行二次解析,增加开发成本及难度,所以公司得多花钱了

SSE

评论中有同学说到了sse,下面我们来探索一下

所谓SSE(Sever-Sent Event),就是浏览器向服务器发送一个HTTP请求,保持长连接,服务器不断单向地向浏览器推送“信息”(message),这么做是为了节约网络资源,不用一直发请求,建立新连接。

看了概念你就会发现,他其实就是类似长轮询,他有一个浏览器内置EventSource对象来操作

//进建立链接
var source = new EventSource();
//关闭链接
source.close();
复制代码

优缺点

优点就是当然是他是一个http的请求啊,由于被浏览器封装了,所以使用简单,节约开发成本

缺点也相当明显,无法实现双向消息

后记

首先先感谢各位大佬的文章

WebSocket 教程 聊一聊Web端的即时通讯

由于工作需要,学习了websocket,并整理再次,帮助大家一篇文章全搞定,不用谢我,请叫我雷锋!