摘要:服務端確認協議版本,升級為協議。自己寫了一個例子,服務端在開始連接后,利用定時器主動向客戶端發送隨機數,客戶端也可以發給服務器消息,然后服務器返回這條消息給客戶端。
寫在前面
webSocket是一項可以讓服務器將數據主動推送給客戶端的技術。前幾天寫了一個日志功能,日志數據需要實時更新。正好項目中有封裝好的WebSocket組件,且接口支持webSocket,就用它實現了。也是第一次用,簡單研究了一下,分享出來。
文章示例代碼:https://github.com/neroneroff...
首先需要明白webSocket的概念,下邊是維基百科的解釋
WebSocket是一種通信協議,可在單個TCP連接上進行全雙工通信。WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,
允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就可以建立持久性的連接,
并進行雙向數據傳輸。
首先,要明白WebSocket是一種通信協議,區別于HTTP協議,HTTP協議只能實現客戶端請求,服務端響應的這種單項通信。
而WebSocket可以實現客戶端與服務端的雙向通訊,說白了,最大也是最明顯的區別就是可以做到服務端主動將消息推送給客戶端。
其余的特點有:
握手階段采用 HTTP 協議。
數據格式輕量,性能開銷小。客戶端與服務端進行數據交換時,服務端到客戶端的數據包頭只有2到10字節,客戶端到服務端需要加上另外4字節的掩碼。
HTTP每次都需要攜帶完整頭部。
更好的二進制支持,可以發送文本,和二進制數據
沒有同源限制,客戶端可以與任意服務器通信
協議標識符是ws(如果加密,則是wss),請求的地址就是后端支持websocket的API。
幾種與服務端實時通信的方法我們都知道,不使用WebSocket與服務器實時交互,一般有兩種方法。AJAX輪詢和Long Polling長輪詢。
AJAX輪詢AJAX輪詢是定時發送請求,也就是普通的客戶端與服務端通信過程,只不過是無限循環發送,這樣,可以保證服務端一旦有最新消息,就可以被客戶端獲取。
Long Polling長輪詢Long Polling長輪詢是客戶端和瀏覽器保持一個長連接,等服務端有消息返回,斷開。
然后再重新連接,也是個循環的過程,無窮盡也。。。
客戶端發起一個Long Polling,服務端如果沒有數據要返回的話,
會hold住請求,等到有數據,就會返回給客戶端。客戶端又會再次發起一次Long Polling,再重復一次上面的過程。
上邊這兩種方式都有個致命的弱點,開銷太大,被動性。假設并發很高的話,這對服務端是個考驗。
而WebSocket一次握手,持久連接,以及主動推送的特點可以解決上邊的問題,又不至于損耗性能。
客戶端發起HTTP握手,告訴服務端進行WebSocket協議通訊,并告知WebSocket協議版本。服務端確認協議版本,升級為WebSocket協議。之后如果有數據需要推送,會主動推送給客戶端。
連接開始時,客戶端使用HTTP協議和服務端升級協議,升級完成后,后續數據交換遵循WebSocket協議。我們看看Request Headers
Accept-Encoding: gzip, deflate, br Accept-Language: zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6 Cache-Control: no-cache Connection: Upgrade Host: 127.0.0.1:3000 Origin: http://localhost:3000 Pragma: no-cache Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits Sec-WebSocket-Key: bwb9SFiJONXhQ/A4pLaXIg== Sec-WebSocket-Version: 13 Upgrade: websocket
重點字段是這些:
Connection: Upgrade 表示要升級協議
Upgrade: websocket 要升級協議到websocket協議
Sec-WebSocket-Version 表示websocket的版本。如果服務端不支持該版本,需要返回一個Sec-WebSocket-Versionheader,里面包含服務端支持的版本號。
Sec-WebSocket-Key 對應服務端響應頭的Sec-WebSocket-Accept,由于沒有同源限制,websocket客戶端可任意連接支持websocket的服務。這個就相當于一個鑰匙一把鎖,避免多余的,無意義的連接。
再看看看服務端響應的 Response Headers
Connection: Upgrade Sec-WebSocket-Accept: 2jrbCWSCPlzPtxarlGTp4Y8XD20= Upgrade: websocket
關鍵是這個字段
Sec-WebSocket-Accept: 用來告知服務器愿意發起一個websocket連接, 值根據客戶端請求頭的Sec-WebSocket-Key計算出來
WebSocket API客戶端若想要與支持webScoket的服務器通信,可以使用WebSocket構造函數返回WebSocket對象。
const ws = new WebSocket("ws://localhost:3000/websocket");
這樣,客戶端就會與服務端開始連接。
返回的實例對象的屬性:
WebSocket.onopen: 連接成功后的回調
WebSocket.onclose: 連接關閉后的回調
WebSocket.onerror: 連接失敗后的回調
WebSocket.onmessage: 客戶端接收到服務端數據的回調
webSocket.bufferedAmount: 未發送至服務器的二進制字節數
WebSocket.binaryType: 使用二進制的數據類型連接
WebSocket.protocol : 服務器選擇的下屬協議
WebSocket.url : WebSocket 的絕對路徑
WebSocket.readyState: 當前連接狀態,對應的四個常量
名稱 | 值 |
---|---|
WebSocket.CONNECTING | 0 |
WebSocket.OPEN | 1 |
WebSocket.CLOSING | 2 |
WebSocket.CLOSED | 3 |
方法:
WebSocket.close() 關閉當前連接
WebSocket.send(data) 向服務器發送數據
示例講了那么多概念以后,終于可以看看怎么用了。實現WebSocket通信,需要客戶端和服務端配合。
自己寫了一個例子,服務端在開始連接后,利用定時器主動向客戶端發送隨機數,客戶端也可以發給服務器消息,
然后服務器返回這條消息給客戶端。客戶端就是js+html,服務端用了express + express-ws來實現。
代碼在這里:https://github.com/neroneroff... 可以clone下來,安裝依賴,npm start運行看下效果。
前端頁面,最終效果如以上效果圖:
服務端返回的消息
js,使用webSocket的代碼都在這里。做的事情就是給頁面的元素綁定事件。
然后創建WebSocket對象,監聽對象的連接、接收消息、關閉等事件,將數據反饋到頁面中
const msgBox = document.getElementById("msg-need-send") const sendBtn = document.getElementById("send-btn") const exit = document.getElementById("exit") const receiveBox = document.getElementById("receive-box") // 創建一個webSocket對象 const ws = new WebSocket("ws://127.0.0.1:3000/websocket/test") ws.onopen = e => { // 連接后監聽 console.log(`WebSocket 連接狀態: ${ws.readyState}`) } ws.onmessage = data => { // 當服務端返回數據的時候,放到頁面里 receiveBox.innerHTML += `服務端${data.data}
` receiveBox.scrollTo({ top: receiveBox.scrollHeight, behavior: "smooth" }) } ws.onclose = data => { // 監聽連接關閉 console.log("WebSocket連接已關閉") console.log(data); } sendBtn.onclick = () => { // 點擊發送按鈕。將數據發送給服務端 ws.send(msgBox.value) } exit.onclick = () => { // 客戶端主動關閉連接 ws.close() }
考慮到了模塊化開發,沒有直接把代碼放到直接創建服務的文件中。而是使用了路由,給webSocket服務分配一個多帶帶的接口
const express = require("express"); const expressWs = require("express-ws") const router = express.Router() expressWs(router); router.ws("/test", (ws, req) => { ws.send("連接成功") let interval // 連接成功后使用定時器定時向客戶端發送數據,同時要注意定時器執行的時機,要在連接開啟狀態下才可以發送數據 interval = setInterval(() => { if (ws.readyState === ws.OPEN) { ws.send(Math.random().toFixed(2)) } else { clearInterval(interval) } }, 1000) // 監聽客戶端發來的數據,直接將信息原封不動返回回去 ws.on("message", msg => { ws.send(msg) }) }) module.exports = router
最后看一下數據交互的過程
總結上邊簡單實現了一個webSocket通信。實際的東西還有很多,比如webSocket擴展,心跳檢測,數據加密,身份認證等知識點。但自己也需要再去研究,所以先不做介紹了。
相關文章WebSocket協議:5分鐘從入門到精通
WebSocket 教程--阮一峰
WebSocket API
express-ws文檔
歡迎關注我的公眾號: 一口一個前端,不定期分享我所理解的前端知識
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105858.html
摘要:就是為了解決這一問題產生的,現在已經寫入標準,主流瀏覽器基本支持。 由于最近寫項目要使用socekt.io技術,于是研究了一段時間,把自己早期學習階段寫的小游戲改造了一下,變成了一個比較完整的小程序。點擊這里可以體驗游戲,建議使用手機模式查看,也可以下載打包好的webapp,安卓版已上架酷安市場,掃碼可下載體驗: showImg(https://segmentfault.com/img...
摘要:原文從零到一,擼一個在線斗地主下篇作者上篇回顧我們說了斗地主游戲的渲染展示部分,最后也講了下中交互的情況,下篇的重點就是游戲邏輯。 原文:從零到一,擼一個在線斗地主(下篇) | AlloyTeam作者:TAT.vorshen 上篇回顧:我們說了斗地主游戲的渲染展示部分,最后也講了下canvas中交互的情況,下篇的重點就是游戲邏輯。 邏輯主要分成兩塊:流程邏輯和撲克牌對比邏輯。 gith...
摘要:運行得十分好,總是使用并且返回消息。這個問題的提出意味著通過實施你自己的函數來使用原套,從回應到讀取。額外的緩沖是因為請求使用的是原始套接字的生成文件方法從中讀取數據。手動進行所以如何從使用通過自己發出請求和處理響應。 Kubernetes有一個之前系統用來做很多工作的REST-ish HTTP API。這個API是開放的,而且文檔十分齊全,很容易整合,可以從代碼方面管理集群。然而這個...
摘要:該區域代表可以被所控制的畫布。那么現在第二個問題,識別該文檔,這或許不是大部分用戶的需求,但小部分用戶并不意味著人數少。因此一個基于的請求于標準內提出。 前言 作為程序員,技術的落實與鞏固是必要的,因此想到寫個系列,名為 why what or how 每篇文章試圖解釋清楚一個問題。 這次的 why what or how 主題:現在幾乎所有人都知道了 HTML5 ,那么 H5 到底相...
閱讀 591·2021-11-15 11:38
閱讀 1174·2021-10-11 10:59
閱讀 3491·2021-09-07 09:58
閱讀 479·2019-08-30 15:44
閱讀 3518·2019-08-28 18:14
閱讀 2599·2019-08-26 13:32
閱讀 3514·2019-08-26 12:23
閱讀 2413·2019-08-26 10:59