最近在項目中使用了WebSocket
進行長連接通信,本文就簡要的記錄一下所用到的東西。
首先我們先學(xué)習(xí)幾個概念:
長連接:
一個連接上可以連續(xù)發(fā)送多個數(shù)據(jù)包摔吏,在連接期間,如果沒有數(shù)據(jù)包發(fā)送纵装,需要雙方發(fā)鏈路檢查包征讲。
TCP/IP:
TCP/IP屬于傳輸層,主要解決數(shù)據(jù)在網(wǎng)絡(luò)中的傳輸問題橡娄,只管傳輸數(shù)據(jù)诗箍。但是那樣對傳輸?shù)臄?shù)據(jù)沒有一個規(guī)范的封裝、解析等處理挽唉,使得傳輸?shù)臄?shù)據(jù)就很難識別滤祖,所以才有了應(yīng)用層協(xié)議對數(shù)據(jù)的封裝筷狼、解析等,如HTTP協(xié)議匠童。
HTTP:
HTTP是應(yīng)用層協(xié)議埂材,封裝解析傳輸?shù)臄?shù)據(jù)。從HTTP1.1開始其實就默認開啟了長連接俏让,也就是請求header中看到的Connection:Keep-alive
楞遏。但是這個長連接只是說保持了(服務(wù)器可以告訴客戶端保持時間Keep-Alive:timeout=200;max=20;)這個TCP通道,直接Request - Response首昔,而不需要再創(chuàng)建一個連接通道寡喝,做到了一個性能優(yōu)化。但是HTTP通訊本身還是Request - Response勒奇。
socket:
與HTTP不一樣预鬓,socket不是協(xié)議,它是在程序?qū)用嫔蠈鬏攲訁f(xié)議(可以主要理解為TCP/IP)的接口封裝赊颠。
我們知道傳輸層的協(xié)議格二,是解決數(shù)據(jù)在網(wǎng)絡(luò)中傳輸?shù)模敲磗ocket就是傳輸通道兩端的接口竣蹦。所以對于前端而言顶猜,socket也可以簡單的理解為對TCP/IP的抽象協(xié)議。
WebSocket:
WebSocket是包裝成了一個應(yīng)用層協(xié)議作為socket,從而能夠讓客戶端和遠程服務(wù)端通過web建立全雙工通信痘括。websocket提供ws和wss兩種URL方案长窄。
WebSocket API
接下來我們再來看看WebSocket
的API。首先我們需要做的就是創(chuàng)建一個實例:
const wsUri = 'ws://zhhy-screen.ums86.com/webSocket/memberId/pc';
let websocket = new WebSocket(wsUri);//創(chuàng)建WebSocket
因為項目中要涉及到重連機制纲菌,所以就使用了ReconnectingWebSocket這個插件挠日,用法也十分簡單,在需要創(chuàng)建ws實例的頁面引入插件:
import ReconnectingWebSocket from './js/reconnecting-websocket.min'
// 具體的路徑根據(jù)具體項目而定
let websocket = new ReconnectingWebSocket(wsUri);
// 然后直接將創(chuàng)建實例的語句寫成這樣就可以了翰舌。
WebSocket 事件
- open
該事件是在服務(wù)器相應(yīng)WebSocket連接請求觸發(fā)嚣潜。
websocket.onopen = (evt) => {
let val = 'message';
websocket.send(JSON.stringify(val));
};
- message
該事件是服務(wù)器有響應(yīng)信息的時候觸發(fā)。
websocket.onmessage = (evt) => {
console.log(evt.data);
};
//實際項目中就是在此事件中獲取返回的數(shù)據(jù)的椅贱。
- error
該事件是出錯時觸發(fā)懂算,并且會關(guān)閉連接。
websocket.onerror = (evt) => {
console.log('error');
}
- close
連接關(guān)閉時觸發(fā)庇麦,這在兩端都可以關(guān)閉犯犁。另外如果連接失敗也是會觸發(fā)的。
websocket.onclose = function(evt) {
console.log("Connection closed.");
};
WebSocket 方法
- send()
該方法用來給服務(wù)端發(fā)送消息女器。需要注意的是,必須在與服務(wù)端成功建立連接后才可以發(fā)送消息住诸,所以一般這樣處理:
if (websocket.readyState === WebSocket.OPEN) {
websocket.send(JSON.stringify(val));
}
- close()
該方法用來主動關(guān)閉連接驾胆。
websocket.close();
上面出現(xiàn)的readyState
就是ws實例的當前狀態(tài)涣澡,一共有4種:
- CONNECTING:值為0,表示正在連接丧诺。
- OPEN:值為1入桂,表示連接成功,可以通信了驳阎。
- CLOSING:值為2抗愁,表示連接正在關(guān)閉。
- CLOSED:值為3呵晚,表示連接已經(jīng)關(guān)閉蜘腌,或者打開連接失敗。
所以可以根據(jù)websocket.readyState === WebSocket.OPEN
來判斷是否連接成功饵隙。
下面我們再來看看建立ws實例的時候撮珠,瀏覽器的請求和相應(yīng):
Request URL:ws://zhhy-screen.ums86.com/webSocket/memberId/pc
Request Method:GET
Status Code:101 //狀態(tài)碼為101表示切換協(xié)議
Response Headers
Connection:upgrade
Content-Type:application/octet-stream
Date:Fri, 08 Dec 2017 02:20:11 GMT
Sec-WebSocket-Accept:M2OEhXpd4zqIkjEQtUsEr0ZSJWs= //服務(wù)器端將加密處理后的握手Key通過這個字段返回給客戶端表示服務(wù)器同意握手建立連接。
Sec-WebSocket-Extensions:permessage-deflate;client_max_window_bits=15
Server:Flaginfo_Web
ufe-result:A2
Upgrade:websocket
Request Headers
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.9,en;q=0.8,fr;q=0.7
Cache-Control:no-cache
Connection:Upgrade //告知服務(wù)器當前請求連接是升級的金矛。
Cookie:_qddaz=QD.f47rqu.5a3mle.j9tqibli
Host:zhhy-screen.ums86.com
Origin:http://localhost:8083
Pragma:no-cache
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
Sec-WebSocket-Key:jo9tTVEKTIC4UXR4HZpZMQ== //為了表示服務(wù)器同意和客戶端進行Socket連接芯急,服務(wù)器端需要使用客戶端發(fā)送的這個Key進行校驗,然后返回一個校驗過的字符串給客戶端驶俊,客戶端驗證通過后才能正式建立Socket連接娶耍。
Sec-WebSocket-Version:13 //版本,必須是13
Upgrade:websocket //告訴服務(wù)器這個HTTP連接是升級的Websocket連接饼酿。
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 //客戶端的瀏覽器信息榕酒,可以根據(jù)此字段判斷瀏覽器版本和客戶端類型
以上就是所掌握的所有websocket知識,同時項目中還涉及到了PC的不同頁面主動向App推送消息嗜湃,其中就用到了VUEX(項目是使用VUE框架進行開發(fā))進行消息的監(jiān)聽與管理奈应,不同頁面通過commit消息this.$store.commit('sendData',data);
,創(chuàng)建ws實例的頁面進行監(jiān)聽购披,監(jiān)聽到消息的變化后杖挣,就通過ws的send()
方法來向app推送消息,服務(wù)器端就起到一個中間人的作用刚陡。
參考文章:WebSocket探秘