WebSocket 是一種網絡通訊協議,不少高級功能都須要它。初次接觸 WebSocket 的人,都會問一樣的問題:咱們已經有了 HTTP 協議,爲何還須要另外一個協議?它能帶來什麼好處? 答案很簡單,由於 HTTP 協議有一個缺陷:通訊只能由客戶端發起。 舉例來講,咱們想了解今天的天氣,只能是客戶端向服務器發出請求,服務器返回查詢結果。HTTP 協議作不到服務器主動向客戶端推送信息。 這種單向請求的特色,註定了若是服務器有連續的狀態變化,客戶端要獲知就很是麻煩。咱們只能使用"輪詢":每隔一段時候,就發出一個詢問,瞭解服務器有沒有新的信息。最典型的場景就是聊天室。輪詢的效率低,很是浪費資源(由於必須不停鏈接,或者 HTTP 鏈接始終打開)。所以,工程師們一直在思考,有沒有更好的方法。WebSocket 就是這樣發明的。javascript
它的最大特色就是,服務器能夠主動向客戶端推送信息,客戶端也能夠主動向服務器發送信息,是真正的雙向平等對話,屬於服務器推送技術的一種。前端
其餘特色包括:java
(1)創建在 TCP 協議之上,服務器端的實現比較容易。web
(2)與 HTTP 協議有着良好的兼容性。默認端口也是80和443,而且握手階段採用 HTTP 協議,所以握手時不容易屏蔽,能經過各類 HTTP 代理服務器。後端
(3)數據格式比較輕量,性能開銷小,通訊高效。瀏覽器
(4)能夠發送文本,也能夠發送二進制數據。服務器
(5)沒有同源限制,客戶端能夠與任意服務器通訊。websocket
(6)協議標識符是ws
(若是加密,則爲wss
),服務器網址就是 URL。網絡
var ws = new WebSocket("ws://echo.websocket.org");
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};
ws.onclose = function(evt) {
console.log("Connection closed.");
}
複製代碼
webSocket構造函數socket
let ws = new WebSocket('ws://localhost:8080');
複製代碼
執行上面語句以後,客戶端就會與服務器進行鏈接。
webSocket的狀態(readyState)
readyState屬性返回實例對象的當前狀態,總共有四種狀態。
CONNECTING:值爲0, 正在鏈接
OPEN:值爲1,鏈接成功
CLOSING:值爲2,鏈接正在關閉
CLOSED:值爲3,鏈接已經關閉
複製代碼
websocket.onopen(鏈接成功以後的回調函數)
實例對象的onopen
屬性,用於指定鏈接成功後的回調函數。
ws.onopen = function () {
ws.send('Hello Server!');
}
複製代碼
若是要指定多個回調函數,可使用addEventListener
方法。
ws.addEventListener('open', function (event) {
ws.send('Hello Server!');
});
複製代碼
websocket.onclose(關閉以後調用的方法)
實例對象的onclose
屬性,用於指定鏈接關閉後的回調函數。
ws.onclose = function(event) {
console.log('onclose')
}
複製代碼
若是要指定多個回調函數,可使用addEventListener
方法。
ws.addEventListener("close", function(event) {
console.log('onclose')
});
複製代碼
websocket.onmessage(接受到服務器數據以後的回調函數)
ws.onmessage = function(event) {
// 獲取數據event.data
var data = event.data;
// 處理數據
};
複製代碼
websocket.send(向服務器端發送數據)
ws.send('your message')
複製代碼
websocket.onerror(報錯時調用的方法)
ws.onerror = function(event) {
// handle error event
};
複製代碼
websocket是先後端交互的長鏈接,先後端也均可能由於一些狀況致使鏈接失效而且相互之間沒有反饋提醒。所以爲了保證鏈接的可持續性和穩定性,websocket心跳重連就應運而生。在使用原生websocket的時候,若是設備網絡斷開,不會馬上觸發websocket的任何事件,前端也就沒法得知當前鏈接是否已經斷開。這個時候若是調用websocket.send方法,瀏覽器纔會發現連接斷開了,便會馬上或者必定短期後(不一樣瀏覽器或者瀏覽器版本可能表現不一樣)觸發onclose函數。後端websocket服務也可能出現異常,形成鏈接斷開,這時前端也並無收到斷開通知,所以須要前端定時發送心跳消息ping,後端收到ping類型的消息,立馬返回pong消息,告知前端鏈接正常。若是必定時間沒收到pong消息,就說明鏈接不正常,前端便會執行重連。爲了解決以上兩個問題,之前端做爲主動方,定時發送ping消息,用於檢測網絡和先後端鏈接問題。一旦發現異常,前端持續執行重連邏輯,直到重連成功。
通常的心跳檢測的函數:
// 心跳檢測, 每隔一段時間檢測鏈接狀態,若是處於鏈接中,就向server端主動發送消息,來重置server端與客戶端的最大鏈接時間,若是已經斷開了,發起重連。
let heartCheck = {
// 心跳,比server端設置的鏈接時間稍微小一點,在接近斷開的狀況下以通訊的方式去重置鏈接時間。
timeout: 100000,
serverTimeoutObj: null,
reset: function() {
clearTimeout(this.serverTimeoutObj)
return this
},
start: function() {
this.serverTimeoutObj = window.setInterval(() => {
if (websocket.readyState === 1) {
websocket.send('ping')
} else {
window.clearTimeout(this.serverTimeoutObj)
// 處理邏輯:重連或者其餘
}
}, this.timeout)
}
}
複製代碼
function newWebSocket(option) {
console.log('new webSocket.....')
let websocket = null
// 判斷當前環境是否支持websocket
if (window.WebSocket) {
if (!websocket) {
websocket = new WebSocket('你的請求地址')
}
} else {
console.log('not support websocket')
}
// 鏈接成功創建的回調方法
websocket.onopen = function(e) {
// 成功創建鏈接後,重置心跳檢測
heartCheck.reset().start()
console.log('connected successfully')
}
// 鏈接發生錯誤,鏈接錯誤時會繼續嘗試發起鏈接
websocket.onerror = function() {
console.log(`onerror`)
newWebSocket()
}
// 接受到消息的回調方法
websocket.onmessage = function(e) {
console.log('onmessage', e.data)
var message = e.data
if (message) {
// 執行接收到消息的操做
if (option != undefined) {
// 執行傳入對象的方法,傳出消息
option.onmessage(message)
}
}
}
// 接受到服務端關閉鏈接時的回調方法
websocket.onclose = function() {
console.log('onclose')
}
// 監聽窗口事件,當窗口關閉時,主動斷開websocket鏈接,防止鏈接沒斷開就關閉窗口,server端報錯
window.onbeforeunload = () => {
return websocket.close()
}
// 心跳檢測, 每隔一段時間檢測鏈接狀態,若是處於鏈接中,就向server端主動發送消息,來重置server端與客戶端的最大鏈接時間,若是已經斷開了,發起重連。
var heartCheck = {
// 心跳,比server端設置的鏈接時間稍微小一點,在接近斷開的狀況下以通訊的方式去重置鏈接時間。
timeout: 100000,
serverTimeoutObj: null,
reset: function() {
clearTimeout(this.serverTimeoutObj)
return this
},
start: function() {
this.serverTimeoutObj = window.setInterval(() => {
if (websocket.readyState === 1) {
websocket.send('ping')
} else {
console.log('websocket stop', websocket.readyState)
window.clearTimeout(this.serverTimeoutObj)
newWebSocket(option)
}
}, this.timeout)
}
}
return websocket
}
複製代碼