1. 程式人生 > >將js進行到底:node學習7

將js進行到底:node學習7

html5 get 瀏覽器 js對象 sso console socket 靜態 學習

Node.js之Websocket技術

我第一次聽說websocket之時,HTML5標準尚未公布,當時只有少數前瞻性瀏覽器攜帶了這個API。

我對websocket最大的印象是,他可以解決我對“在線聊天系統”開發的疑惑(我一直想不通http如何保持長連接),這樣我們無需使用輪詢ajax和php無限循環去模擬,還記得2014年初那會我寫了一個在線聊天室,那時候我真的應該用websocket技術的,php無限輪詢的方式,只要3-4個人在線就可以讓linux+apache服務器崩潰。

很可惜,我沒有在php上使用過Websocket API,今天是我第一次將這個技術用來實踐,使用node.js實現

引入模塊

package.json

{
    "name":"chat-websocket",
    "version":"0.0.1",
    "description":"use websocket to create a char server",
    "dependencies":{
        "express":"latest",
        "express-ws":"latest"
    }
}

在引入http必備的express框架後,再引入基於express的中間件——express-ws

express-ws是express上的websocket中間件,為express提供了websocket請求處理功能

另外註意:《了不起的node.js》一書中使用的是websocket.io模塊,兩個東西原理,方法都差不多,我個人覺得io那個老了點,我選擇了express-ws模塊!主要是因為其與express配合效果更佳,專門為express而設計的中間件,何樂而不用呢?

做個測試

先來看看,node中使用express-ws的基本套路:

index.js

var express = require("express");

//創建express下的http服務
var app = express();
//關聯express-ws中間件
var expressWs = require("express-ws"
)(app); //express托管靜態文件 app.use(express.static(__dirname+‘/views‘,{‘index‘:‘index.html‘})); app.ws(‘/ws‘,function(ws,req){ ws.on(‘message‘,function(msg){ console.log(msg); }) });; app.listen(80);

這個index.js是服務端代碼

解析:

  • 先引入express模塊,並使用express()方法獲得app對象
  • 引入express-ws並構造對象,構造函數傳入的是app對象,意思大概是綁定到express服務器上
  • 通常接受http請求使用的是app.use(),而websocket請求則使用app.ws(),該方法就是中間件擴展的
  • message事件監聽客戶端發送的數據,並打印到終端上

views/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>測試</title>
</head>
<body>
    <input type="text" value="" id="msg">
    <button id="submit">提交</button>
</body>
<script>
    window.onload=function(){
        var ws = new WebSocket(‘ws://localhost/ws‘)
        var sub = document.getElementById("submit");
        var msg = document.getElementById("msg")
        sub.addEventListener("click",function(){
            ws.send(msg.value);
        });
        ws.addEventListener("open",function(){
            alert("WebSocket has been opened!");
        })
    }
</script>
</html>

這些為前端代碼:實現了一個簡單輸入框,輸入後主動通過websocket發送給服務端,服務端那邊會打印再console中

效果:

技術分享圖片

測試成功,每一次點擊提交都會顯示!

WebSocket開發在線聊天室

功能點

  1. 用戶進入輸入用戶名可開始聊天
  2. 登入後提示當前在線用戶
  3. 聊天內容會廣播給聊天室其他在線用戶
  4. 用戶進入後提示xxx用戶進入聊天室
  5. 用戶關閉後提示xxx用戶退出聊天室

package.json同上不變,具體設計邏輯參考另一篇博客,我用TCP API實現的聊天室程序http://www.cnblogs.com/devilyouwei/p/8423961.html

前端:index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>測試</title>
    <style>
        #inchat{
            display:none;
        }
        ul{
            list-style-type:none;
        }
    </style>
</head>
<body>
    <div id="inputname">
        <input type="text" value="" id="msg" placeholder="輸入一個聊天昵稱">
        <button id="submit">提交</button>
    </div>
    <div id="inchat">
        <ul id="chat-content">
        </ul>
        <textarea id="sendContent" value="">
        </textarea>
        <button id="send">發送</button>
    </div>
</body>
<script>
    window.onload=function(){
        var ws = new WebSocket(‘ws://localhost/ws‘);//需要修改為相應地址
        var sub = document.getElementById("submit");
        var msg = document.getElementById("msg")
        var ul  = document.getElementById("chat-content");
        var send = document.getElementById("send");
        var sendContent = document.getElementById("sendContent");
        sendContent.value="";
        sub.addEventListener("click",function(){
            ws.send(msg.value);
        });
        send.addEventListener("click",function(){
            ws.send(sendContent.value);
            sendContent.value=""
        });
        ws.addEventListener("open",function(){
            alert("WebSocket has been opened!");
        })
        ws.addEventListener("message",function(e){
            var res = JSON.parse(e.data);
            //如果正在聊天中
            if(res.ischat){
                var li = document.createElement("li");
                li.innerText = res.info;
                ul.appendChild(li);
            }else{
                if(res.status == 1){
                    document.getElementById("inputname").style.display="none";
                    document.getElementById("inchat").style.display="block";
                }else{
                    alert(res.info);
                }
            }
        });
    }
</script>
</html>

註意:放到公網訪問需要把localhost改成ip或者網址!

後端:index.js

var express = require("express");

//創建express下的http服務
var app = express();
//關聯express-ws中間件
var expressWs = require("express-ws")(app);

var users = {};
var count = 0;

//express托管靜態文件
app.use(express.static(__dirname+‘/views‘,{‘index‘:‘index.html‘}));

//用ws方法而不是use方法
app.ws(‘/ws‘,function(ws,req){
    var username = null;
    ws.on(‘message‘,function(msg){
        if(!username){
            if(users[msg]){
                ws.send(JSON.stringify({status:0,info:"用戶名重復請重試",ischat:false}));
            }else if(msg == ""){
                ws.send(JSON.stringify({status:0,info:"用戶名不能為空",ischat:false}));
            }else{
                username = msg;
                count++;   //用戶+1
                users[msg] = ws;
                ws.send(JSON.stringify({status:1,info:"註冊成功,歡迎"+username,ischat:false}));
                console.log(username+"用戶加入聊天室!當前在線:"+count);
                broadcast(username+"用戶加入聊天室!當前在線:"+count);
            }
        }else{
            broadcast(username+":"+msg);
            console.log(username+":"+msg);
        }
    });

    ws.on(‘close‘,function(){
        delete users[username];
        count--;
        console.log(username+"用戶退出聊天室!當前在線:"+count);
        broadcast(username+"用戶退出聊天室!當前在線:"+count);
    })
});;

app.listen(80);

//需要廣播給所有人(不排除自己)
function broadcast(msg){
    for(var i in users)
        users[i].send(JSON.stringify({status:1,info:msg,ischat:true}));
}

註意1:express-ws中的ws.send()方法只能發送字符串,並沒有express http的res.send()那麽強大,故而我在傳入js對象時,手動使用JSON.stringify()將對象轉換為json字符串,待前端收到後再使用JSON.parse()轉換回js對象。

註意2:WebSocket API中幾個最重要的事件:open,close,message,error,對應了連接過程中的打開連接,關閉連接,消息傳遞,錯誤事件,無論前端還是後端都需要對這幾個事件進行綁定監聽,傳入回掉函數做必要的處理

註意3:設計邏輯再講一遍:users變量存儲每一個連接引用,username作為局部變量,再每一次連接域內部,每一個客戶端連入都會創建一個,單獨的broadcast()方法遍歷所有socket連接發送消息,最後再提醒一遍node.js開發一定要特別註意作用域範圍!

效果

技術分享圖片

將js進行到底:node學習7