最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Node.js websocket使用socket.io庫(kù)實(shí)現(xiàn)實(shí)時(shí)聊天室

 更新時(shí)間:2021年07月11日 13:11:25   作者:HaoDaWang  
這篇文章主要為大家詳細(xì)介紹了Node.js websocket使用socket.io庫(kù)實(shí)現(xiàn)實(shí)時(shí)聊天室,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

認(rèn)識(shí)websocket

WebSocket protocol 是HTML5一種新的協(xié)議。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信(full-duple)。一開始的握手需要借助HTTP請(qǐng)求完成。

其實(shí)websocket 并不是很依賴Http協(xié)議,它也擁有自己的一套協(xié)議機(jī)制,但在這里我們需要利用的socket.io 需要依賴到http 。
之前用java jsp寫過(guò)一個(gè)聊天,其實(shí)實(shí)現(xiàn)邏輯并不難,只是大部分時(shí)間都用在UI的設(shè)計(jì)上,其實(shí)現(xiàn)原理就是一個(gè)基于websocket的通信,要想做一個(gè)好的聊天室,我覺(jué)得大部分精力可能更應(yīng)該花在與用戶的視覺(jué)層交互上。

廢話不閑扯,我們先來(lái)看一下websocket 與傳統(tǒng)的ajax 有什么不同之處。
在之前,如果我們想要獲取到服務(wù)器更新的信息,我們可以使用ajax 輪詢來(lái)完成,然而,這樣做的弊端是增大了我們與服務(wù)器的交互次數(shù),然而極大部分的交互都是無(wú)意義的,因?yàn)槲覀冎皇亲鲆粋€(gè)詢問(wèn),如果沒(méi)有任何新的信息,我們幾乎什么都不用做,因此這樣會(huì)極大的浪費(fèi)服務(wù)器資源和帶寬。
然而使用websocket 會(huì)使客戶端與服務(wù)器建立一個(gè)長(zhǎng)連接,并且,當(dāng)服務(wù)器有新消息時(shí)可以主動(dòng)推送到客戶端,所以我們可以不用一次次的去詢問(wèn)服務(wù)器是否有新消息,而是直接由服務(wù)器主動(dòng)推送到客戶端,這樣在無(wú)消息的狀態(tài)下,客戶端不會(huì)頻繁的去請(qǐng)求服務(wù)器。
使用websocket 的特點(diǎn)在于服務(wù)器可以主動(dòng)推送消息到客戶端。

使用socket.io 庫(kù)實(shí)現(xiàn)實(shí)時(shí)聊天

這也是這篇博文的主題之處。socket.io發(fā)布到npm 平臺(tái)上,我們可以直接用npm 來(lái)安裝到**當(dāng)前**node_modules目錄下。

npm install socket.io --save 

下面我們就可以直接使用require 方法來(lái)將這個(gè)模塊引入

const socket = require("socket.io");

在創(chuàng)建此websocket 服務(wù)器之前,它需要依賴于一個(gè)已經(jīng)創(chuàng)建好的http服務(wù)器。

let socketServer = socket.listen(require("http").createServer((req,resp) => {
 //返回頁(yè)面
 resp.end(require("fs").readFileSync("./socketIOTest1.html"));
}).listen(9999,"localhost",() => {console.log("listening");}));

在上述代碼中socketIOTest1.html 是在當(dāng)前目錄下的一個(gè)html文件,在下面我會(huì)貼上詳細(xì)的代碼,這里先稍稍帶過(guò)。

在websocket 服務(wù)器對(duì)象中有一個(gè)connection事件,這個(gè)事件在有客戶端連接到socket服務(wù)器時(shí)被觸發(fā)。下面我們監(jiān)聽這個(gè)事件,打印一句話來(lái)表示有用戶連接。

//監(jiān)聽connection 事件
socketServer.on("connection",socket => {
 console.log("有一用戶連接");
}

上述代碼中,callback有一個(gè)參數(shù)socket為連接到客戶端的一個(gè)socket端口對(duì)象,這個(gè)對(duì)象有一個(gè)message 事件,當(dāng)客戶端有消息推送到服務(wù)器時(shí),事件循環(huán)會(huì)取出這個(gè)事件與之對(duì)應(yīng)的回調(diào)函數(shù)并執(zhí)行。

socket.on("message",msg => {
 console.log(msg);
});

同時(shí),socket對(duì)象還可以監(jiān)聽disconnect 事件,來(lái)監(jiān)聽用戶斷開連接的情況

socket.on("disconnect",() => {
 console.log("有一用戶退出連接");
});

因?yàn)槲覀冞@次的主題是要?jiǎng)?chuàng)建一個(gè)能夠?qū)崟r(shí)聊天的聊天室,因此光有這些是不夠的,我們還需要一個(gè)能夠與用戶交互的客戶端。
下面是我的socketIOTest代碼:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
 <textarea name="" id="content" cols="30" rows="10" ></textarea>
 <input id="write" type="text" placeholder="please write content here">
 <input id="send" type="button" value="send" />
 <script src="./socket.io/socket.io.js"></script>
 <script>
  let send = document.getElementById("send");
  let write = document.getElementById("write");
  let content = document.getElementById("content");
  let socket = io.connect();
  //發(fā)送消息
  send.onclick = () => {
   let msg = write.value;
   // content.innerHTML = content.value + msg + "\n";
   socket.send(msg);
  };
  //接收到消息
  socket.on("message",msg => {
   console.log("從服務(wù)器接收到的消息 : " + msg);
   //更新內(nèi)容
   content.innerHTML = content.value + msg + "\n";
  });
  socket.on("disconnect",() => {
   console.log("與服務(wù)器斷開連接");
  });
 </script>
</body>
</html>

在上述代碼中,我用script標(biāo)簽引入了一個(gè)socket.io.js文件,這個(gè)文件不需要另外去下載,而直接引入即可,因?yàn)閟ocket.io.js是被包含于socket.io模塊中,在上面node的程序代碼中,我們通過(guò)require方法引入了socket.io模塊,因此我們可以直接通過(guò)相對(duì)路徑訪問(wèn)到它。

<script src="./socket.io/socket.io.js"></script>

接下來(lái)我們就可以在script標(biāo)簽中使用如同服務(wù)端的代碼。

let socket = io.connect();

使用io.connect()方法連接到websocket服務(wù)器,該方法返回一個(gè)與連接的服務(wù)器與之對(duì)應(yīng)的一個(gè)socket端口對(duì)象。

下面我們同樣監(jiān)聽message 和 disconnect事件。

//接收到消息
socket.on("message",msg => {
 console.log("從服務(wù)器接收到的消息 : " + msg);
 //更新內(nèi)容
 content.innerHTML = content.value + msg + "\n";
});
socket.on("disconnect",() => {
 console.log("與服務(wù)器斷開連接");
});

為了更能突出websocket的作用,在html代碼中,我只使用了一個(gè)textarea標(biāo)簽來(lái)顯示內(nèi)容,兩個(gè)input標(biāo)簽用于發(fā)送。
使用socket對(duì)象的send方法就能使消息在服務(wù)器與客戶端進(jìn)行消息傳遞。

websocket群聊實(shí)現(xiàn)

現(xiàn)在我們假設(shè)一個(gè)場(chǎng)景,有u1和u2兩個(gè)用戶,同時(shí)連接到服務(wù)器,那么我們?cè)趺词顾麄兓ハ嗤ㄐ拍兀瑢?shí)現(xiàn)的方法及其簡(jiǎn)單。當(dāng)u1連接到服務(wù)器,在服務(wù)器中,使用一個(gè)map鍵值對(duì)把與u1對(duì)應(yīng)的socket對(duì)象進(jìn)行保存。

//創(chuàng)建一個(gè)用于放置用戶對(duì)象的map
let map = new Map();
//用于記錄用戶數(shù)量的變量,并初始化為0
let userCount = 0;
//監(jiān)聽connection 事件
socketServer.on("connection",socket => {
 console.log("有一用戶連接");
 map.set(++userCount,socket);
 //...
}); 

與此同時(shí),u2也連接上服務(wù)器,也由該map把與u2與之對(duì)應(yīng)的socket對(duì)象進(jìn)行儲(chǔ)存。
現(xiàn)在,u1點(diǎn)擊了send按鈕發(fā)送一條消息至服務(wù)器,服務(wù)器收到消息后遍歷map,轉(zhuǎn)發(fā)給所有socket對(duì)象,實(shí)現(xiàn)群聊的實(shí)時(shí)通信。

socketServer.on("connection",socket => {
 console.log("有一用戶連接");
 map.set(++userCount,socket);
 //監(jiān)聽客戶端來(lái)的信息
 socket.on("message",msg => {
  //從客戶端接收的消息
  //遍歷所有用戶
  map.forEach((value,index,arr) => {
   value.send(msg);
  });
 });
}); 

下面我貼上服務(wù)端的完整代碼,僅供參考

const socket = require("socket.io");
//創(chuàng)建一個(gè)websocket服務(wù)器
let socketServer = socket.listen(require("http").createServer((req,resp) => {
 //返回頁(yè)面
 resp.end(require("fs").readFileSync("./socketIOTest1.html"));
}).listen(9999,"localhost",() => {console.log("listening");}));

//創(chuàng)建一個(gè)用于放置用戶對(duì)象的map
let map = new Map();
//用于記錄用戶數(shù)量的變量,并初始化為0
let userCount = 0;

//監(jiān)聽connection 事件
socketServer.on("connection",socket => {
 console.log("有一用戶連接");
 map.set(++userCount,socket);
 //監(jiān)聽客戶端來(lái)的信息
 socket.on("message",msg => {
  //從客戶端接收的消息
  //遍歷所有用戶
  map.forEach((value,index,arr) => {
   value.send(msg);
  });
 });
 //監(jiān)聽客戶端退出情況
 socket.on("disconnect",() => {
  console.log("有一用戶退出連接");
 });
}); 

websocket私聊實(shí)現(xiàn)

在說(shuō)私聊的實(shí)現(xiàn)之前,我們首先要找到對(duì)于每一個(gè)用戶的唯一標(biāo)識(shí),在通常的項(xiàng)目開發(fā)中,我們都使用用戶的用戶名進(jìn)行標(biāo)識(shí),每個(gè)用戶通過(guò)注冊(cè)獲得與之對(duì)應(yīng)的用戶名。將用戶名保存在數(shù)據(jù)庫(kù)中利用主鍵防止重復(fù)。

實(shí)現(xiàn)私聊的方法有很多種,這里我的實(shí)現(xiàn)方法是這樣的:

① 當(dāng)用戶連接時(shí),把用戶的socket端口對(duì)象使用map進(jìn)行儲(chǔ)存,儲(chǔ)存的key 為用戶的socket對(duì)象,value為用戶的用戶名,寫一個(gè)方法用于更新客戶端列表
② 用戶默認(rèn)用戶名為 <未命名>,指定自定義用戶名時(shí),使用socket.emit方法觸發(fā)服務(wù)端的某個(gè)事件,遍歷map找到與之對(duì)應(yīng)的key,進(jìn)行value修改
③ 發(fā)送消息時(shí),根據(jù)選擇列表來(lái)指定要發(fā)送的人,在服務(wù)端,遍歷map,找到要發(fā)送到的用戶名,進(jìn)行發(fā)送,同時(shí)更新到自己的聊天框

以上就是私聊的簡(jiǎn)單實(shí)現(xiàn)。

下面看一下具體代碼:

//Node.js

const socket = require("socket.io");
//創(chuàng)建一個(gè)websocket服務(wù)器
let socketServer = socket.listen(require("http").createServer((req,resp) => {
 //返回頁(yè)面
 resp.end(require("fs").readFileSync("./socketIOTest1.html"));
}).listen(9999,"localhost",() => {console.log("listening");}));

//創(chuàng)建一個(gè)用于放置用戶對(duì)象的map
let map = new Map();
//用于記錄用戶數(shù)量的變量,并初始化為0
let userCount = 0;
//遍歷map 
let scanMap = func => {
 try{
  map.forEach((value,index,arr) => {
   func(value,index,arr);
  });
 }
 catch(e){
  if(e.message == "break"){
   return;
  }
  else{
   throw e;
  }
 }
}

//通知客戶端彈出對(duì)話框
let showDialog = (socket,msg) => {
 socket.emit("showDialog",msg);
}

//更新用戶列表
let updateList = socket => {
 let userArr = [];
 scanMap((value,index) => {
  if(value != undefined){
   userArr.push(value);
  }
 });
 socket.emit("newUser",userArr);
}

//監(jiān)聽connection 事件
socketServer.on("connection",socket => {
 console.log("有一用戶連接");
 //初始化存儲(chǔ)當(dāng)前socket對(duì)象
 map.set(socket,"<未命名>");
 //將用戶信息寫入map
 socket.on("getUser",user => {
  //修改名稱
  map.set(socket,user);
  scanMap((value,index) => {
   updateList(index);
  });
 });
 //通知所有客戶端更新列表
 scanMap((value,index) => {
  updateList(index);
 });
 //監(jiān)聽客戶端來(lái)的信息
 socket.on("message",msg => {
  //從客戶端接收的消息
  let sender;
  //遍歷所有用戶
  scanMap((value,index) => {
   if(index == socket){
    sender = value;
   }
  });
  scanMap((value,index) => {
   if(msg.person == "all"){
    index.send(sender + " : " + msg.msg);
   }
   else if(msg.person == value){
    socket.send(sender + " : " +msg.msg);
    index.send(sender + " : " +msg.msg);
    throw new Error("break");
   }
  });
 });
 //監(jiān)聽客戶端退出情況
 socket.on("disconnect",() => {
  //用戶退出,從map里刪除該用戶
  map.set(socket,undefined);
  //通知所有用戶更新列表
  scanMap((value,index) => {
   updateList(index);
  });
  console.log("有一用戶退出連接");
 });
}); 

客戶端:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
 <textarea name="" id="content" cols="30" rows="10" ></textarea>
 <input id="write" type="text" placeholder="please write content here">
 <input id="send" type="button" value="send" />
 <input type="text" id="user" placeholder="user">
 <select style="width: 100px;" size="2" name="" id="userList">
  <option value="all">群聊</option>
 </select>
 <script src="./socket.io/socket.io.js"></script>
 <script>
  let send = document.getElementById("send");
  let write = document.getElementById("write");
  let content = document.getElementById("content");
  let user = document.getElementById("user");
  //用戶列表
  let userList = document.getElementById("userList");
  let socket = io.connect();
  //判斷用戶名是否為空
  let isUserEmpty = () => {
   if(user.value == ""){
    alert("請(qǐng)?zhí)顚懹脩裘?);
    return false;
   }
   else {
    return true;
   }
  }
  //監(jiān)聽用戶名變化
  let oldUser;
  user.onblur = () => {
   if(isUserEmpty()){
    //防止重復(fù)發(fā)射
    if(oldUser == user.value){return;}
    oldUser = user.value;
    socket.emit("getUser",user.value);
   }
  }  
  //發(fā)送消息
  send.onclick = () => {
   if(isUserEmpty()){
    let msg = write.value;
    // content.innerHTML = content.value + msg + "\n";
    socket.send({msg:msg,person:userList.value});
   }
   if(select.value == ""){
    alert("請(qǐng)選擇一個(gè)聊天對(duì)象");
   }
  };
  //接收到消息
  socket.on("message",msg => {
   console.log("從服務(wù)器接收到的消息 : " + msg);
   //更新內(nèi)容
   content.innerHTML = content.value + msg + "\n";
  });
  socket.on("disconnect",() => {
   console.log("與服務(wù)器斷開連接");
  });
  //新用戶加入聊天室
  socket.on("newUser",arr => {
   userList.innerHTML = "";
   let all = document.createElement("option");
   all.innerHTML = "群聊";
   all.setAttribute("value","all");
   userList.appendChild(all);
   //添加新用戶
   arr.forEach((value,index) => {
    console.log("value :" + value + "index :" + index);
    let option = document.createElement("option");
    option.innerHTML = value;
    option.setAttribute("value",value);
    userList.appendChild(option);
    userList.setAttribute("size",userList.children.length);
   });
   //默認(rèn)選中群聊
   userList.value = "all";
  });
  //接收服務(wù)器需要彈出對(duì)話框的需求
  socket.on("showDialog",msg => {
   alert(msg);
  });
 </script>
</body>
</html>

代碼的具體我就不在詳細(xì)講解,都標(biāo)有注釋,由于只是用于博文,整體代碼沒(méi)有重構(gòu)優(yōu)化,大家看不懂的可以回復(fù)我,或者有什么地方錯(cuò)誤請(qǐng)指出,我會(huì)及時(shí)改正。

另外在這個(gè)聊天室中,當(dāng)用戶刷新頻率較快時(shí),websocket會(huì)出現(xiàn)偽連接現(xiàn)象。

下面附上我的github地址,大家可以下載我的源碼進(jìn)行修改學(xué)習(xí),共勉。
https://github.com/HaoDaWang/chat

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Node.js學(xué)習(xí)之內(nèi)置模塊fs用法示例

    Node.js學(xué)習(xí)之內(nèi)置模塊fs用法示例

    這篇文章主要介紹了Node.js學(xué)習(xí)之內(nèi)置模塊fs用法,結(jié)合實(shí)例形式詳細(xì)分析了node.js內(nèi)置模塊fs的基本功能、用法與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2020-01-01
  • Node.js的模塊化機(jī)制和Buffer對(duì)象詳解

    Node.js的模塊化機(jī)制和Buffer對(duì)象詳解

    這篇文章主要為大家詳細(xì)介紹了Node.js的模塊化機(jī)制和Buffer對(duì)象,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-02-02
  • 詳解一些適用于Node.js的命名約定

    詳解一些適用于Node.js的命名約定

    這篇文章主要介紹了詳解一些適用于Node.js的命名約定,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 手把手教你用node.js搭建一個(gè)Web服務(wù)

    手把手教你用node.js搭建一個(gè)Web服務(wù)

    Web服務(wù)器一般指網(wǎng)站服務(wù)器,是指駐留于因特網(wǎng)上某種類型計(jì)算機(jī)的程序,下面這篇文章主要給大家介紹了關(guān)于用node.js搭建一個(gè)Web服務(wù)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-04-04
  • node 使用 async 控制并發(fā)的方法

    node 使用 async 控制并發(fā)的方法

    這篇文章主要介紹了node 使用 async 控制并發(fā)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • Node.js使用對(duì)話框ngDialog的示例代碼

    Node.js使用對(duì)話框ngDialog的示例代碼

    本篇文章主要介紹了Node.js使用對(duì)話框ngDialog的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • 一文詳解npm如何更新項(xiàng)目最新依賴包

    一文詳解npm如何更新項(xiàng)目最新依賴包

    npm包的更新速度很快,為了將項(xiàng)目或者全局依賴更新到最新版本,下面這篇文章主要給大家介紹了關(guān)于npm如何更新項(xiàng)目最新依賴包的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • nodejs中fs模塊三種讀寫文件方法的使用區(qū)別

    nodejs中fs模塊三種讀寫文件方法的使用區(qū)別

    Node.js的fs模塊有多種文件操作API,readFile、read、createReadStream讀取文件,writeFile、write、createWriteStream寫入文件,readFile和writeFile將文件寫入緩存區(qū),適合小文件操作,read和write分步驟讀寫,適合大文件,createReadStream和createWriteStream通過(guò)流處理文件
    2024-10-10
  • Nodejs做文本數(shù)據(jù)處理實(shí)現(xiàn)詳解

    Nodejs做文本數(shù)據(jù)處理實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了Nodejs做文本數(shù)據(jù)處理實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • node.js中fs文件系統(tǒng)模塊的使用方法實(shí)例詳解

    node.js中fs文件系統(tǒng)模塊的使用方法實(shí)例詳解

    這篇文章主要介紹了node.js中fs文件系統(tǒng)模塊的使用方法,結(jié)合實(shí)例形式詳細(xì)分析了node.js fs文件系統(tǒng)模塊各種常見(jiàn)方法的基本使用技巧與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2020-02-02

最新評(píng)論

贵溪市| 苗栗县| 定兴县| 池州市| 开化县| 柞水县| 姚安县| 嘉禾县| 丹凤县| 依兰县| 邛崃市| 镇巴县| 清徐县| 威海市| 建阳市| 上高县| 宁夏| 东台市| 肇源县| 五指山市| 静安区| 盱眙县| 台中县| 赫章县| 陆丰市| 土默特左旗| 佛山市| 库伦旗| 灵山县| 浏阳市| 涞源县| 通化县| 夏津县| 天全县| 北流市| 孟津县| 堆龙德庆县| 西畴县| 甘洛县| 厦门市| 西乌|