-
什么是WebSocket?
WebSocket是HTML5开始提供的一种在TCP连接上进行全双工通讯的协议,是一种服务器推送技术。
-
WebSocket出现之前客户端如何获取服务端的实时数据?
Comet:是一种服务器向页面推送数据的技术。有以下实现方式:
(1) ajax轮询:浏览器定时向服务器发送请求,询问服务器是否有新信息。
(2) 长轮询:客户端发送请求后,服务器会保持连接,直到有新数据返回给客户端或请求超时。客户端收到数据并处理完,或者请求超时后,再向服务端发送新请求。
(3) HTTP流:浏览器向服务器发送一个请求,服务器保持连接打开,然后周期性地向浏览器发送数据。浏览器通过侦听
readystatechange事件及检测readyState的值是否为3,是则比较此前接收到的数据,决定从收到的数据中的什么位置开始取得最新的数据。(4) SSE:服务器发送事件,是围绕只读Comet交互推出的API。使用SSE API创建到服务器的单向连接,服务器可以通过这个连接发送任意数量的数据。SSE支持ajax轮询、长轮询和HTTP流,能在断开连接时自动确定何时重新连接。
-
为什么需要WebSocket?
(1) HTTP协议的局限
- HTTP协议是一种单向的网络协议,通信只能由客户端发起,服务器无法主动向客户端推送信息。
- HTTP协议是无状态的,客户端向服务器发送请求时,每次都要重读发送鉴别信息。
(2) 轮询、HTTP流、SSE方式存在局限性:
- ajax轮询:服务器在某个时间段没有数据更新时,会返回相同的数据,浪费带宽,降低CPU的利用率。
- 长轮询:服务端数据更新较快时,需等待客户端发送请求才可返回数据,存在客户端处理数据、请求建立连接和网络延时等时差问题。
- HTTP流:管理连接比较容易出错。
- SSE:只适用于服务器到客户端的单向推送,如比赛成绩。
WebSocket是一种与服务器进行全双工、双向通信的信道,专门为快速传输小数据设计。适用于聊天室等需要双向通信的场景。
-
WebSocket的特点(1) 双向通信,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息
(2) 建立在TCP协议之上
(3) 与HTTP协议有着良好的兼容性
(4) 数据格式比较轻量,性能开销小,通信高效
(5) 可以发送文本,也可以发送二进制数据
(6) 没有同源限制,是否与某个域中的页面通信,完全取决于服务器(通过握手信息可以知道请求来自何方)
(7) 协议标识符为
ws或wss,服务器网址就是URL -
WebSocket协议与HTTP协议的联系和区别
(1) 联系
- 两者都建立在TCP协议之上,通过TCP传递数据。
- 为了兼容现有浏览器的握手规范,WebSocket需要借用HTTP协议完成握手阶段。
(2)区别
WebSocket HTTP 是HTML5新增的一种通信协议 与HTML无直接关系 双向通信 单向通信 持久连接,整个通讯过程建立在一次连接中 非持久连接,每次请求要重新建立连接 -
建立WebSocket连接的过程
客户端向服务器发送一个升级协议的HTTP请求,这个请求头部包含
Connection和Upgrade字段,表示客户端需要使用WebSocket协议。服务器将HTTP协议升级成WebSocket协议后返回客户端响应数据,即完成了握手阶段,建立了WebSocket连接,这个连接会持续存在,直到客户端或服务器主动关闭连接。 -
WebSocket的使用
(1) 使用
WebSocket构造函数创建WebSocket对象var ws = new WebSocket('wss://wx.douyucdn.cn')(2) 添加事件处理程序
ws.onopen = function() { // 调用send()方法发送数据 ws.send('发送数据'); } ws.onmessage = function(event) { // 处理接收到的数据 var receive_msg = event.data; } ws.onerror = function() { // 出错处理 ws.close(); } ws.onclose = function() { // 关闭WebSocket } -
WebSocket的应用
双向通信,如聊天室。
(1) 微信小程序对
WebSocket进行了封装,wx.connectSocket()可以理解为创建了一个WebSocket实例SocketTask。(2)
socket.io支持WebSocket、轮询、HTTP流等方式。 -
WebSocket的降级方案
WebSocket协议不同于HTTP,需要建立和维护WebSocket服务器。如果现有服务器不能用于WebSocket通信,可以组合使用XHR和SSE实现双向通信。【WebSocket相当于在SSE的基础上增加了send()方法】
详解:
-
HTTP流的实现:
-
服务端
<?php $i = 0; while(true) { // 输出一些数据,然后立即刷新输出缓存 echo "Number is $i"; flush(); // 等待几秒钟 sleep(10); $i++; } ?> -
浏览器
function createStreamingClient(url, progress, finished){ var xhr = new XMLHttpRequest(), received = 0; xhr.open("get", url, true); xhr.onreadystatechange = function(){ var result; if (xhr.readyState == 3){ //只取得最新数据并调整计数器 result = xhr.responseText.substring(received); received += result.length; //调用 progress 回调函数 progress(result); } else if (xhr.readyState == 4){ finished(xhr.responseText); } }; xhr.send(null); return xhr; } var client = createStreamingClient("streaming.php", function(data){ alert("Received: " + data); }, function(data){ alert("Done!"); });
-
-
SSE API
(1) 创建一个
EventSource对象var source = new EventSource('myevents.php');(2) 添加事件处理程序
source.onopen = function() {} source.onmessage = function(event) { // 处理数据 var data = event.data; } source.onerror = function() { // 出错处理 source.close(); }(3) 属性
readyState属性:连接的状态- 0:表示正在连接
- 1:打开了连接
- 2:关闭了连接
(4) 方法
- close():强制立即断开连接并且不再重新连接。默认情况下,EventSource对象会保持与服务器的活动连接,如果连接断开,还会重新连接。
注:SSE方式服务器返回的数据是流信息,响应的
MIME类型必须是text/event-stream,即设置响应头部:Content-Type: text/event-stream -
HTTP与WebSocket的生命周期

-
WebSocket握手的请求头和响应头
请求中的header:
Connection: Upgrade:指示这是一个升级请求Upgrade: websocket:表示升级为websocket协议Sec-WebSocket-Key:向服务器提供所需的信息,确认客户端有权请求升级到WebSocket,防止滥用。此头部由客户端自动生成,无法通过XMLHttpRequest.setRequestHeader()方法添加。Sec-WebSocket-Protocol:以优先顺序指定客户端要使用的一个或多个协议Sec-WebSocket-Version:指定客户端希望使用的WebSocket协议版本Sec-WebSocket-Extensions:指定要求服务器使用的一个或多个WebSocket扩展
服务端响应中的header:
Connection: UpgradeUpgrade: websocket:和Connection一起表示服务器已成功切换协议Sec-WebSocket-Accept:返回根据请求头中Sec-WebSocket-Key字段值生成的值Sec-WebSocket-Protocol:返回客户端指定使用的所有协议中支持的第一个协议Sec-WebSocket-Version:服务器如果支持客户端要求的协议版本,则响应头中不包含这个字段。否则,服务端将返回一个错误,并在响应头中以此字段返回服务器支持的协议列表Sec-WebSocket-Extensions:MDN文档中没有明确指明,私以为与Sec-WebSocket-Version相似
-
WebSocket API
(1) 使用构造函数创建
WebSocket对象var socket = new WebSocket(url, [protocol]); // protocol指定了可接收的子协议(2) 属性
WebSocket.readyState:返回实例对象的当前状态- 0:对应
WebSocket.CONNECTING,正在连接 - 1:对应
WebSocket.OPEN,连接成功 - 2:对应
WebSocket.CLOSING,正在关闭连接 - 3:对应
WebSocket.CLOSED,连接关闭,或打开连接失败
- 0:对应
WebSocket.bufferedAmount:表示队列中等待传输的UTF-8字节数WebSocket.onopenWebSocket.onmessageWebSocket.onerrorWebSocket.oncloseWebSocket.binaryType:WebSocket.protocolWebSocket.url
(3) 事件
事件 事件处理程序 描述 open Socket.onopen 连接建立时触发 message Socket.onmessage 客户端接收服务端数据时触发 error Socket.onerror 通信发生错误时触发 close Socket.onclose 连接关闭时触发 (4) 方法
-
WebSocket.send(data):发送数据 -
WebSocket.close:关闭连接
-
WebSocket在项目中的应用:
- 微信斗鱼小程序
tcp/TcpClient.js使用wx.connectSocket返回WebSocket实例SocketTask - 主站代码
@shark/net/lib/tcp/webSocket.js直接调用WebSocket构造函数创建WebSocket实例
- 微信斗鱼小程序
参考:
【阮一峰】WebSocket教程:www.ruanyifeng.com/blog/2017/0…
【知乎】WebSocket是什么原理?为什么可以实现持久连接?www.zhihu.com/question/20…
【掘金】WebSocket介绍以及node+socket-io实现聊天室功能:juejin.cn/post/684490…
【MDN】协议升级机制:developer.mozilla.org/zh-CN/docs/…
【MDN】WebSocket:developer.mozilla.org/en-US/docs/…
【菜鸟教程】HTML5 WebSocket:www.runoob.com/html/html5-…
【HTML5】全双工通信的WebSocket:halfrost.com/websocket/
【socket.io】socket.io:socket.io/get-started…
【JavaScript高级程序设计】第21章21.5节