一个前端面试的典型题。

服务器数据变化如何主动告知浏览器?
脚手架工具中,代码更新后,是如何通知浏览器数据变化的?

WebSocket 是一种基于 TCP 连接上进行全双工通信的协议,相对于 HTTP 这种非持久的协议来说,WebSocket 是一个持久化网络通信的协议;它不仅可以实现客户端请求服务器,同时可以允许服务端主动向客户端推送数据。在 WebSocket API 中,客户端和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

一、为什么需要 WebSocket

在 Web 应用架构中,连接由 HTTP/1.0 和 HTTP/1.1 处理。HTTP 是客户端/服务器模式中 请求一响应 所用的协议,在这种模式中,客户端(一般是浏览器)向服务器提交 HTTP 请求,服务器响应请求的资源(例如HTML页面)。
HTTP 是无状态的,也就是说,它将每个请求当成唯一和独立的。无状态协议具有一些优势,例如,服务器不需要保存有关会话的信息,从而不需要存储数据。但是,这也意味着在每次 HTTP 请求和响应中都会发送关于请求的冗余信息,比如使用 Cookie 进行用户状态的验证;

随着客户端和服务器之间交互的增加,HTTP 协议在客户端和服务器之间通信所需要的信息量快速增加。
从根本上讲,HTTP 还是 半双工 的协议,也就是说,在同一时刻信息的流向只能单向的:客户端向服务器发送请求(单向),然后服务器响应请求(单向)半双工方式的通信效率是非常低的。

同时 HTTP 协议有一个缺陷:通信只能由客户端发起。
这种单向请求的特点,注定了如果服务器有状态变化,是无法主动通知客户端的。
为了能够及时的获取服务器的变化,我们尝试过各种各样的方式:
轮询(polling):每隔一段时候,就发出一个请求,了解服务器有没有新的信息。不精准,有延时,大量无效数据交换;

长轮询( long polling):客户端向服务器请求信息,并在设定的时间段内保持连接。直到有服务器有新消息响应,或者连接超时,这种技术常常称作“挂起GET”或“搁置POST”。占用服务器资源,相对轮询并没有优势,没有标准化;

**流化技术:**在流化技术中,客户端发送一个请求,服务器发送并维护一个持续更新和保持打开(可以是无限或者规定的时间段)的开放响应。每当服务器有需要交付给客户端的信息时,它就更新响应。服务器从不发出完整的 HTTP 响应;代理和防火墙可能缓存响应,导致信息交付的延迟增加。

上述方法提供了近乎实时的通信,但是它们也涉及 HTTP 请求和响应首标,包含了许多附加和不必要的首标数据与延迟。此外,在每一种情况下,客户端都必须等待请求返回,才能发出后续的请求,而这显著地增加了延退。同时也极大的增加了服务器的压力;

二、什么是 WebSocket

而 Websocket 是一种自然的全双工、双向、单套接字连接。解决了 HTTP 协议中不适合于实时通信的不足。2008年被提出,2011 年成为国际标准。
Websocket 协议能够通过 Web 进行客户端和服务器之间的全双工通信,并支持二进制数据和文本字符串的传输。这个协议由开始的握手和之后的基本消息框架组成,是建立在 TCP 协议上的。相比于 HTTP 协议,Websocket 链接一旦建立,即可进行双向的实时通信;

三、特点

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

四、相似技术

Server-sent Events(SSE):
https://www.ruanyifeng.com/blog/2017/05/server-sent_events.html

https://www.cnblogs.com/goloving/p/9196066.html

SPDY (读作“SPeeDY”): 已不再维护,由 HTTP/2 取代
https://baike.baidu.com/item/SPDY/3399551#7

WebRTC

https://baike.baidu.com/item/WebRTC/5522744

五、通信原理

WebSocket 链接是如何建立的?

前面说过,WebSocket 在握手阶段采用的是 HTTP 协议,Websocket 借用了 HTTP 的一部分协议来完成一次握手。(HTTP的三次握手,此处只完成一次)

HTTP 请求与响应首部

WebSocket 请求与响应首部

链接通信模拟

HTTP 轮询

首先是 ajax轮询 ,ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息
场景再现:
客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你 ’ 西岭真帅’ 。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。没。。。。没。。没有

从上面可以看出,轮询其实就是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性。同时,http的每一次请求与响应结束后,服务器将客户端信息全部丢弃,下次请求,必须携带身份信息(cookie),无状态性

WebSocket

客户端通过 http (骑马) 带着信请求服务器,但同时,携带了Upgrade:websocket和Connection:Upgrade(两根管子),服务器如果支持WebSocket协议(有两根管子的接口),使用Websocket协议返回可用信息(丢弃马匹),此后信息的传递,均使用这两个管子,除非有一方人为的将管子切断;若服务器不支持,客户端请求链接失败,返回错误信息;

Websocket 的出现,干净利落的解决了这些问题;
所以上面的情景可以做如下修改。
客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:13(HTTP Request)
服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的。
客户端:balab开始斗图alabala
服务端:西岭确实很帅
客户端:流鼻血了,我擦……
服务端:哈牛XX啊哈哈哈哈
服务端:笑死我了哈哈

六、Websocket 服务端与客户端实现

经过前面对通信过程的梳理,我们将 WebSocket 通信的基本机制已经说的差不多了,为了方便你快速进入实战阶段,我们暂时放弃纯手写实现,直接选择使用老牌的 WebSocket 库: WebSocket-Node

https://github.com/theturtle32/WebSocket-Node

简单介绍一下 WebSocket-Node ,它有多老牌呢?NPM 的包名字就是直接使用的 “WebSocket”,曾经,我们西岭老湿看到之后就给出了两个字的评价:“猖狂”;这个库完全使用 JavaScript 实现,包含了客户端及服务端的实例,其中,客户端包含了 Node 和 浏览器 两个运行环境的代码,除了支持我们前面提到的 Websocket 协议的 13 版本,它同时还支持 Websocket 协议 8 这个老版本,实属优秀;
接下来,我们就来看看,如何借助 Websocket-Node 实现一个 Websocket 服务;

服务端

安装 npm install websocket 后,创建服务器运行文件 ws-server.js ,代码如下:

// === 作为帅哥,一定要加注释 ===
var Websocket = require('websocket').server
var http = require('http')// 创建 HTTP 服务,作为第一次握手链接使用
var httpServer = http.createServer().listen(8080,function(){console.log('http://127.0.0.1:8080')
})// 创建 websocket 服务实力
var wsServer = new Websocket({// 配置依赖的握手 http 服务器httpServer:httpServer,autoAcceptConnections:false
})// 保存链接池
var conArr = []// 监听 ws 请求事件
wsServer.on('request',function(request){// 获取链接示例var connection = request.accept()// 保存连接池conArr.push(connection)// 监听消息事件connection.on('message',function(msg){console.log(msg)// 循环连接池,推送广播消息至客户端for(let i = 0;i<conArr.length;i++){conArr[i].send(msg.utf8Data)}})
})// 据说,长得好看的都会看注释

过多的描述,就不写了,据说,长得好看的都会看代码注释;
运行代码文件后,不出意外的情况下,命令行进程会被占用,监听端口也会被占用,证明服务端运行成功,如果两个都没被占用,想啥呢?失败了呀宝子……

如果服务器启动成功,我怎么用客户端建立链接查看呢?有一款 Websocket 客户端工具叫 WebsocketMan,如果感兴趣,你可以下载来试试;
但是像我这样的帅哥,一般都是自己写客户端;

客户端

websocket 的客户端并没有什么技术难点,就是浏览器 API 调用,只要你把通信机制够清楚,这玩意就没有不会;因为非常简单,我们直接选择纯手写就可以了,如果你想使用 Websocket-Node 客户端,确实还会更简单;
当然,在写之前,还是要去看看手册的,要不然你怎么知道有哪些 API 呢?来,手册地址给你:
https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
你先看着,我就不客气了,直接开干……


<body><div id="msg"></div><input type="text" id="text"><input type="button" value="发送" onclick="send()"><script>//调用websocket对象建立连接://参数:ws/wss(加密)://ip:port (字符串)var websocket = new WebSocket('ws://127.0.0.1:8080')// console.log(websocket.readyState) // 0 // readyState // 0 链接还没有建立(正在建立链接)// 1 链接建立成// 2 链接正在关闭// 3 链接已经关闭// 监听链接开启事件websocket.onopen = function () {console.log(websocket.readyState)}// 绑定按钮点击事件function send() {var text = document.getElementById('text').value// ws 消息发送websocket.send(text)}// 监听服务端消息推送事件websocket.onmessage = function (back) {console.log(back.data)}// 监听连接错误信息// websocket.onerror = function (evt, e) {//   console.log('Error occured: ' + evt.data);// };//监听连接关闭// websocket.onclose = function (evt) {//   console.log("Disconnected");// };</script></body>

过多的描述,就不写了,据说,长得好看的都会看代码注释;

至此,一个完整的 websocket 通信已经建立完成并能够进行双向通信了;
Websocket-Node 确实很好用,但是功能也确实比较单一了,需要你 WebSocket 机制有一定的理解之后,才能实现相应的能力;如果,我对 websocket 完全不懂,但又想搞个聊天室,能不能行?
淡然能行……

七、Socket.IO

一个目前最为强大且好用的,基本屏蔽了 websocket 概念的 websocket 库;你几乎不用掌握 websocket 相关的知识,只需要按照 Socket.IO 中提供的 API 就能够很好的实现一个 websocket 通信;
注意:程序员要“除机心”,在不了解 websocket 时,学习 websocket 中,强烈不建议使用 Socket.IO ;在生产环境下,强烈建议使用,确实很不错;

服务端

const { createServer } = require("http");
const { Server } = require("socket.io");const httpServer = createServer();
const io = new Server(httpServer, {cors: {origin: "*",methods: ["GET", "POST"]}
});io.on("connection", (socket) => {  socket.on('sendMsg',(data)=>{io.emit('pushMsg',data)})
});httpServer.listen(3000, function () {console.log('http://127.0.0.1:3000')
});

客户端

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="https://cdn.socket.io/4.2.0/socket.io.min.js"integrity="sha384-PiBR5S00EtOj2Lto9Uu81cmoyZqR57XcOna1oAuVuIEjzj0wpqDVfD0JA9eXlRsj"crossorigin="anonymous"></script>
</head><body><input type="text" id="text"><input type="button" value="发送" onclick="send()"><script>var socket = io.connect('http://127.0.0.1:3000')function send() {var text = document.getElementById('text').valuesocket.emit('sendMsg', text)}socket.on('pushMsg', (data) => {console.log(data)})</script></body></html>

没什么可解释的,就直接按照 Socket.IO 的 API 写就行了;

吾闻之吾师,有机械者必有机事,有机事者必有机心。机心存於胸中,则纯白不备。
– 《庄子·天地》

参考资料:

《HTML5 WebSocket权威指南》 机械工业出版社 2014 年 3 月第 1 版

http://www.ruanyifeng.com/blog/2017/05/websocket.html
https://www.cnblogs.com/hustskyking/p/websocket-with-node.html
https://www.cnblogs.com/jingmoxukong/p/7755643.html
https://zhuanlan.zhihu.com/p/23467317
https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket

库:

https://socket.io/docs/

https://github.com/theturtle32/WebSocket-Node

图文:
https://mp.weixin.qq.com/s/T65qVLUKPJHbjPGRGccS0w

https://mp.weixin.qq.com/s/CUMvqsWEJlmY5ImguTlYaw

https://mp.weixin.qq.com/s/1ZdDvHSntrLuLQgdCZxqcw

WebSocket 通信协议相关推荐

  1. socket通信和websocket通信协议

    socket通信 网络上的两个程序通过一个双向的通信链接实现数据的交换,这个链接的一端称为一个socket socket通信流程图 服务器端通过创建一个socket的通信链接,然后绑定socket和端 ...

  2. 强烈推荐|websocket 全双工通信协议详解

    一. 前言 在开发中,我们经常会遇到这样一类需求:需要在网页上显示天气预报,股票数据或者实时排行榜单等实时变化的数据.对于此类需求,一种较为原始的做法就是让客户端每隔一段时间主动去轮询服务器.但这种做 ...

  3. 自定义Udp/Tcp协议,通信协议Socket/WebSocket,IM粘包、分包解决等(2),ProtocolBuffer

    > 自定义Udp/Tcp协议/通信协议(Java/C):自定义构建和解析IM协议消息:IM自定义UDP通信协议   类似于网络通信中的TCPIP协议一般,比较可靠的通信协议往往包含有以下几个组成 ...

  4. websocket+netty实时视频弹幕交互功能(Java版)

    欢迎关注方志朋的博客,回复"666"获面试宝典 2021年了,还有不支持弹幕的视频网站吗,现在各种弹幕玩法层出不穷,抽奖,ppt都上弹幕玩法了,不整个弹幕都说不过去了,今天笔者就抽 ...

  5. 【物联网智能网关-14】Html5:Canvas+WebSocket实现远程实时通信(下)

    在上篇博文<Html5:Canvas+WebSocket实现远程实时通信(上)>中已经介绍了当前实现动态网页的一些基本技术,也说明了在.NET micro framework平台下实现We ...

  6. js websocket同步等待_WebSocket硬核入门:200行代码,教你徒手撸一个WebSocket服务器...

    本文原题"Node.js - 200 多行代码实现 Websocket 协议",为了提升内容品质,有较大修订. 1.引言 最近正在研究 WebSocket 相关的知识,想着如何能自 ...

  7. 网页实时聊天之PHP如何实现websocket

    网页实时聊天之PHP如何实现websocket 一.总结 一句话总结: 应用 PHP 的 socket 函数库:PHP 的 socket 函数库跟 C 语言的 socket 函数非常类似 PHP 实现 ...

  8. 计算机网络:WebSocket协议详解

    1. 概述 一直以来,网络在很大程度上都是围绕着HTTP的请求/响应模式而构建的.所有的HTTP通信都是由客户端控制的,这就需要用户进行互动或者定期轮询,以便从服务器加载数据.长期以来存在着各种技术让 ...

  9. 例子 客户端_服务端也是可以主动向客户端推送数据的--WebSocket

    简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议.WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范.WebSocket API也 ...

最新文章

  1. 使用EditPlus打造一个Python IDE
  2. HDU 4023 (博弈 贪心 模拟) Game
  3. hdu 5020 求三点共线的组合数(容器记录斜率出现次数)
  4. 反欺诈之地址的处理和使用
  5. [转] 理解SVG transform坐标变换
  6. django settings 定义的变量不存在_使用Django部署机器学习模型(1)
  7. [转]Web Api系列教程第2季(OData篇)(二)——使用Web Api创建只读的OData服务
  8. es6 map与set
  9. c语言实现语音检测vad_TWS+AI?国芯发布超低功耗语音芯片,可能是目前最理想方案...
  10. 用CSS让文字居于div的底部
  11. 网友发来ifeng网址,打开后却是QQ空间,总提示QQ未登录?原来是一个阴险的诱骗网页...
  12. 51单片机c语言脉冲计数实验报告,单片机计数器实验报告.doc
  13. 思维导图MindManager:大脑思维发散和归纳的工具
  14. 实时语音视频通话SDK如何实现立体声(二)
  15. Excel翻译单元格内容
  16. 互信息(Mutual Information)介绍
  17. 弘辽科技:拼多多批发单算销量吗?拼多多刚开店怎么有销量
  18. SQL Server查询IP地址等信息
  19. 2021 CNSS招新赛 WEB WP
  20. 【每日一问】工作日问题

热门文章

  1. gateway整合https(自定义证书)
  2. Google可能在春节后回归中国市场。
  3. 计网PPT 第七章 网络安全
  4. oracle 小数格式化为百分数
  5. 服务器出错的原因有哪些 原
  6. 【JavaScript】节点的常用属性和方法
  7. 推荐系统-协同过滤算法简介简化版实例
  8. H264码流打包成RTP包
  9. NOIP提高组 旷野大计算
  10. 爱帮网CEO刘建国:垂直化是搜索发展的趋势