WebSocket的原理、实现和应用


webSocket是一项可以让服务器将数据主动推送给客户端的技术,无论前后端都会接触。在实际的项目开发中,webSocket在用于双向传输和推送消息方面能做到灵活、简便、高效,发挥着不可或缺的作用。

一、什么是webSocket

webSocket是HTML5的协议,支持持久连接,它允许服务端向客户端传递信息,实现浏览器和客户端的双工通信。

二、webSocket的协议

webSocket是基于http协议的持久化的🆕应用层协议,或者说是借用了http协议来完成一部分握手👋,在握手阶段和http是相同的(为了兼容现有的浏览器,握手时不容易屏蔽,能通过各种http代理服务器),它弥补了http不支持长连接的特点

http协议不支持持久性连接,http1.0 和 http 1.1都不支持持久性连接,http1.1中的keep-alive,将多个请求合并为一个)

webSocket握手协议的实现:

  • 两个属性upgradeconnection
  • 基本请求示例(添加了两个属性,告知Apache、Nginx等服务器所发是webSocket):

典型的webSocket握手:

GET / chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade 
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

服务器返回(表示接收到请求,成功建立webSocket连接):

HTTP/1.1 101 Switching Protocols
Upgrade: webSocket
Connection: Upgrade
Sec-Websocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

http的生命周期是通过Request来界定,也就是Request一个Response,那么在http1.0协议中,这次http请求就结束了。在http1.1中进行了改进,是有了一个connection:keep-alive,也即在一个http连接中可以发送多个request和接收多个response。但在http协议中一个request只能对应一个response,而这个response是被动的,不能主动发起。

webSocket和Http的差异

三、webSocket的原理

webSocket诞生的本质:解决HTTP协议本身的单向性问题 ——请求必须由客户端向服务端发起,然后从服务端进行响应。这样的Request-Response关系是无法改变的。在一般情况下没有问题,但是一旦我们希望服务器能够主动向客户端发送消息的时候就比较麻烦💫(因为之前的TCP连接已经释放掉了,服务端根本找不到客户端在哪里)

webSocket从技术根本上解决了上述的问题,它借用了web的端口和消息头来创建连接,后续的数据传输和基于TCP的Socket几乎完全相同,但是其中封装了许多原本在Socket中需要我们手动去实现的功能,如:原生支持wss安全访问(跟https共用端口和证书)、创建连接时的校验、从数据帧中自动拆分消息包等等🍂 ,webSocket还可以在新旧浏览器上实现兼容。

webSocket的原理:
webSocket是应用层第七层上的一个应用层协议,它必须依赖HTTP协议进行一次握手,握手成功之后,数据就直接从TCP通道传输,与HTTP无关了。
webSocket分为握手和数据传输阶段,即进行HTTP握手 + 双工的TCP连接

对比基于TCP的Socket, webSocket的优点💓

基于Tcp的Socket webSocket
身份验证 Socket连接之后还要进行一些复杂的身份验证,同时还需要阻止未验证的连接发送控制指令 在建立webSocket连接的URL中就可以携带身份验证参数,验证不通过可以直接拒绝,不用设置状态
加密机制 需要自己实现一套类似SSL的非对称加密机制 直接通过WSS加密,同时还能保证证书的可信性
数据格式 需要自己定义Socket数据格式,设置长度和标志,处理粘包、分包等问题 webSocket收到的直接就是完整的数据包,不用自行处理
部署 部署困难 前端的nginx可以直接进行转发和负载均衡,部署更简单

⭐️ webSocket的特点:

  • 实现了真正的双向平等对话,属于一种服务推送技术
  • 建立在TCP协议上,服务端的实现比较容易
  • 与HTTP协议有着良好的兼容性,默认端口也是80和443
  • 轻量级数据格式,性能开销小,通信更高效
  • 可以发送文本和二进制数据
  • 没有同源限制,客户端可与任意服务器通信
  • 协议标识符是ws(加密则为wss),服务器网址就是URL

由于http协议的 单向性被动性,传统的 ajax轮询和长连接(long poll) 的请求方式早已经不能够很好地适应数据通信的发展需要,不仅效率低还造成了资源的浪费❗️

四、webSocket的实现

webSocket在前端中的实现(聊天室demo为例):

if(window.WebSocket){
  websocket = new WebSocket('ws:127.0.0.1:2345/server.php')
}else{
  // 不支持webSocket
  console.log('该浏览器不支持webSocket')
}
websocket.send('msg')
alert(websocket.readyState)
// 发生错误
websocket.onerror = function(){
  i++;
  console.log('websocket Connection Error!')
  $('.show-area').append('

Connect to WebSocket server error.

') window.location.hash = '#' + i } // 连接建立 websocket.onopen = function(event){ console.log('Connected to WebSocket server') $('.show-area').append('

Connected to WebSocket server!

') } // 收到消息 websocket.onmessage = function(event){ var msg = JSON.parse(event.data) // 解析收到的json消息数据 var type = msg.type; // 消息类型 var umsg = msg.message; // 消息文本 var uname = msg.name; // 发送人 i++ if(type === 'usermsg'){ $('.show-area').append('

' + uname + ' : ' + umsg + '

'); } if(type === 'system'){ $('.show-area').append('

' + umsg + '

') } $('#message').val('') window.location.hash = '#' + i } // 连接关闭 websocket.onclose = function(){ i++; console.log('websocket Connection Closed. '); $('.show-area').append('

websocket Connection Closed.

') window.location.hash = '#' + i; } function send(){ var name = $('#name').val() var message = $('#message').val() if(!name){ alert('请输入用户名!') return false } if(!message){ alert('发送消息不可为空!') return false } var msg = { message: message, name: name } try{ websocket.send(JSON.stringify(msg)) } catch(ex){ console.log(ex) } } // 按下enter键发送消息 $(window).keydown(function(event){ if(event.keyCode === 13){ console.log('use Enter!') send() } }); // 点击发送按钮发送消息 $('.send').bind('click', function(){ send() }); }

完整的前端聊天室demo代码

五、webSocket的应用场景

webSocket的使用场景有很多,社交聊天、弹幕、多玩家游戏、协同编辑、股票基金实时报价、体育实况更新、视频会议/聊天、基于位置的应用、在线教育、智能家居等需要高实时的场景🌄

我们都知道,近几年来,在原生JS和其他库都吸收了jQuery的优势之后,jQuery已经渐渐淡出了我们的视野👀
💬既然webSocket在数据通信中具备如此之多的优势,那它能否取代Ajax❓

  • 绝大部分的Ajax的使用场景依旧是传统的Request-Response形式(如获取json数据、post表单之类),直接使用Ajax更加简单、成熟
  • 传输大文件、图片、媒体流的时候,最好依旧使用http来传。没必要去占用用于推送消息、对实时性要求很强的连接,否则会造成串行的webSocket拥塞堵死

在上述情况☝️中不推荐使用webSocket并不意味着它不能去实现这些,只是一层叠一层去造一个新轮子实在是没有必要,同样的情况下,直接使用Ajax更加简洁高效、更加契合我们开发的初衷💖

每项技术都有自己的优势应用场景,在优势场景下可以发挥出它的最大优势🌟,但若仅因它的个别优势就全方位推广并非明智之举,效果很有可能适得其反。webSocket在双向传输、推送消息方面能够做到灵活、简便、高效,效果非常的好。但这并不意味着可以用webSocket取代HTTP,它只是取代了原先用于通信的基于TCP的Socket.


Author: Casey Lu
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Casey Lu !
评论
 Previous
跨域的原理及解决方案 跨域的原理及解决方案
一、何为跨域 广义的跨域是指一个域下的文档和脚本试图请求另一个域下的资源。 举 资源跳转: A链接、重定向、表单提交 资源嵌入:<link>、<script>、<img>、<frame>等D
2020-03-16
Next 
CSS中的Flex弹性布局和定位 CSS中的Flex弹性布局和定位
在移动端的开发中,为了方便起见,Flex弹性布局成为最常见的布局方式,单纯地使用普通定位以及不能满足我们的设计需要。 一、传统的元素定位 传统布局上,对于元素的定位,我们主要从三方面来设置display、position 和 float,此
2020-03-13
  TOC