亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

利用express+socket.io實現(xiàn)一個簡易版聊天室

Chaz / 1758人閱讀

摘要:用偽代碼來模擬下長輪詢的過程前端利用下面函數(shù)進行請求后端代碼做如下更改利用隨機數(shù)的大小來模擬是否有新數(shù)據(jù)有新數(shù)據(jù)來了長輪詢的確減少了請求的次數(shù),但是它也有著很大的問題,那就是耗費服務(wù)器的資源。

寫在前面

最近由于利用node重構(gòu)某個項目,項目中有一個實時聊天的功能,于是就研究了一下聊天室,在線demo|源碼,歡迎大家反饋。這個聊天室的主要利用到了socket.ioexpress。這個聊天室支持群聊,私聊,支持發(fā)送圖片(PS:大家在體驗時最好開啟兩個瀏覽器,自問自答)。下面就來和大家分享下實現(xiàn)過程:

WebSocket

HTML5一種新的協(xié)議。它實現(xiàn)了瀏覽器與服務(wù)器全雙工通信。

為了更好的理解WebSocket,需要了解一下在沒有WebSocket階段是如何寫聊天室這種實時系統(tǒng)的:
基于http協(xié)議瀏覽器可以實現(xiàn)單向通信,只能由瀏覽器發(fā)起請求(Request),服務(wù)器進行響應(yīng)(Response),一個請求對應(yīng)一個響應(yīng)。由于服務(wù)器不能主動向客戶端推送消息,于是普遍采用的方式就是輪詢(polling),輪詢實現(xiàn)起來非常簡單,就是定時的利用ajax向服務(wù)器端進行請求。如果服務(wù)器有新的數(shù)據(jù)就返回新的數(shù)據(jù),如果沒有數(shù)據(jù)就返回空響應(yīng)。用代碼來模擬下就是這個樣子的:

// 前端請求代碼
function update (fn) {
    var xhr = new XMLHttpRequest();
    xhr.open("get", "./update.php");
    xhr.onreadystatechange = function(){    
    if(xhr.readyState === 4){
      if(xhr.status == 200){    
        const res = JSON.parse(xhr.response);
          if (res.flag) {
              // 進行相應(yīng)操作
              
              // fn為接到響應(yīng)后的處理函數(shù)
              fn && fn(fn);
          }
      }
    }
    };
    xhr.send();
}
function polling () {
    update();
}
setInterval(polling, 2000);
// 后臺響應(yīng)代碼
 true, 
            "data" => "有新數(shù)據(jù)來了"
        ));
    } else {
        echo json_encode(array(
            "flag" => false
        ));
    }
?>

這種定時請求的方式的關(guān)鍵在于間隔時間的選取,依據(jù)我在上面代碼做的模擬,很少概率能拿到下真正的數(shù)據(jù),多半的ajax請求是無效的,于是又有前輩基于輪詢提出來了Comet(服務(wù)器推),這種技術(shù)可以通過長輪詢(long polling)實現(xiàn)(還可以利用iframe),長輪詢也是靠ajax實現(xiàn)客戶端的請求,其流程為:客戶端發(fā)起請求,服務(wù)器掛起請求,假若有新的數(shù)據(jù)返回,服務(wù)器響應(yīng)客戶端剛才的請求,客戶端得到響應(yīng)后繼續(xù)請求服務(wù)器。用偽代碼來模擬下長輪詢的過程:

// 前端利用下面函數(shù)進行請求
function longPolling () {
    update(update);
}
longpolling();
// 后端代碼做如下更改
 true, 
                "data" => "有新數(shù)據(jù)來了"
            ));
            break;
        }
    }
?>

長輪詢的確減少了請求的次數(shù),但是它也有著很大的問題,那就是耗費服務(wù)器的資源。
無論是輪詢還是長輪詢,還有著一個問題就是http并不是支持長連接很多人會說keep-alive不就是做到了長連接嗎?然而并非如此,keep-alive是重用一個TCP連接,就是說http 1.1做到了一個TCP連接可以發(fā)送多個http請求,然而每個http請求還需要發(fā)送Request Header,每個請求的響應(yīng)還會帶著Response Header。對于輪詢和長輪詢來說伴隨著真實數(shù)據(jù)的交換,還有進行的就是大量的http header的交換。
基于這些問題,WebSocket被提出,WebSocket可以理解為對http的一個補丁包,WebSocket使http變成了一個真正的長連接,握手階段利用http協(xié)議,之后就不會再發(fā)起http請求了。下面來看下WebSocket握手的過程:


客戶端的請求頭比一般的http請求多出來幾個字段:

Upgrade: websocket,Connection: Upgrade,利用這兩個字段來告訴服務(wù)器,我要將協(xié)議升級為websocket。

Sec-WebSocket-Version: 13,來告訴服務(wù)器我想要使用的WebSocket的版本。

Sec-WebSocket-Key,其值采用base64編碼的隨機16字節(jié)長的字符序列,這個值會在響應(yīng)頭中回應(yīng)。

Sec-WebSocket-Extensions,提供了一個客戶端支持的協(xié)議擴展列表來供服務(wù)器選擇,服務(wù)器只能選擇一個,并且會將選擇的擴展寫入響應(yīng)頭的Sec-WebSocket-Extensions。

Sec-WebSocket-Protocol,與Sec-WebSocket-Extensions原理相似,用于協(xié)商應(yīng)用子協(xié)議。

再來看看響應(yīng)頭:

Status Code,值為101,表示已經(jīng)升級到WebSocket協(xié)議

Sec-WebSocket-Extensions告訴客戶端服務(wù)器選擇的協(xié)議擴展

Sec-WebSocket-Protocol告訴客戶端服務(wù)器選擇的子協(xié)議

Sec-WebSocket-Accept經(jīng)服務(wù)器確認并且加密后的Sec-WebSocket-Key

還有一點值得關(guān)注的就是協(xié)議頭由http/https換成了ws/wss,也標識真http完成了其使命,接下來的事情由WebSocket來負責啦!

socket.io

由于寫原生的WebSocket在處理低版本瀏覽器的兼容性上的困難,所以一般在寫實時交互的這種項目時一般會利用到socket.iosocket.io并不僅僅是WebSocket,還包含著AJAX long polling,AJAX multipart streaming,JSONP Polling等。socket.io可以看做是基于engine.io的二次開發(fā)。通過emiton可以輕松地實現(xiàn)服務(wù)器與客戶端之間的雙向通信,emit來發(fā)布事件,on來訂閱事件。

用戶登錄/登出

下面開始來寫代碼,我利用的構(gòu)建工具是gulp,模板語言是jade,css預(yù)處理語言是less,假若也需要使用到這些,可以關(guān)注下我所在團隊搭建的一個小的腳手架,先從app.js開始:

const users = {}, 
    app = express(),
    server = require("http").createServer(app),
    io = require("socket.io").listen(server); 
// 將socket.io綁定到服務(wù)器上,使得任何連接到服務(wù)器的客戶端都具有實時通信的功能

// 服務(wù)器來監(jiān)聽客戶端
io.on("connection", (socket) => {
    // socket是返回的連接對象,兩端的交互就是通過這個對象
});

需要創(chuàng)建一個對象(users)來存儲在線用戶,鍵值為用戶昵稱,為用戶登錄來訂閱個事件:

socket.on("login", (nickname) => {
        if (users[nickname] || nickname === "system") {
            socket.emit("repeat");            
        } else {
            socket.nickname = nickname;
            users[nickname] = {
                name: nickname,
                socket: socket,
                lastSpeakTime: nowSecond()
            };
            socket.emit("loginSuccess");            
            UsersChange(nickname, true);
        }
});
socket.on("disconnect", () => {
    if (socket.nickname && users[socket.nickname]) {
        delete users[socket.nickname];
        UsersChange(socket.nickname, false);
    }
});
function UsersChange (nickname, flag) {
    io.sockets.emit("system", {
        nickname: nickname,
        size: Object.keys(users).length,
        flag: flag
    });
}
function nowSecond () {
    return Math.floor(new Date() / 1000);
}

用戶登錄時需要驗證其昵稱是否含有,假若函數(shù),則觸發(fā)在客戶端的js代碼中注冊的repeat事件,反之觸發(fā)loginSuccess事件并且登錄成功后需要向所有的客戶端來廣播,所以利用了io.sockets.emit。repeatloginSuccess,system,在src/js/index.js中進行注冊,主要用于頁面的顯示,也就是一些dom操作,所以在這里沒有什么好講的。用戶退出,直接調(diào)用默認事件disconnect就好,并將該用戶從用戶對象中移除。

心跳檢測

在用戶的狀態(tài)上的坑還是不少的,因為WebSocket中間過程比較復雜,經(jīng)常會出現(xiàn)一些異常的情況,所以需要進行心跳檢測,我采用的方式是服務(wù)端定時遍歷用戶列表,假若用戶最后的發(fā)言時間與現(xiàn)在相比超過了5分鐘,就將其視為掉線,從而避免了"用戶undefined退出群聊"的這種情況。

function pong () {
    const now = nowSecond();
    for (let k in users) {
        if (users[k].lastSpeakTime + MAX_LEAVE_TIME < now) {
            var socket = users[k].socket;
            users[k].socket.emit("disconnect");
            socket.emit("nouser", "由于長時間未說話,您已經(jīng)掉線,請重新刷新頁面");
            socket = null;
        } 
    }
}
// 心跳檢測
setInterval(pong, PONG_TIME);
function UsersChange (nickname, flag) {
    io.sockets.emit("system", {
        nickname: nickname,
        size: Object.keys(users).length,
        flag: flag
    });
}
寫在最后

其實socket.io的使用真的非常簡單,很容易就會上手,所以其余功能不再一一演示,大家可以看代碼的實現(xiàn)(寫的比較差,還請見諒),客戶端代碼中大量用到了L,相當于zepto$,特別需要處理的是在私信和發(fā)送圖片的處理上,私信需要處理不同消息框,到底把消息添加到那個消息框中,我利用了一個對象來存儲這些信息(cache),cache的鍵名為用戶的昵稱(因為在注冊時判斷了其是否唯一,所以可以將其視為唯一的);鍵值為對象,對象屬性如下圖所示:


具體實現(xiàn)大家還是到源碼中去看吧!

感謝王哇勇大神的HiChat和小胡子哥的blogChat
由于本人水平有限,如有錯誤,歡迎大家指出!

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/91020.html

相關(guān)文章

  • 基于expresssocket.io簡易天室

    摘要:簡易版聊天室技術(shù)棧功能實現(xiàn)實時聊天創(chuàng)建房間表情包完善私聊效果登錄服務(wù)端判斷之前是否登錄過聊天室,如果是則直接進入聊天室,否則跳轉(zhuǎn)到登錄頁面。客戶端發(fā)送創(chuàng)建房間和切換房間的事件給服務(wù)端。 Chat 簡易版聊天室 技術(shù)棧 express socket.io 功能 實現(xiàn) 實時聊天 創(chuàng)建房間 表情包 完善 私聊 效果 登錄 showImg(https://segmentfa...

    dendoink 評論0 收藏0
  • vue+socket.io+express+mongodb 實現(xiàn)簡易多房間在線群聊

    摘要:項目簡介主要是通過做一個多人在線多房間群聊的小項目來練手全棧技術(shù)的結(jié)合運用。編譯運行開啟服務(wù),新建命令行窗口啟動服務(wù)端,新建命令行窗口啟動前端頁面然后在瀏覽器多個窗口打開,注冊不同賬號并登錄即可進行多用戶多房間在線聊天。 項目簡介 主要是通過做一個多人在線多房間群聊的小項目、來練手全棧技術(shù)的結(jié)合運用。 項目源碼:chat-vue-node 主要技術(shù): vue2全家桶 + socket....

    android_c 評論0 收藏0
  • vue.js+socket.io打造一個好玩的新聞社區(qū)

    摘要:云新聞云新聞收藏的使用需要注意的地方提交的是,而不是直接的狀態(tài)變更可以包含任意異步操作。的使用利用實現(xiàn)了簡單的聊天功能,在同一個服務(wù)器下。 title: Socket.io+vue打造新聞社區(qū)date: 2017-06-12 20:19:05 tags: [vue.js,javascript,socket.io] vue2.0 + socket.io打造一個DIY新聞社區(qū)(web第一...

    xiguadada 評論0 收藏0
  • node技術(shù)棧 - 收藏集 - 掘金

    摘要:異步最佳實踐避免回調(diào)地獄前端掘金本文涵蓋了處理異步操作的一些工具和技術(shù)和異步函數(shù)。 Nodejs 連接各種數(shù)據(jù)庫集合例子 - 后端 - 掘金Cassandra Module: cassandra-driver Installation ... 編寫 Node.js Rest API 的 10 個最佳實踐 - 前端 - 掘金全文共 6953 字,讀完需 8 分鐘,速讀需 2 分鐘。翻譯自...

    王偉廷 評論0 收藏0
  • socket.io+express實現(xiàn)天室的思考(一)

    摘要:但是需要注意的一點是協(xié)議是建立在協(xié)議基礎(chǔ)之上的,需要經(jīng)過一次握手。所以連接的發(fā)起方仍是客戶端。是一個簡潔而靈活的應(yīng)用框架提供一系列強大特性幫助你創(chuàng)建各種應(yīng)用。這也是為什么要采用協(xié)議來實現(xiàn)聊天室的原因。 從開始寫到完善差不多斷斷續(xù)續(xù)差不多半個月時間,雖然還沒有打到想要的效果但還是階段性總結(jié)一下。(下一步加入打算視頻通訊功能)本文默認你已掌握 node 相關(guān)基礎(chǔ)知識 GitHub地址:ht...

    fjcgreat 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<