iT邦幫忙

DAY 9
2

Node.js 系列學習日誌系列 第 9

Node.js 系列學習日誌 #9 - 運用 express, socket.io, boostrap 建立一個簡單的聊天室功能

寫過網頁的大大們,大致上都了解 http 是一種無狀態的形式,假設頁面登進來的使用者,為了保存它的登入資訊,通常會使用 session 的觀念作為存放的物件,否則 server 端就不知道 client 端登進來的人是誰,不過今天不講session 的部分,而是我們要透過 socket.io 做雙向溝通,只要一旦建立了連線之後,就可以立即傳訊息出去,Client 的部分也立即接收到,相互取得訊息。

今天要來透過介紹的是簡單的聊天室功能,讓大家了解 node.js 在 socket.io 運作的方式,以下是本章節會學習到的部分:

使用 Bootstrap 規劃製作前端頁面框架
使用 express 套功能
使用 socket.io 套件
開發聊天室功能

本章節程式同步放置於 :https://github.com/weijutu/nodejs-simple-chatroom

首先我們先定義好需要的動向有:

  1. 進入聊天室之前需要先輸入姓名
  2. 進入聊天室頁面之後,左邊區域負責聊天的顯示部分,右邊區域負責登入的名單
  3. 聊天功能會有個按鈕,當使用者輸入訊息後,按下按鈕送出
  4. 瀏覽器關閉時,也等於退出聊天功能

首先建立一個 nodejs-simple-chatroom 資料夾,安裝 express, socket.io 相依套件,package.json 檔案內容為:

{
     "name": "nodejs-simple-chatroom",
     "version": "0.0.1",
     "private": true,
     "dependencies": {
          "socket.io": "1.0.3",
          "express": "4.4.0"
     }
}

(安裝相依套件方式請詳情:http://ithelp.ithome.com.tw/ironman7/app/article/dev/recent/10158140)
建立 package.json 完成之後,先將畫面基礎規劃完成

client 端:
當多開幾個瀏覽器,可以輸入進入聊天的姓名,以下是在聊天之前先輸入的欄位:

在這邊使用了 bootstrap 前端框架來美化一下聊天室外觀

<div class="container">
          <div id="nickWrap">
               <form id="setNick" role="form">
                    <h2 class="form-signin-heading">請輸入您的名字:</h2>
                    <p id="nickError"></p>
                    <div style="margin:20px;">
                         <input size="35" id="txtNickname" required="" />
                    </div>
                    <input type="Submit" class="btn btn-lg btn-primary">
               </form>
          </div>
          <div id="contentWrap">
               <div id="chatWrap" class="panel panel-default" >
                      <div class="panel-heading">即時聊天</div>
                      <div class="panel-body">
                           <div class="row">
                                <div class="col-md-9">
                                     <div id="chat"></div>
                                  <form id="send-message">
                                    <div class="input-group">
                                      <input type="text" class="form-control" placeholder="請輸入聊天訊息..." id="message">
                                      <span class="input-group-btn">
                                        <button type="submit" class="btn btn-default">送出留言</button>
                                      </span>
                                    </div>
                                  </form>
                              </div>
                              <div class="col-md-3">
                                    線上名單:
                                   <div id="users"></div>
                              </div>
                         </div>
                      </div>
               </div>
          </div>
          <script type="text/javascript" src="//code.jquery.com/jquery-latest.min.js"></script>
          <script type="text/javascript" src="/socket.io/socket.io.js"></script>
     </div>

javascript 部分:

這部分寫在 client 裏面,作為控制像的事件觸發操作

<script type="text/javascript">
          $(function(){
               var socket = io.connect();
               var $frmMessage = $('#send-message');
               var $frmNick = $('#setNick');
               var $nickError = $('#nickError');
               var $nickBox = $('#txtNickname');
               var $boxMessage = $('#message');
               var $chat = $('#chat');
              
               $frmNick.submit(function(e){
                    console.log($nickBox.val());
                    console.log('hi, frmNick');
                    e.preventDefault();
                   
                    socket.emit('new user', $nickBox.val() );
                   
                    $nickBox.val('');

                    $('#nickWrap').hide();
                    $('#contentWrap').show();
                   
               });

               $frmMessage.submit(function(e){
                    e.preventDefault();
                    socket.emit('send message', $boxMessage.val().trim());
                    $boxMessage.val('');
               });


               socket.on('usernames', function(data){
                    var sb = '';
                    for(var d = 0; d < data.length; d++ ) {
                         console.log(data[d]);
                         sb += data[d] + "<br />";
                    }
                    $('div#users').html(sb);
               });


               socket.on('chat', function(server,msg){
                   
                    var now = new Date();
                    var datetime = now.getFullYear()+'/'+(now.getMonth()+1)+'/'+now.getDate();
                      datetime += ' '+now.getHours()+':'+now.getMinutes()+':'+now.getSeconds();

                    $chat.append("<br /><i>系統訊息: <b>[ " + msg + " ]</b> (" +
                         datetime + ")</i><br />");
               });


               socket.on('new message', function(data){
                    var msg = data.msg;
                    var name = data.nick;

                    var now = new Date();
                    var datetime = now.getFullYear()+'/'+(now.getMonth()+1)+'/'+now.getDate();
                      datetime += ' '+now.getHours()+':'+now.getMinutes()+':'+now.getSeconds();

                    $chat.append("<b>" + name + " </b>: " + msg + " (<i>" + datetime + "<i>)<br />");
               });
          });
</script>

server 端:(app.js)

var express = require('express'),
     app = express(),
     server = require('http').createServer(app),
     io = require('socket.io').listen(server),
     nicknames = [];


server.listen(3000);


app.get('/', function(req, res){
     res.sendfile(__dirname + '/index.html');
});
app.use('/public', express.static(__dirname + '/public'));


io.sockets.on('connection', function(socket) {
     socket.on('new user', function(data){
          console.log(data);
          if (nicknames.indexOf(data) != -1) {


          } else {
               socket.emit('chat', 'SERVER', '歡迎光臨 ' + data);


               socket.nickname = data;
               nicknames.push(socket.nickname);
               io.sockets.emit('usernames', nicknames);
               updateNicknames();
          }
     });


     function updateNicknames(){
          io.sockets.emit('usernames', nicknames);
     }


     //
     socket.on('send message', function(data){
          io.sockets.emit('new message', { msg: data, nick: socket.nickname });
     });


     socket.on('disconnect', function(data){
          if (!socket.nickname) return;
          io.sockets.emit('chat', 'SERVER', socket.nickname + ' 離開了聊天室~');
          nicknames.splice(nicknames.indexOf(socket.nickname), 1);
          updateNicknames();
     });
});

結果:

(內容取至於 ptt joke https://www.ptt.cc/bbs/joke/M.1412577767.A.0EB.html)

已上圖來講,原本有三個聊天者,當某一個使用者關閉瀏覽器 離開聊天室時,將會把使用者從名單上移除。
本章節是個簡易的聊天室功能,當然也可以進階一點做一些顯示上的呈現,或者將這些的留言都使用資料庫(例如:mongodb)儲存這些訊息。

參考資料:
http://getbootstrap.com/
http://expressjs.com
http://socket.io/
https://www.ptt.cc/bbs/joke/M.1412577767.A.0EB.html


上一篇
Node.js 系列學習日誌 #8 - 使用 express, ejs 實作讀取靜態 JSON 格式資料
下一篇
Node.js 系列學習日誌 #10 - 用 Web Storm Javascript IDE 開發 Node.js 應用程式
系列文
Node.js 系列學習日誌30

尚未有邦友留言

立即登入留言