聽說小米今天破發(fā)了子姜。作為一個偽米粉慌植,我還是比較談定的驯嘱,如果你的身邊有一個人時長提醒你--蘋果干到小米現(xiàn)在的位置需要50年,小米只需要10幾年為什么;小米負(fù)債千億人民幣...等等謠言陕靠。說了這么多迂尝,小米依舊是小米脱茉,我依舊是哪個普普通通的屌絲程序員。
事件起因:
我在剛開始接觸到項目的時候雹舀,看到一位同事為了保證本地狀態(tài)和遠(yuǎn)端的狀態(tài)芦劣,是用http進(jìn)行輪詢的粗俱,就像下面的代碼一樣:
{
//代碼都是臨時手寫的 可能有不完善的見諒啊
let xmlhttp;
let isFishish = false;
let time = setInterval(()=>{
if(isFishish){
clearInterval(time)
}else{
getHttpRequest();
}
},3000)
function getHttpRequest(){
xmlhttp=null;
if(window.XMLHttpRequest)
{
xmlhttp=new XMLHttpRequest();
}else if (window.ActiveXObject)
{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
if (xmlhttp!=null)
{
xmlhttp.onreadystatechange=state_Change;
xmlhttp.open("GET",url,true);
xmlhttp.send(null);
}
else
{
alert("Your browser does not support XMLHTTP.");
}
}
function state_Change(){
if (xmlhttp.readyState==4)
{
if (xmlhttp.status==200)
{// 200 = OK
// ...our code here...
} else {
alert("Problem retrieving XML data");
}
}
}
}
這種輪詢的方式有好有壞说榆,能夠初步的達(dá)到用戶的需求,但是用戶的數(shù)據(jù)改變卻又不能即使的反饋給用戶寸认。算了就這樣說好像沒什么可塑性签财,就直接用一個生活小實例來描述吧。
- http請求
client:辦公室有沒有吃的偏塞?
server:沒有
client:辦公室還沒有吃的唱蒸?
server:沒有
client:現(xiàn)在有吃的沒?
server:沒有 - socket請求:
client: 我們來建立socket連接吧灸叼,如果你有累點重點的活兒就告訴我
server:求之不得神汹,連接好了
client:有活兒就立馬告訴我
server:好的
server:幫我把這數(shù)據(jù)用Excel表格列出來
client:好的,弄好了
server:謝謝
從上面的例子上面就可以看出socket其實是可以古今,由服務(wù)端發(fā)起主動想客戶端發(fā)送數(shù)據(jù)的屁魏。
介紹一下socket:
前面說了這么多大體都提及了socket,但是具體的socket是什么捉腥?有什么優(yōu)點氓拼?基本都介紹的模棱兩可。現(xiàn)在來系統(tǒng)的介紹一下
- 介紹一下socket
WebSocket是為解決客戶端與服務(wù)端實時通信而產(chǎn)生的技術(shù)抵碟。其本質(zhì)是先通過HTTP/HTTPS協(xié)議進(jìn)行握手后創(chuàng)建一個用于交換數(shù)據(jù)的TCP連接桃漾, - socket的優(yōu)點在哪兒:
以前我們實現(xiàn)推送技術(shù),用的都是輪詢拟逮,在特點的時間間隔有瀏覽器自動發(fā)出請求撬统,將服務(wù)器的消息主動的拉回來,在這種情況下敦迄,我們需要不斷的向服務(wù)器 發(fā)送請求宪摧,然而HTTP request 的header是非常長的,里面包含的數(shù)據(jù)可能只是一個很小的值颅崩,這樣會占用很多的帶寬和服務(wù)器資源几于。會占用大量的帶寬和服務(wù)器資源。
WebSocket API最偉大之處在于服務(wù)器和客戶端可以在給定的時間范圍內(nèi)的任意時刻沿后,相互推送信息沿彭。在建立連接之后,服務(wù)器可以主動傳送數(shù)據(jù)給客戶端尖滚。
此外喉刘,服務(wù)器與客戶端之間交換的標(biāo)頭信息很小瞧柔。
WebSocket并不限于以Ajax(或XHR)方式通信,因為Ajax技術(shù)需要客戶端發(fā)起請求睦裳,而WebSocket服務(wù)器和客戶端可以彼此相互推送信息造锅;
此后服務(wù)端與客戶端通過此TCP連接進(jìn)行實時通信。
我是這樣使用socket的
關(guān)于socket的使用廉邑,我是直接用最基礎(chǔ)的源碼來寫的哥蔚,雖然最后知道了js里面有一個常用的庫,但是想想呢蛛蒙?既然是寫文章就直接用最基礎(chǔ)的東西更能說明問題糙箍。
{
initWebSocket(){ //初始化weosocket
//ws地址
const wsuri = process.env.WS_API + "/websocket/threadsocket";
this.websock = new WebSocket(wsuri);
this.websock.onmessage = this.websocketonmessage
this.websock.onerror = this.websocketerror
this.websock.onopen = this.websocketonopen
this.websock.onclose = this.websocketclose
},
websocketonmessage(e){ //數(shù)據(jù)接收
const redata = JSON.parse(e.data);
console.log(redata.value);
},
websocketsend(data){//數(shù)據(jù)發(fā)送
this.websock.send(data);
},
websocketclose(e){ //關(guān)閉
console.log("connection closed (" + e.code + ")");
}
}
當(dāng)然,我在這里使用的是vue牵祟,這套代碼深夯,自己的項目也在使用,是可以使用的诺苹。我在這里呢咕晋?主要來談一下websocket的四個方法:
- readyState: 返回實例對象的當(dāng)前狀態(tài),一共有下面四種:
CONNECTING:值為0,表示正在連接收奔。
OPEN:值為1掌呜,表示連接成功,可以通信了筹淫。
CLOSING:值為2站辉,表示連接正在關(guān)閉。
CLOSED:值為3损姜,表示連接已經(jīng)關(guān)閉饰剥,或者打開連接失敗。
- onopen:用于指定連接成功后的回調(diào)函數(shù)
當(dāng)然我也看到很多人這么來寫:
websock.addEventListener('open', function (event) {
// dosomethings...
});
- onerror: 用于指定報錯時的回調(diào)函數(shù)
- onmessage: 用于指定收到服務(wù)器數(shù)據(jù)后的回調(diào)函數(shù)
- onclose: 用于指定連接關(guān)閉后的回調(diào)函數(shù)
- send: 發(fā)送socket消息
最后再聊一塊錢的
當(dāng)然摧阅,我在開發(fā)的時候遇到很常見的一個小問題汰蓉,那就是socket容易斷開。我最后這一塊錢就想由斷線重連談起棒卷。
我在這里首先來介紹一下前面提到的一個回調(diào)onclose
,這個如果存在連接中斷的情況會調(diào)用該回調(diào)顾孽,告知客戶端。首先看一下參數(shù):
由上面的圖解知道比规,close會有三個參數(shù):
code
若厚、reason
、wasClean
- code:返回一個 unsigned short 類型的數(shù)字, 表示服務(wù)端發(fā)送的關(guān)閉碼, 以下為已分配的狀態(tài)碼.
狀態(tài)碼 | 名稱 | 描述 |
---|---|---|
0–999 |
保留段, 未使用. |
|
1000 |
CLOSE_NORMAL |
正常關(guān)閉; 無論為何目的而創(chuàng)建, 該鏈接都已成功完成任務(wù). |
1001 |
CLOSE_GOING_AWAY |
終端離開, 可能因為服務(wù)端錯誤, 也可能因為瀏覽器正從打開連接的頁面跳轉(zhuǎn)離開. |
1002 |
CLOSE_PROTOCOL_ERROR |
由于協(xié)議錯誤而中斷連接. |
1003 |
CLOSE_UNSUPPORTED |
由于接收到不允許的數(shù)據(jù)類型而斷開連接 (如僅接收文本數(shù)據(jù)的終端接收到了二進(jìn)制數(shù)據(jù)). |
1004 |
<wbr> | 保留. <wbr>其意義可能會在未來定義. |
1005 |
CLOSE_NO_STATUS |
保留. <wbr> 表示沒有收到預(yù)期的狀態(tài)碼. |
1006 |
CLOSE_ABNORMAL |
保留. <wbr>用于期望收到狀態(tài)碼時連接非正常關(guān)閉 (也就是說, 沒有發(fā)送關(guān)閉幀). |
1007 |
Unsupported Data | 由于收到了格式不符的數(shù)據(jù)而斷開連接 (如文本消息中包含了非 UTF-8 數(shù)據(jù)). |
1008 |
Policy Violation | 由于收到不符合約定的數(shù)據(jù)而斷開連接. 這是一個通用狀態(tài)碼, 用于不適合使用 1003 和 1009 狀態(tài)碼的場景. |
1009 |
CLOSE_TOO_LARGE |
由于收到過大的數(shù)據(jù)幀而斷開連接. |
1010 |
Missing Extension | 客戶端期望服務(wù)器商定一個或多個拓展, 但服務(wù)器沒有處理, 因此客戶端斷開連接. |
1011 |
Internal Error | 客戶端由于遇到?jīng)]有預(yù)料的情況阻止其完成請求, 因此服務(wù)端斷開連接. |
1012 |
Service Restart | 服務(wù)器由于重啟而斷開連接. [Ref] |
1013 |
Try Again Later | 服務(wù)器由于臨時原因斷開連接, 如服務(wù)器過載因此斷開一部分客戶端連接. [Ref] |
1014 |
<wbr> | 由 WebSocket 標(biāo)準(zhǔn)保留以便未來使用. |
1015 |
TLS Handshake | 保留. <wbr>表示連接由于無法完成 TLS 握手而關(guān)閉 (例如無法驗證服務(wù)器證書). |
1016 –1999
|
<wbr> | 由 WebSocket 標(biāo)準(zhǔn)保留以便未來使用. |
2000 –2999
|
<wbr> | 由 WebSocket 拓展保留使用. |
3000 –3999
|
<wbr> | 可以由庫或框架使用. <wbr>不應(yīng)由應(yīng)用使用. 可以在 IANA 注冊, 先到先得. |
4000 –4999
|
<wbr> | 可以由應(yīng)用使用. |
- reson:返回一個
DOMString
用以表示服務(wù)器關(guān)閉連接的原因. 這是由服務(wù)器和子協(xié)議決定的. - wasClean: 返回一個 Boolean用以表示連接是否完全關(guān)閉.
由上面的基礎(chǔ)我們就可以在onclose
回調(diào)里面做重連代碼蜒什,我是這樣寫的
{
onclose(e){
if(!Object,is(e.code,1000)){
if(!e.wasClean) {
this.websock.close()
}
setTimeout(()=>{
initWebSocket();
},4000)
}
}
}
我這樣寫呢测秸?暫時也沒有發(fā)現(xiàn)什么錯誤,但是也不一定是全正確的。如果諸位看官在使用的時候有任何瑕疵請立即告訴我霎冯,我如果在使用中有任何不舒服的铃拇,我也會及時更新的。謝謝呢沈撞,您咧?独蟆!
說在最后
最近老是9點半左右下班缠俺,大清早旁邊的工地又不停的在吵显晶,攪的人無神無主、五雷轟頂...哇啊啊啊晋修,算了 或許這就是生命吧吧碾。反正一切的一切都是在炫耀自己懶罷了凰盔。最近呢墓卦?也準(zhǔn)備和大學(xué)室友寫一個項目,其中包含前端户敬、后端落剪、Android端。如果有看官希望一起開發(fā)尿庐、或者有興趣的話忠怖,請聯(lián)系我。大家一起邊開發(fā)邊學(xué)習(xí)邊成長抄瑟。好了凡泣,午時已到,準(zhǔn)備睡覺皮假,嗯 洗澡去了