WebSocket簡介以及簡單的應(yīng)用

WebSocket是HTML5的一個新特性提供了瀏覽器端和服務(wù)器端雙向?qū)崟r通信的能力,用于實現(xiàn)一些實時在線的應(yīng)用,這篇文章將簡單介紹WebSocket的使用,并實現(xiàn)一個簡單的在線聊天的功能。

WebSocket簡介

WebSocket與HTTP

WebSocket與HTTP都是一種協(xié)議,但是HTTP是一應(yīng)一答的,無論是 HTTP 1.0 還是 HTTP 1.1,它們都需要瀏覽器端發(fā)出請求(request)后才能讓服務(wù)器返回響應(yīng)(respones),在需要實時通訊的應(yīng)用時,比如在線聊天,在線游戲等,過去只能使用輪詢的方式來獲取最新的消息,這樣會消耗大連的網(wǎng)絡(luò)資源。

但是有了WebSocket之后,我們可以讓瀏覽器端和服務(wù)器端建立一個持久的雙向通訊連接,在有新數(shù)據(jù)的時候自動發(fā)送并接收,減少請求的數(shù)量,節(jié)省網(wǎng)絡(luò)資源。

HTML5的WebSocket

在HTML5中我們可以使用 WebSocket 對象來連接WebSocket服務(wù)器

var ws=new WebSocket(URL,protocols);

參數(shù)名 參數(shù)描述
URL 需要連接的地址
protocols (可選)可以是一個單個的協(xié)議名字字符串或者包含多個協(xié)議名字字符串的數(shù)組。

當構(gòu)建成功時會返回一個WebSocket對象,失敗時返回錯誤。

WebSocket對象提供了2個方法:

  1. void close(in optional unsigned long code, in optional DOMString reason);

    這個方法用于關(guān)閉WebSocket連接。

    參數(shù)名 參數(shù)描述
    code 關(guān)閉連接的狀態(tài)號,默認為1000(正常關(guān)閉)
    reason 一個字符串,用于描述關(guān)閉的原因。這個字符串必須是不長于123字節(jié)的UTF-8 文本

    code狀態(tài)碼列表

  2. void send(in DOMString data);

    這個方法用于像服務(wù)端發(fā)送數(shù)據(jù)。

    參數(shù)名 參數(shù)描述
    data 發(fā)送的數(shù)據(jù)

這兩個方法是有瀏覽器主動執(zhí)行,服務(wù)器發(fā)過來的消息需要通過監(jiān)聽事件來實現(xiàn)。

WebSocket對象有4個事件:

  1. open事件

    當連接成功連接時會觸發(fā)該事事件,會傳入一個Event對象

  2. message事件

    當接收到消息時會觸發(fā)該事件,會傳入一個Event對象,并且可以通過Event對象data屬性獲得數(shù)據(jù)

  3. error事件

    當連接出錯時會觸發(fā)該事事件,并傳入一個Event對象

  4. close事件

    當連接關(guān)閉的時候會觸發(fā)該事件,并傳入一個Event對象

=、=有了API下面肯定就是要擼起袖子搞一波啦~~~

WebSocket的簡單應(yīng)用-在線簡易聊天室

服務(wù)器的搭建

在這里我使用nodejs搭建服務(wù)器,畢竟咋們是學前端嘛=、=

能搭建Websocket服務(wù)器的包有很多,這里我們選用 ws ,因為這個包是十分基礎(chǔ)的一個,同時API也與W3C的規(guī)范差不多,方便理解。

sever.js


var path=require('path');
var http=require('http');
var express=require('express');
var ws=require('ws');

var app=express();
app.use(express.static(path.join(__dirname, './public')));

var server=app.listen(3000,()=>{
  console.log('服務(wù)器啟動');
});

var io=new ws.Server({server});
io.on('connection',function(client){

  client.on('message',function(data){
    console.log(data);
  });
  client.on('close',function(data){
    console.log(data);
  });

  setTimeout(function() {
    if(client.readyState===1){//防止已斷開連接
      client.send('來之服務(wù)器的消息');
    }
  }, 1000);

});

這段代碼我們就簡單的搭建了一個websokect服務(wù)器,我們可以從代碼中看出,服務(wù)器在websokect連接上后,會輸出 messageclose 請求的信息,并且我們在連接后會向瀏覽器發(fā)送一條消息。

下面是我們?yōu)g覽器的JS


var ws=new WebSocket('ws://127.0.0.1:3000/');

ws.onopen=function(event){
 console.log('連接成功');
 ws.onmessage=function(event){
   console.log('收到消息:'+event.data);
 }
 ws.onclose=function(event){
   console.log('連接關(guān)閉');
 }

  ws.send('來自瀏覽器的消息');

};
ws.onerror=function(event){
  console.log('連接出錯');
};

我們將這段代碼加入到服務(wù)器public目錄的index.html文件中,然后訪問 localhost:3000 查看瀏覽器和服務(wù)器控制臺

服務(wù)器控制臺

image

瀏覽器控制臺

image

我們可以看到WebSocket服務(wù)器已經(jīng)成功的運行了起來

簡易聊天室的實現(xiàn)

我們這里只實現(xiàn)一個簡單的聊天室的功能,發(fā)送和接受以及展示。

首先我我們把聊天室的頁面進行簡單的編寫,實現(xiàn)簡單的樣式


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>websokect簡易聊天室</title>
  <style>
    .messageBox{
      background: lightslategrey;
    }

    p{
      font-size: 16px;
      margin: 5px;
    }

    .messageBox-sys{
      text-align: center;
      color:yellow
    }

    .messageBox-other{
      text-align: left;
      color:black;
    }

    .messageBox-my{
      text-align: right;
      color:white;
    }

    input{
      height: 20px;
      margin: 3px;
      border: 1px;
    }

  </style>
</head>
<body>
  <div style="width:60%;margin:0 auto;background: grey">
    <div id="messageBox" class="messageBox">
      <p class="messageBox-sys">系統(tǒng)消息</p>
      <p class="messageBox-other">別人的消息</p>
      <p class="messageBox-my">我的消息</p>
    </div>
    <div class="control">
      <div style="margin: auto;width: 80%;position: relative">
        <input id="message" style="width: 70%" type="" name="" placeholder="輸入聊天內(nèi)容" value="">
        <input id="btn-send" style="width:20%" type="button" value="發(fā)送">
      </div>
    </div>
  </div>
</body>
</html>

image

我們在這里使用JSON格式的數(shù)據(jù)進行數(shù)據(jù)發(fā)送,包含文本內(nèi)容和發(fā)送時間


document.getElementById('btn-send').addEventListener('click',sendMessage);
function sendMessage(){
  if(ws.readyState!==1){
    alert('連接為建立');
    return;
  }

  var msg=document.getElementById('message').value;
  ws.send(JSON.stringify({
    msg:msg,
    date:new Date().getTime()
  }));

  var p=document.createElement('p');
  p.innerHTML="msg";
  p.className="messageBox-my";
  document.getElementById('messageBox').appendChild(p);
}

然后監(jiān)聽服務(wù)器發(fā)回的消息


ws.onmessage=function(event){
  var data=event.data;
  var type=data.type||0;
  var msg=data.msg||'';
  var date=data.date||new Date();
}

頁面寫好了,我們只需要服務(wù)器簡單處理下就可以了

服務(wù)器需要實現(xiàn)

  1. 收到消息轉(zhuǎn)發(fā)給別人

  2. 當有人加入/離開時進行提示

我們只需要一個數(shù)組存儲好加入進來的每個連接,我們就可以進行統(tǒng)一的轉(zhuǎn)發(fā)和處理。

當一個新的連接加入時,我們就可以遍歷數(shù)組向其他連接發(fā)送消息。


var linkarr=[];

function join(client){

  linkarr.forEach((pepole)=>{
    pepole.send(JSON.stringify({
      type:0,
      msg:"有新的成員加入!現(xiàn)在聊天室有 "+(linkarr.length+1)+" 個人",
      date:new Date().getTime()
    }));
  });

  client.send(JSON.stringify({
    type:0,
    msg:"歡迎加入!現(xiàn)在聊天室有 "+(linkarr.length+1)+" 個人",
    date:new Date().getTime()
  }));

  linkarr.push(client);

}

同理離開和發(fā)送信息時也一樣


function leave(client){

  var index=linkarr.indexOf(client);
  linkarr.splice(index,1);
  linkarr.forEach((pepole)=>{
    pepole.send(JSON.stringify({
      type:0,
      msg:"有一位成員離開!現(xiàn)在聊天室有 "+(linkarr.length)+" 個人",
      date:new Date().getTime()
    }));
  });
}

function sendData(client,data){

  linkarr.forEach((pepole)=>{
    if(pepole===client)return;
    pepole.send(JSON.stringify({
      type:1,
      msg:data.msg,
      date:data.date
    }));
  });
}

然后在我們的需要調(diào)用函數(shù)的地方加上,我們的服務(wù)器就完成啦?。。?/p>


var path=require('path');
var http=require('http');
var express=require('express');
var ws=require('ws');

var app=express();
app.use(express.static(path.join(__dirname, './public')));

var server=app.listen(3000,()=>{
  console.log('服務(wù)器啟動');
});

var io=new ws.Server({server});

var linkarr=[];

io.on('connection',function(client){
  join(client);

  client.on('message',function(data){
    sendData(client,JSON.parse(data));
  });
  client.on('close',function(data){
    leave(client);
  });

});

function join(client){

  linkarr.forEach((pepole)=>{
    pepole.send(JSON.stringify({
      type:0,
      msg:"有新的成員加入!現(xiàn)在聊天室有 "+(linkarr.length+1)+" 個人",
      date:new Date().getTime()
    }));
  });

  client.send(JSON.stringify({
    type:0,
    msg:"歡迎加入!現(xiàn)在聊天室有 "+(linkarr.length+1)+" 個人",
    date:new Date().getTime()
  }));

  linkarr.push(client);
}

function leave(client){

  var index=linkarr.indexOf(client);
  linkarr.splice(index,1);

  linkarr.forEach((pepole)=>{
    pepole.send(JSON.stringify({
      type:0,
      msg:"有一位成員離開!現(xiàn)在聊天室有 "+(linkarr.length)+" 個人",
      date:new Date().getTime()
    }));
  });
}

function leave(client){

  var index=linkarr.indexOf(client);
  linkarr.splice(index,1);

  linkarr.forEach((pepole)=>{
    pepole.send(JSON.stringify({
      type:0,
      msg:"有一位成員離開!現(xiàn)在聊天室有 "+(linkarr.length)+" 個人",
      date:new Date().getTime()
    }));
  });
}

function sendData(client,data){

  linkarr.forEach((pepole)=>{
    if(pepole===client)return;
    pepole.send(JSON.stringify({
      type:1,
      msg:data.msg,
      date:data.date
    }));
  });

}

同理我們的頁面也能快速的寫出來


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>websokect簡易聊天室</title>
  <style>
    .messageBox{
      background: lightslategrey;
    }

    p{
      font-size: 16px;
      margin: 5px;
    }

    .messageBox-sys{
      text-align: center;
      color:yellow
    }

    .messageBox-other{
      text-align: left;
      color:black;
    }

    .messageBox-my{
      text-align: right;
      color:white;
    }

    input{
      height: 20px;
      margin: 3px;
      border: 1px;
    }

  </style>
</head>
<body>
  <div style="width:60%;margin:0 auto;background: grey">
    <div id="messageBox" class="messageBox">
    </div>
    <div class="control">
      <div style="margin: auto;width: 80%;position: relative">
        <input id="message" style="width: 70%" type="" name="" placeholder="輸入聊天內(nèi)容" value="">
        <input id="btn-send" style="width:20%" type="button" value="發(fā)送">
      </div>
    </div>
  </div>
  <script type="text/javascript">
    var ws=new WebSocket('ws://127.0.0.1:3000/');
    ws.onopen=function(event){
      console.log('連接成功');

      ws.onmessage=function(event){
        var data=JSON.parse(event.data);
        var type=data.type||0;
        var msg=data.msg||'';
        var date=data.date||new Date();

        var p=document.createElement('p');
        p.innerHTML=msg;

        switch (type){
          case 0:p.className="messageBox-sys";break;
          case 1:p.className="messageBox-other";break;
          default:p.className="messageBox-sys";
        }

        document.getElementById('messageBox').appendChild(p);
      }

      ws.onclose=function(event){
        console.log('連接關(guān)閉');
      }

    };
    ws.onerror=function(event){
       console.log('連接出錯');
    };


    document.getElementById('btn-send').addEventListener('click',sendMessage);
    function sendMessage(){
      if(ws.readyState!==1){
        alert('連接為建立');
        return;
      }

      var msg=document.getElementById('message').value;
      ws.send(JSON.stringify({
        msg:msg,
        date:new Date().getTime()
      }));

      var p=document.createElement('p');
      p.innerHTML=msg;
      p.className="messageBox-my";
      document.getElementById('messageBox').appendChild(p);
    }


  </script>
</body>
</html>


于是很簡單的就完成了一個在線的簡易聊天室,=、=下面是一些簡單的效果結(jié)果

image
image

詳細DEMO可以查看參考資料

后記

這個DEMO的功能做的很簡單,離正真的聊天室功能還差很多,但是。。。不然為嘛叫DEMO呢=、=

有興趣的話我會慢慢完善功能-、-,所不定我會掛在上我的博客~~~~

啦啦啦

參考資料

MDN_WebSocket

DEMO地址

END

2017-3-5 完成

2017-3-5 立項

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容