一、WebSocket是什么?
WebSocket 是一种网络通信协议。RFC6455定义了它的通信标准。
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
二、为什么需要WebSocket?
我们知道,传统的HTTP协议是无状态的,每次请求(request)都要由客户端(如 浏览器)主动发起,服务端进行处理后返回response结果,而服务端很难主动向客户端发送数据;这种客户端是主动方,服务端是被动方的传统Web模式 对于信息变化不频繁的Web应用来说造成的麻烦较小,而对于涉及实时信息的Web应用却带来了很大的不便。
因此,随着HTML5的诞生,一种新的通信协议应运而生---WebSocket,他最大的特点就是服务端可以主动向客户端推送消息,客户端也可以主动向服务端发送消息,实现了真正的平等。
举个例子:如带有即时通信、实时数据、订阅推送等功能的应用。在WebSocket规范提出之前,开发人员若要实现这些实时性较强的功能,经常会使用折衷的解决方法:ajax轮询(最原始的实现实时Web应用的解决方案)
ajax轮询的原理:ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。明显地,这种方法会导致过多不必要的请求,浪费流量和服务器资源。
场景再现:
客户端:有没有新信息(Request)
服务端:没有(Response)
客户端:有没有新信息(Request)
服务端:没有。。(Response)
客户端:有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:有没有新消息(Request)
服务端:有啦,给你。(Response)
客户端:有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) ---- loopwebsocket的原理:当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。
场景再现:
客户端:我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:好的,有的时候会告诉你的。
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
服务端:笑死我了哈哈哈哈哈哈哈
三、WebSocket其他特点如下:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws
(如果加密,则为wss
),服务器网址就是 URL。
四、创建WebSocket对象
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。当你获取 WebSocket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
// url, 指定连接的 URL
// protocol 是可选的,指定可接受的子协议。
let Socket = new WebSocket(url, [protocol] );
五、WebSocket属性
假设我们创建了一个Socket对象
Socket.readyState :只读属性 readyState 表示连接状态,可以是以下值:
0 - 表示连接尚未建立。
1 - 表示连接已建立,可以进行通信。
2 - 表示连接正在进行关闭。
3 - 表示连接已经关闭或者连接不能打开。
Socket.bufferedAmount :只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
六、WebSocket事件
假设我们创建了一个Socket对象
| 事件 | 事件处理程序 | 描述 |
| open | Socket.onopen | 连接建立时触发 |
| message | Socket.onmessage | 客户端接收服务端数据时触发 |
| error | Socket.onerror | 通信发生错误时触发 |
| close | Socket.onclose | 连接关闭时触发 |
七、WebSocket方法
假设我们创建了一个Socket对象
Socket.send() 使用连接发送数据
Socket.close() 关闭连接
八、WebSocket实例
【1】原生JavaScript实现WebSocket连接
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<style>
</style>
<body>
<input id="text" type="text" />
<button onclick="send()">发送消息</button>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<div id="message"></div>
<script>
let websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/websocket");
} else {
alert('当前浏览器不支持websocket!')
}
//连接发生错误的回调方法
websocket.onerror = function () {
console.log("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
document.getElementById('message').innerHTML += event.data + '<br/>';
}
//连接关闭的回调方法
websocket.onclose = function () {
console.log("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
}
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send() {
let message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</body>
</html>
【2】在vue项目中实现WebSocket连接
<template>
<div>
<el-input v-model="params" clearable/>
<el-button type="primary" @click="send">发消息</el-button>
</div>
</template>
<script>
export default {
data() {
return {
params: '',
path: "ws://localhost:8080/websocket",
socket: ""
}
},
mounted() {
// 初始化
this.init()
},
destroyed() {
// 销毁监听
this.socket.onclose = this.close
},
methods: {
init: function () {
if (typeof (WebSocket) === "undefined") {
console.log("您的浏览器不支持socket")
} else {
// 实例化socket
this.socket = new WebSocket(this.path)
// 监听socket连接成功回调
this.socket.onopen = this.open
// 监听socket连接失败回调
this.socket.onerror = this.error
// 监听后台返回的socket消息
this.socket.onmessage = this.getMessage
}
},
open: function () {
console.log("socket连接成功")
},
error: function () {
console.log("连接错误")
},
getMessage: function (msg) {
console.log(msg.data)
},
send: function () {
this.socket.send(params)
},
close: function () {
console.log("socket已经关闭")
}
}
}
</script>