一、介绍
WebSocket 是一种网络通信协议,很多高级功能都需要它。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
其他特点包括:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
ws://example.com:80/some/path
1.1、请求流程
WebSocket 不同于TCP的三次握手。 WebSocket是先进行一次HTTP请求,这个请求头不同于普通HTTP请求。然后服务器开始辨认请求头,如果是WebSocket的请求头,则进行普通的TCP连接,即三次握手。如果不是WebSocket的请求头,按普通的HTTP请求处理。
WebSocket请求头(Sec-WebSocket-Key):
GET ws://localhost:12345/websocket/test.html HTTP/1.1
Origin: http://localhost
Connection: Upgrade
Host: localhost:12345
Sec-WebSocket-Key: JspZdPxs9MrWCt3j6h7KdQ== // 这个叫“梦幻字符串”,这个加密规则可以去百度。只有有这个密钥 服务器才能通过解码 认出来,哦~这是个WebSocket的请求,我要建立TCP连接了!!!如果这个字符串没有按照加密规则加密,那服务端就认不出来,就会认为这整个协议就是个HTTP请求。更不会开TCP。其他的字段都可以随便设置,但是这个字段是最重要的字段,标识WebSocket协议的一个字段。
Upgrade: websocket
Sec-WebSocket-Version: 13
服务器回应(Sec-WebSocket-Accept):
HTTP/1.1 101 Web Socket Protocol Handshake
WebSocket-Location: ws://localhost:12345/websocket/test.php
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: zUyzbJdkVJjhhu8KiAUCDmHtY/o= // 这个字符串是要让客户端辨认的,客户端拿到后自动解码。并且辨认是不是一个WebSocket请求。然后进行相应的操作。加密规则百度。
WebSocket-Origin: http://localhost
ps:TCP三次握手:
第一次握手,客户主动(active open)去connect服务器,并且发送SYN 假设序列号为J,服务器是被动打开(passive open);
第二次握手,服务器在收到SYN后,它会发送一个SYN以及一个ACK(应答)给客户,ACK的序列号是 J+1表示是给SYN J的应答,新发送的SYN K 序列号是K;
第三次握手,客户在收到新SYN K, ACK J+1 后,也回应ACK K+1 以表示收到了,然后两边就可以开始数据发送数据了。
二、api
1、构造函数
var ws = new WebSocket('ws://localhost:8080');
执行上面语句之后,客户端就会与服务器进行连接。
实例对象的所有属性和方法清单,参见:https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
2、webSocket.readyState 返回实例对象的当前状态
CONNECTING:值为0,表示正在连接。
OPEN:值为1,表示连接成功,可以通信了。
CLOSING:值为2,表示连接正在关闭。
CLOSED:值为3,表示连接已经关闭,或者打开连接失败。
3、webSocket.onopen 指定连接成功后的回调函数
ws.onopen = function () {
ws.send('Hello Server!');
}
//如果要指定多个回调函数,可以使用addEventListener方法。
ws.addEventListener('open', function (event) {
ws.send('Hello Server!');
});
4、webSocket.onclose 指定连接关闭后的回调函数。同上
5、webSocket.onerror 指定报错时的回调函数。同上
6、 webSocket.onmessage 指定收到服务器数据后的回调函数。同上
注意,服务器数据可能是文本,也可能是二进制数据(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属性,显式指定收到的二进制数据类型。
// 收到的是 blob 数据
ws.binaryType = "blob";
ws.onmessage = function(e) {
console.log(e.data.size);
};
// 收到的是 ArrayBuffer 数据
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
console.log(e.data.byteLength);
};
7、webSocket.send() 向服务器发送数据
//发送文本对象的例子。
ws.send('your message');
//发送 Blob 对象的例子。
var file = document
.querySelector('input[type="file"]')
.files[0];
ws.send(file);
//发送 ArrayBuffer 对象的例子。
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
ws.send(binary.buffer);
8、webSocket.bufferedAmount 判断发送是否结束(还有多少字节的二进制数据没有发送出去)
var data = new ArrayBuffer(10000000);
socket.send(data);
if (socket.bufferedAmount === 0) {
// 发送完毕
} else {
// 发送还没结束
}
标题三、简单使用
1、vue
//连接websocket
connectWebsocket(){
let _this = this
let webSocket = new WebSocket(this.$httpUrl.ws)
webSocket.onopen = () => {
console.log('已连接')
webSocket.onmessage = e => {
let content = JSON.parse(e.data)
console.log(content)
}
}
webSocket.onclose = () => {
let _timer = setTimeout(()=>{
console.log('正在重新连接')
_this.connectWebsocket()
clearTimeout(_timer)
}, 2000)
}
webSocket.onerror = () => {
console.log('网络不畅通,请联系管理员')
}
}
全局websocket
//main.js
Vue.prototype.ws = new WebSocket(Httpurl.ws)
//app.vue
webSk() {
let that = this;
// let webSocket = new WebSocket(this.$httpApi.ws);
this.ws.onopen = () => {
console.log("已连接");
// this.ws.onmessage = e => {
// let obj = JSON.parse(e.data);
// console.log(obj);
// }
};
this.ws.onclose = () => {
console.log('已关闭连接');
let _timer = setTimeout(()=>{
console.log('正在重新连接')
that.webSk()
clearTimeout(_timer)
}, 2000)
};
this.ws.onerror = () => {
console.log("网络不畅通,请联系管理员");
};
},
//任意页面
let data={
key:'Compute_Range',
data:{
startTime: that.trackForm.startTime,
endTime: that.trackForm.endTime
}
}
this.ws.send(JSON.stringify(data))
2、uni-app
webSk() {
let that = this;
let socketTask = uni.connectSocket({
url: this.$lyApi.ws, //仅为示例,并非真实接口地址。
header: {
'content-type': 'application/json'
},
method: 'GET',
success: ()=> {}
});
socketTask.onOpen(function(){
console.log('连接已打开');
socketTask.onMessage(function(res){
let obj = JSON.parse(res.data);
}
})
socketTask.onClose(function(){
console.log('连接已关闭');
let _timer = setTimeout(()=>{
console.log('正在重新连接')
that.webSk()
clearTimeout(_timer)
}, 2000)
})
socketTask.onError(function(){
that.$common.errorToShow('网络不畅通,请联系管理员');
})
},
四、心跳包
在实际项目中,别让这种长连接一直保持,可以设置连接无交流超时断开,大概设置10分钟左右,然后每8分钟定时发送一条心跳,具体想法就看你们喽~
webSk() {
let that = this;
let webSocket = null;
let heartFlag = false; // webSocket连接成功标识
let tryTime = 0; // 尝试连接次数
initWs();
// 心跳函数,每8分钟执行一次
function heart(){
heartFlag && webSocket.send("keep connection"); // 发送一条心跳
const timer = setTimeout(() => {
heart();
clearTimeout(timer);
}, 8 * 60 * 1000);
}
function initWs(){
webSocket = new WebSocket(that.$httpUrl.lotteryws);
webSocket.onopen = () => {
console.log('websocket连接成功')
heartFlag = true
tryTime = 0 // 连接成功,尝试次数设为0
heart()
}
// 接收信息
webSocket.onmessage = function(e) {
let obj = JSON.parse(e.data);
console.log("obj",obj)
};
// 连接关闭
webSocket.onclose = () => {
console.log('websocket已关闭连接');
heartFlag = false;
if(tryTime < 10){ // 连接关闭尝试重新连接,尝试超过10次提示错误。
const _timer = setTimeout(() => {
window.console.log('websocket正在重新连接');
webSocket = null;
tryTime++;
initWs();
clearTimeout(_timer)
}, 2000)
}else{
window.console.log('websocket重连失败,请刷新页面重试')
}
};
// 连接错误
webSocket.onerror = () => heartFlag = false
}
},
五、待求证