亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

websocket

網頁中的絕大多數請求使用的是 HTTP 協議,HTTP 是一個無狀態的應用層協議,它有著即開即用的優點,每次請求都是相互獨立的,這對于密集程度較低的網絡請求來說是優點,因為無需創建請求的上下文條件,但是對于密集度或者實時性要求較高的網絡請求(例如 IM 聊天)場景來說,可能 HTTP 會力不從心,因為每創建一個 HTTP 請求對服務器來說都是一個很大的資源開銷。這時我們可以考慮一個相對性能較高的網絡協議 Socket,他的網頁版本被稱為 Websocket。

1. 背景

近年來,隨著 HTML5 和 w3c 的推廣開來,WebSocket 協議被提出,它實現了瀏覽器與服務器的實時通信,使服務端也能主動向客戶端發送數據。在 WebSocket 協議提出之前,開發人員若要實現這些實時性較強的功能,經常會使用一種替代性的解決方案——輪詢。

輪詢的原理是采用定時的方式不斷的向服務端發送 HTTP 請求,頻繁地請求數據。明顯地,這種方法命中率較低,浪費服務器資源。伴隨著 WebSocket 協議的推廣,真正實現了 Web 的即時通信。

WebSocket 的原理是通過 JavaScript 向服務端發出建立 WebSocket 連接的請求,在 WebSocket 連接建立成功后,客戶端和服務端可以實現一個長連接的網絡管道。因為 WebSocket 本質上是 TCP 連接,它是一個長連接,除非斷開連接否則無需重新創建連接,所以其開銷相對 HTTP 節省了很多。

2. API

2.1 創建連接

通過使用新建一個 websocket 對象的方式創建一個新的連接,不過在創建之前需要檢測一下瀏覽器是否支持 Websocket,因為只有支持 HTML5 的瀏覽器才能支持 Websocket,如下:

if(typeof window.WebSocket == 'function'){
    var ws = new WebSocket('http://127.0.0.1:8003');//創建基于本地的8003端口的websocket連接
}else alert("您的瀏覽器不支持websocket");

上述代碼會對本地的 8003 接口請求 Websocket 連接,前提是本地的服務器有進程監聽 8003 端口,不然的話會連接失敗。

2.2 創建成功

由于 JavaScript 的各種 IO 操作是基于事件回調的,所以 Websocket 也不例外,我們需要創建一個連接成功的回調函數來處理連接創建成功之后的業務處理,如下:

ws.onopen = function(){//通過監聽 open 時間來做創建成功的回調處理
    console.log('websocket連接創建成功')
    //進行業務處理
}

2.3 接收消息

我們辛辛苦苦創建了長連接就是為了發送或者接收網絡數據,那么怎么接收呢,跟上邊提到的意義,還是需要在回調函數里處理,一不小心就陷入了回調地獄了:

ws.onmessage = function(event){
    var d = event.data;
    //接收到消息之后的業務處理
    switch(typeof d){//判斷數據的類型格式
    case "String":
        break;
    case "blob":
        break;
    case "ArrayBuffer":
        break;
    default:
        return;
    }
} 

上述實例通過監聽 message 事件對 websocket 的消息進行一定的業務處理,這其中需要判斷數據類型格式,因為 Websocket 是基于二進制流格式的,傳輸過來的消息可能不一定是基于 utf8 的字符串格式,因此需要對格式進行判斷。

2.4 發送消息

客戶端通過使用 send 函數向服務端發送數據,例如:

ws.send("一段測試消息");

可以發送文本格式,也可以發送二進制格式,例如:

var input  = document.getElementById("file"); 
input.onchange = function(){
    var file = this.files[0];
    if(!!file){
        //讀取本地文件,以gbk編碼方式輸出
        var reader = new FileReader();
        reader.readAsBinaryString(file);
        reader.onload = function(){
            //讀取完畢后發送消息
            ws.send(this.result);
        }
    }
}

2.5 監聽錯誤信息

類似上述提到的如果創建實例失敗的情況,系統會出現異常,但是我們并不能準確判斷出異常的信息,這時需要通過監聽錯誤事件來獲取報錯信息,例如:

ws.onerror = function(event){
    //這里處理錯誤信息
}

2.6 關閉連接

當服務端或者客戶端關閉 websocket 連接時,系統會觸發一個關閉事件,例如:

ws.onclose = function (event){
    //這里處理關閉之后的業務
}

2.7 連接的狀態

通過 websocket 對象的 readyState 屬性可以獲取到當前連接的狀態,其中常用的有4種,通過 websocket 對象的幾種定義常量對比判斷:

switch (ws.readyState){
    case WebSocket.CONNECTING:break;//處于正在連接中的狀態
    case WebSocket.OPEN:break;//表示已經連接成功
    case WebSocket.CLOSING:break;//表示連接正在關閉
    case WebSocket.CLOSE:break;//表示連接已經關閉,或者創建連接失敗
    default:break;
}

3. websocket 實例

實例演示
預覽 復制
復制成功!
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
    <style>
        p {
            text-align: left;
            padding-left: 20px;
        }
    </style>
</head>
<body>
<div style="width: 700px;height: 500px;margin: 30px auto;text-align: center">
    <h1>聊天室實戰</h1>
    <div style="width: 700px;border: 1px solid gray;height: 300px;">
        <div style="width: 200px;height: 300px;float: left;text-align: left;">
            <p><span>當前在線:</span><span id="user_num">0</span></p>
            <div id="user_list" style="overflow: auto;">

            </div>
        </div>
        <div id="msg_list" style="width: 598px;border:  1px solid gray; height: 300px;overflow: scroll;float: left;">
        </div>
    </div>
    <br>
    <textarea id="msg_box" rows="6" cols="50" onkeydown="confirm(event)"></textarea><br>
    <input type="button" value="發送" onclick="send()">
</div>
</body>
</html>

<script type="text/javascript">
    var uname = window.prompt('請輸入用戶名', 'user' + uuid(8, 16));
    var ws = new WebSocket("ws://127.0.0.1:8081");
    ws.onopen = function () {
        var data = "系統消息:連接成功";
        listMsg(data);
    };
    ws.onmessage = function (e) {
        var msg = JSON.parse(e.data);
        var data =  msg.content;
        listMsg(data);
    };

    ws.onerror = function () {
        var data = "系統消息 : 出錯了,請退出重試.";
        listMsg(data);
    };

    function confirm(event) {
        var key_num = event.keyCode;
        if (13 == key_num) {
            send();
        } else {
            return false;
        }
    }

    /**
     * 發送并清空消息輸入框內的消息
     */
    function send() {
        var msg_box = document.getElementById("msg_box");
        var content = msg_box.value;
        var reg = new RegExp("\r\n", "g");
        content = content.replace(reg, "");
        var msg = {'content': content.trim(), 'type': 'user'};
        sendMsg(msg);
        msg_box.value = '';
    }

    /**
     * 將消息內容添加到輸出框中,并將滾動條滾動到最下方
     */
    function listMsg(data) {
        var msg_list = document.getElementById("msg_list");
        var msg = document.createElement("p");

        msg.innerHTML = data;
        msg_list.appendChild(msg);
        msg_list.scrollTop = msg_list.scrollHeight;
    }

    /**
     * 將數據轉為json并發送
     * @param msg
     */
    function sendMsg(msg) {
        var data = JSON.stringify(msg);
        ws.send(data);
    }
</script>
運行案例 點擊 "運行案例" 可查看在線運行效果

上述實例通過使用 websocket 實現了一個簡單的聊天室功能,功能上只實現了接受和發送消息的功能,在登錄認證和安全性等問題上并沒有做過多的處理,只是為了給大家連貫的展示一下 websocket 在實際項目中的使用。

4. 注意事項

實際項目中使用 websocket 需要注意一些問題 :

  • websocket 創建之前需要使用 HTTP 協議進行一次握手請求,服務端正確回復相應的請求之后才能創建 websocket 連接;
  • 創建 websocket 時需要進行一些類似 token 之類的登錄認證,不然任何客戶端都可以向服務器進行 websocket 連接;
  • websocket 是明文傳輸,敏感的數據需要進行加密處理;
  • 由于 websocket 是長連接,當出現異常時連接會斷開,服務端的進程也會丟失,所以服務端最好有守護進程進行監控重啟;
  • 服務器監聽的端口最好使用非系統性且不常使用的端口,不然可能會導致端口沖突

5. 小結

本章介紹了 websocket 的前世今生,詳細說明其對應的 API 的調用方式,最后使用了一個簡單的聊天室的例子來對其函數串通了一下,最后延伸了一下實際項目中使用 websocket 需要注意的地方,希望大家在實際開發中針對其優缺點來選擇合適的使用場景。