websockt 實(shí)現(xiàn)簡易聊天室

一穆壕、原生websocket


首先介紹一下實(shí)現(xiàn)即時通信方式,有如下三種:
1. 輪詢Ajax——不停詢問其屏,浪費(fèi)性能喇勋;
2.服務(wù)器響應(yīng)流:服務(wù)器不關(guān)閉連接,一次響應(yīng)偎行,一直保持連接——資源有限川背;
3. 使用websocket——有兼容問題

websocket是HTML5中新推出的通信協(xié)議贰拿,是在原來http協(xié)議基礎(chǔ)上進(jìn)行升級的,屬于長連接通信渗常。
原來http協(xié)議為一問一答形式壮不,先有請求才有響應(yīng)的機(jī)制。而websocket其底層利用了TCP協(xié)議(客戶端與服務(wù)器建立連接之后皱碘,便可以自由通信)询一,服務(wù)器也可以主動發(fā)請求給客戶端。
注:websocket只存在于前臺癌椿,是html5帶的一種東西健蕊。后臺本就有socket

原生的websocket中:

  • 客戶端可用直接通過new Websocket('ws://xxxx/')建立通訊,通過原生的一系列事件與方法來實(shí)現(xiàn)與服務(wù)端的溝通踢俄;
  • 服務(wù)端基于net模塊(實(shí)現(xiàn)TCP協(xié)議)+crypto模塊(保證安全)來創(chuàng)建連接和監(jiān)聽通信數(shù)據(jù)缩功;
  • 服務(wù)端原生的socket實(shí)現(xiàn)十分復(fù)雜,包括:
    1. 響應(yīng)客戶端的請求報文都办,從請求報文中拆分出連接的版本等基本信息判斷是否需要響應(yīng)嫡锌;
    2. 從請求報文中解析出掩碼并根據(jù)掩碼回寫響應(yīng)報文;
    3. 最復(fù)雜的在于數(shù)據(jù)幀的解析琳钉,涉及到幀結(jié)構(gòu)的拆分势木,數(shù)據(jù)的加密,二進(jìn)制數(shù)據(jù)的讀寫等歌懒。
ws請求報文

websocket幀結(jié)構(gòu)

總之啦桌,原生的websocket使用起來十分不便,在實(shí)際應(yīng)用中及皂,多使用第三方庫甫男。

二、中間件與第三方庫


核心思想socket.io——交互方式可能通過websocket/輪詢Ajax/服務(wù)器響應(yīng)流實(shí)現(xiàn):1.服務(wù)器可主動發(fā)數(shù)據(jù)到客戶端验烧;2.客戶端可通過服務(wù)器向客戶端發(fā)數(shù)據(jù)板驳。

客戶端:依賴socket.io-client
  1. 引入js文件 <script src = "/socket.io.js"></script>

  2. 建立連接 const socket = io('[http://localhost:8888');](http://localhost:8888');/)

  3. 監(jiān)聽事件 socket.on(eventName, data => {});

  4. 向服務(wù)器傳遞消息 socket.emit(name, value);

服務(wù)端:依賴koa-socket
  1. 引入IO并實(shí)例化對象 const IO = require("koa-socket"); const io = new IO();

  2. io監(jiān)管app io.attach(app);

  3. 聲明事件 io.on(eventName, sock=> {});

  4. 接收客戶端消息

    io.on(name, ctx => {
        //ctx.data就是前臺發(fā)送的數(shù)據(jù)
        ctx.socket.emit(name, value);        //回應(yīng)消息,name需要與前臺對應(yīng)
    })
  1. 廣播事件
  • 廣播事件 io.broadcast(name, value)
  • 私聊推送 app._io.to(usersId).emit(name, value)
  • 加入群組 ctx.socket.socket.join(id)
  • 群組推送 ctx.socket.socket.to(groupId).emit(name, value)

三碍拆、案例——簡易聊天室

服務(wù)端:
const Koa = require("koa");
const static = require("koa-static");
const IO = require("koa-socket");

const app = new Koa();
//實(shí)例化socket.io對象
const io = new IO();
//io監(jiān)管app
io.attach(app);
//模擬用戶信息
let user = {};
//記錄用戶信息
io.on('user', ctx => {
  //存儲用戶通信id
  user[ctx.data.userName] = ctx.socket.id;
  //添加分組信息
  ctx.socket.socket.join(ctx.data.groupId);
})
io.on('msg', ctx => {
  //獲取客戶端數(shù)據(jù)并廣播
  let {to, from, content, groupId} = ctx.data;
  
  if(groupId == '0') {          //公共消息則廣播給所有人
    io.broadcast('msg', {from, content});
  } else if(to == 'all') {      //群組消息則發(fā)送給整個群組
    //向監(jiān)管部分推送消息
    ctx.socket.socket.to('0').emit('msg', {from: from, content});
    //向群組成員推送消息
    ctx.socket.socket.to(groupId).emit('msg', {from: from, content});
    //向發(fā)起人推送消息
    app._io.to(user[from]).emit('msg', {from: from, content});
  } else {                      //私聊信息則只發(fā)給特定的人
    let fromStr = from + '(私聊)';
    //向發(fā)起人推送消息
    app._io.to(user[from]).emit('msg', {from: fromStr, content});
    //私人消息則僅僅發(fā)送給對應(yīng)的人
    app._io.to(user[to]).emit('msg', {from: fromStr, content});
  }
})

//配置靜態(tài)資源文件
app.use(static('./'));
//開啟服務(wù)器
app.listen(8888, ()=>{
  console.log('The server is running...');
})
客戶端
 <!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>Document</title>
  <script src="./socket.io.js" charset="utf-8"></script>
  <style media="screen">
    .chatroom {
      display: none;
    }
    .myOwnMessage {
      color: #205da1;
    }
  </style>
</head>
<body>
  <!-- 用戶信息設(shè)置窗口 -->
  <div class="setting">
    輸入你的昵稱:<input type="text" name="username">
    選擇加入的群聊組: <select class="groups" name="">
      <option value="0">江湖</option>
      <option value="1">魔法</option>
      <option value="2">物理</option>
    </select>
    <button type="button" name="button" id="setName">提交</button>
  </div>
  <!-- 聊天區(qū)域 -->
  <div class="chatroom">
    <div class="info">
      <span id="user">xxx</span>,您好倔监!歡迎來到聊天室~
    </div>
    <!-- 信息列表 -->
    <ul id="chatList">
    </ul>
    <!-- 信息輸入?yún)^(qū)域 -->
    <textarea id="content" name="content" rows="8" cols="80"></textarea>
    <button type="button" id="submit" name="button">發(fā)送</button>
    <button type="button" id="toBoss" name="button">私聊給老板</button>
  </div>
</body>
<script type="text/javascript">
  //建立連接
  const socket = io('[http://localhost:8888');](http://localhost:8888');/)
  //監(jiān)聽事件
  socket.on('connect', data => {
    console.log('連接上了!')
  });
  socket.on('disconnect', data => {
    console.log('斷開連接了!')
  });
  //獲取元素
  let oSubmit = document.getElementById('submit');
  let oSubmitToBoss = document.getElementById('toBoss');
  let oChat = document.getElementsByClassName('chatroom')[0];
  let oSettingBox= document.getElementsByClassName('setting')[0];
  let oSetName = document.getElementById('setName');
  let oText = document.getElementById('content');
  //記錄用戶名稱與分組信息
  let username, groupId;
  //綁定設(shè)定昵稱事件
  oSetName.onclick = function () {
    //獲取昵稱
    userName = document.querySelectorAll('input[name="username"]')[0].value;
    groupId = document.querySelectorAll('.groups')[0].value;
    //隱藏昵稱設(shè)置區(qū)域
    this.parentNode.style.display = 'none';
    //顯示聊天區(qū)域并發(fā)送用戶信息
    socket.emit('user', {
      userName, groupId
    });
    document.getElementById('user').innerHTML = userName;
    oChat.style.display = 'block';
  }
  function sendMsg(toWho) {
    //獲取內(nèi)容
    let sContent = oText.value;
    oText.value = "";
    //發(fā)送通信數(shù)據(jù)
    socket.emit('msg', {
      to: toWho,
      groupId: groupId,
      from: userName,
      content: sContent
    });
  }
  //綁定事件浩习,發(fā)送聊天信息
  oSubmit.onclick = function () {
    sendMsg('all');
  }
  //發(fā)送私聊信息
  oSubmitToBoss.onclick = function () {
    sendMsg('boss');
  }
  //監(jiān)聽服務(wù)器的廣播
  socket.on('msg', sock => {
    let oUl = document.getElementById('chatList');
    let oLi = document.createElement('li');
    //判斷是否為自己發(fā)送的消息济丘,是則設(shè)置樣式
    if(sock.from.indexOf(userName) != -1) {
      oLi.className = 'myOwnMessage';
    }
    oLi.innerHTML = `${sock.from}:${sock.content}`;
    oUl.appendChild(oLi);
  })
</script>
</html>
  • 實(shí)現(xiàn)效果


    聊天室效果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谱秽,一起剝皮案震驚了整個濱河市洽蛀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌疟赊,老刑警劉巖郊供,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異近哟,居然都是意外死亡驮审,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門吉执,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疯淫,“玉大人,你說我怎么就攤上這事戳玫∥醪簦” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵咕宿,是天一觀的道長币绩。 經(jīng)常有香客問我,道長府阀,這世上最難降的妖魔是什么缆镣? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮肌似,結(jié)果婚禮上费就,老公的妹妹穿的比我還像新娘。我一直安慰自己川队,他們只是感情好力细,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著固额,像睡著了一般眠蚂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上斗躏,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天逝慧,我揣著相機(jī)與錄音,去河邊找鬼啄糙。 笑死笛臣,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的隧饼。 我是一名探鬼主播沈堡,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼燕雁!你這毒婦竟也來了诞丽?” 一聲冷哼從身側(cè)響起鲸拥,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤僧免,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后撞叨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勃痴,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年劣领,在試婚紗的時候發(fā)現(xiàn)自己被綠了铁材。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡村生,死狀恐怖饼丘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卫病,我是刑警寧澤典徘,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布逮诲,位于F島的核電站,受9級特大地震影響梅鹦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜齐唆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一蝶念、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧媒殉,春花似錦、人聲如沸全封。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攒暇。三九已至,卻和暖如春就轧,著一層夾襖步出監(jiān)牢的瞬間田度,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工乎莉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奸笤,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓肥橙,卻偏偏與公主長得像秸侣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子味榛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容