1.WebSocket 是什么?
WebSocket 是 HTML5 提供的一种在单个 TCP 连接上进行全双工通讯的协议。(双向通信协议)
2.WebSocket 的作用?
实现客户端与服务器之间的双向通信,允许服务端主动向客户端推送数据。
在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
3.WebSocket 和 HTTP 区别?
WebSocket 与 HTTP的关系图:
相同点:
1. WebSocket 都是一样基于 TCP 的可靠性传输协议,都是应用层协议
异同点:
1. WebSocket 可以双向发送或接受信息,而 HTTP 是单向的(HTTP 通信只能由客户端发起,不具备服务器主动推送能力);
2. WebSocket 的使用,需要先进行一次 客户端与服务器的握手,两者建立连接后才可以正常双向通信,而 HTTP 是一个 主动的 Request 对应一个 被动的Response;
4.WebSocket 的协议标识符?
如果服务器网址是 HTTP 那么 WebSocket 对应的是 ws
如果服务器网址是 HTTPS 加密的 那么 WebSocket 对应的是 wss
5.WebSocket 的作用总结?
WebSocket 是为了能够实现在 web 应用上与服务器进行双向通信的需求 而产生出来的协议,
相比于轮询 HTTP 请求的方式,WebSocket 节省了服务器资源,有效的提高了效率。
6.HTML5 WebSocket的使用方式描述
通过JavaScript中的WebSocket对象来创建WebSocket连接,并通过send()方法向服务器发送数据,通过onmessage()方法接收服务器返回的数据。
目前,WebSocket协议已经广泛应用于实时聊天、在线游戏、语音视频聊天、股票行情等领域。
7.WebSocket协议的特点
最大特点:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
其他特点:
(1)实时性:与传统的 HTTP 请求/响应模式不同,WebSocket 允许实时双向通信,使得服务器能够主动向客户端推送数据,而不需要客户端发起请求。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)服务器压力减少:WebSocket 连接保持打开状态,因此服务器不需要为每个请求创建一个新的连接。这可以减轻服务器的负载并提高性能。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
(7)只需要经过一次 HTTP 请求,就可以一直传送消息(也称为回调)。
8.WebSocket 工作原理
WebSocket 的工作原理可以分为三个阶段:握手、数据传输和断开连接。
- 握手:客户端发起 WebSocket 连接时,通过向服务器发送一个特殊的 HTTP 请求头来建立连接。服务器检查请求头中的特定字段,确认支持 WebSocket 协议后,发送特殊的 HTTP 响应头进行握手确认。握手成功后,双方建立了 WebSocket 连接,可以进行后续的数据传输。
- 数据传输:一旦建立了 WebSocket 连接,客户端和服务器可以通过该连接进行双向的实时数据传输。双方可以发送和接收消息,消息以帧的形式进行传输。WebSocket 协议定义了不同类型的帧,如文本帧和二进制帧,用于传输不同类型的数据。
- 断开连接:当连接不再需要时,客户端或服务器可以发起关闭连接的请求。双方会交换特殊的关闭帧,以协商关闭连接,并确保双方都接收到了关闭请求。
9.WebSocket连接的过程
首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;
最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信
10、HTML5 WebSocket 需要后端配合吗
是的,
HTML5 WebSocket 需要后端配合。具体来说,WebSocket 需要在后端实现一个 WebSocket 服务器,该服务器能够接收 WebSocket 请求,并在客户端和服务器之间建立 WebSocket 连接。通常情况下,后端可以使用一些 WebSocket 服务器库(如 Node.js 的 Socket.IO 或 Java 的 Java-WebSocket)来实现 WebSocket 服务器。在客户端,HTML5 提供了 WebSocket API,让客户端能够与服务器建立 WebSocket 连接、发送和接收 WebSocket 消息。因此,HTML5 WebSocket 需要客户端和服务器的配合。
以上原理相关内容结束,以下就是实际使用相关的内容
11.客户端API
WebSocket 构造函数
|
新建 WebSocket 实例:
var ws = new WebSocket('ws://localhost:8080');
|
执行上面语句之后,客户端就会与服务器进行连接。 |
|
| Socket.readyState |
获取实例的当前链接状态 |
- CONNECTING:值为0,表示正在连接。
- OPEN:值为1,表示连接成功,可以通信了。
- CLOSING:值为2,表示连接正在关闭。
- CLOSED:值为3,表示连接已经关闭,或者打开连接失败。
|
示例:
switch (ws.readyState) {
case WebSocket.CONNECTING:
|
webSocket.onopen
|
用于指定连接成功后的回调函数
|
ws.onopen = function () {
ws.send('Hello Server!');
}
如果要指定多个回调函数,可以使用addEventListener方法。
ws.addEventListener('open', function (event) {
ws.send('Hello Server!');
});
|
|
webSocket.onclose
|
用于指定连接关闭后的回调函数 |
ws.onclose = function(event) {
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;
|
|
webSocket.onmessage
|
用于指定收到服务器数据后的回调函数 |
ws.onmessage = function(event) {
var data = event.data;
注意,服务器数据可能是文本,也可能是二进制数据(blob对象或Arraybuffer对象)。
ws.onmessage = function(event){
if(typeof event.data === String) {
console.log("Received data string");
}
if(event.data instanceof ArrayBuffer){
var buffer = event.data;
console.log("Received arraybuffer");
}
}
除了动态判断收到的数据类型,也可以使用binaryType属性,显式指定收到的二进制数据类型。
|
|
webSocket.send()
|
向服务器发送数据 |
发送文本的例子。
ws.send('your message');
发送 Blob 对象的例子。
var file = document
.querySelector('input[type="file"]')
.files[0];
ws.send(file);
发送 ArrayBuffer 对象的例子。
|
|
webSocket.bufferedAmount
|
表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。 |
var data = new ArrayBuffer(10000000);
socket.send(data);
if (socket.bufferedAmount === 0) {
|
|
webSocket.onerror
|
用于指定报错时的回调函数 |
socket.onerror = function(event) {
|
|
代码实例:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
<script type="text/javascript">
function WebSocketTest()
{
if ("WebSocket" in window)
{
alert("您的浏览器支持 WebSocket!");
// 打开一个 web socket
var ws = new WebSocket("ws://localhost:9998/echo");
ws.onopen = function()
{
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send("发送数据");
alert("数据发送中...");
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
alert("数据已接收...");
};
ws.onclose = function()
{
// 关闭 websocket
alert("连接已关闭...");
};
}
else
{
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}
</script>
</head>
<body>
<div id="sse">
<a href="javascript:WebSocketTest()">运行 WebSocket</a>
</div>
</body>
</html>
12.服务端的实现
常用的 Node 实现有以下三种。
- µWebSockets
- Socket.IO
- WebSocket-Node
13.应用场景
基于websocket的事实通信的特点,其存在的应用场景大概有:
- 即时聊天通信
- 多玩家游戏
- 在线协同编辑/编辑
- 实时数据流的拉取与推送
- 体育/游戏实况
- 实时地图位置
- 即时
Web应用程序:即时Web应用程序使用一个Web套接字在客户端显示数据,这些数据由后端服务器连续发送。在WebSocket中,数据被连续推送/传输到已经打开的同一连接中,这就是为什么WebSocket更快并提高了应用程序性能的原因。 例如在交易网站或比特币交易中,这是最不稳定的事情,它用于显示价格波动,数据被后端服务器使用Web套接字通道连续推送到客户端。
- 游戏应用程序:在游戏应用程序中,你可能会注意到,服务器会持续接收数据,而不会刷新用户界面。屏幕上的用户界面会自动刷新,而且不需要建立新的连接,因此在
WebSocket游戏应用程序中非常有帮助。
- 聊天应用程序:聊天应用程序仅使用
WebSocket建立一次连接,便能在订阅户之间交换,发布和广播消息。它重复使用相同的WebSocket连接,用于发送和接收消息以及一对一的消息传输。
不能使用WebSocket的场景
如果我们需要通过网络传输的任何实时更新或连续数据流,则可以使用WebSocket。如果我们要获取旧数据,或者只想获取一次数据供应用程序使用,则应该使用HTTP协议,不需要很频繁或仅获取一次的数据可以通过简单的HTTP请求查询,因此在这种情况下最好不要使用WebSocket。
14.websocket 断线重连
1.如何判断在线离线?
概述:对两次请求的时间差与指定时间进行比较
- 当客户端第一次发送请求至服务端时会携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果不存在就存入db或者缓存中,
- 第二次客户端定再次发送请求依旧携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果存在就把上次的时间戳拿取出来,使用当前时间戳减去上次的时间,
- 得出的毫秒秒数判断是否大于指定的时间,若小于的话就是在线,否则就是离线;
2.如何解决断线问题
通过查阅资料了解到 nginx 代理的 websocket 转发,无消息连接会出现超时断开问题。网上资料提到解决方案两种,一种是修改nginx配置信息,第二种是websocket发送心跳包。
断线和重连
-
断线的可能原因1:websocket超时没有消息自动断开连接,应对措施:
这时候我们就需要知道服务端设置的超时时长是多少,在小于超时时间内发送心跳包,有两种方案:一种是客户端主动发送上行心跳包,另一种方案是服务端主动发送下行心跳包。
心跳包一般来说都是在逻辑层发送空的echo包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。
心跳检测步骤:
- 客户端每隔一个时间间隔发生一个探测包给服务器
- 客户端发包时启动一个超时定时器
- 服务器端接收到检测包,应该回应一个包
- 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
- 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
// 前端解决方案:心跳检测
var heartCheck = {
timeout: 30000, //30秒发一次心跳
timeoutObj: null,
serverTimeoutObj: null,
reset: function(){
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: function(){
var self = this;
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
ws.send("ping");
console.log("ping!")
self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
ws.close(); //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
}, self.timeout);
}, this.timeout);
}
}
-
断线的可能原因2:websocket异常包括服务端出现中断,交互切屏等等客户端异常中断等等
当若服务端宕机了,客户端怎么做?服务端再次上线时怎么做?
- 客户端则需要断开连接,通过onclose 关闭连接
- 服务端再次上线时则需要清除之间存的数据,若不清除 则会造成只要请求到服务端的都会被视为离线。
针对这种异常的中断解决方案就是处理重连,下面我们给出的重连方案是使用js库处理:引入reconnecting-websocket.min.js,ws建立链接方法使用js库api方法:
var ws = new ReconnectingWebSocket(url);
// 断线重连:
reconnectSocket(){
if ('ws' in window) {
ws = new ReconnectingWebSocket(url);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(url);
} else {
ws = new SockJS(url);
}
}
断网监测支持使用js库:offline.min.js
onLineCheck(){
Offline.check();
console.log(Offline.state,'---Offline.state');
console.log(this.socketStatus,'---this.socketStatus');
if(!this.socketStatus){
console.log('网络连接已断开!');
if(Offline.state === 'up' && websocket.reconnectAttempts > websocket.maxReconnectInterval){
window.location.reload();
}
reconnectSocket();
}else{
console.log('网络连接成功!');
websocket.send("heartBeat");
}
}
// 使用:在websocket断开链接时调用网络中断监测
websocket.onclose => () {
onLineCheck();
};
补充概念知识:
心跳是什么?
心跳是 websocket 中的一个概念,就是在 websocket 中用定时器,定时向服务器发送一个消息内容,我们约定,如果后端收到这个消息内容,就要向前端回一个消息内容。如果前端没有收到消息回复,则有可能 websocket 断掉了,那么就要进行重连操作,来保证我们的 websocket 处于连接状态。
let url = `${process.env['VUE_APP_WEBSOCKET']}/websocket`
let ws = new WebSocket(url)
ws.addEventListener('open', e => {
console.log('长连接连接成功')
// 执行心跳方法
dispatch('wsHeartStart')
})
websocket 链接成功以后,开启心跳方法。心跳功能的实现如下:
const state = {
ws: null,
// 心跳时间(s)
wsTimeout: 20,
// 等待心跳响应时间(s),等待心跳的响应时间要大于心跳时间5s以上
waitHeartTime: 25
}
// 开启心跳
wsHeartStart({ state, dispatch }) {
timer.wsTimeoutObj = setTimeout(() => {
if(state.ws && state.ws.readyState == 1) {
state.ws.send('')
} else {
dispatch('handlerWSError')
}
}, state.wsTimeout * 1000)
timer.serverTimeoutObj = setTimeout(() => {
console.log('接收心跳异常!')
dispatch('handlerWSError')
}, state.waitHeartTime * 1000)
}
上面这个代码片段,描述了心跳的基本意思;就是用定时器去向服务端发送一个空的字符串,我们与后端约定,当后端 ws 收到空字符串的时候,要回一个字符串。如果我们没有收到,则判断为 接收心跳异常,准备重连机制。
来源:https://www.cnblogs.com/meiyanstar/p/18345121 |