寫過網頁的大家們,大致上都了解 http 是一種無狀態的形式,假設頁面登進來的使用者,為了保存它的登入資訊,通常會使用 session 的觀念作為存放的物件,否則 server 端就不知道 client 端登進來的人是誰,不過今天不講session 的部分,而是我們要透過 socket.io 做雙向溝通,只要一旦建立了連線之後,就可以立即傳訊息出去,Client 的部分也立即接收到,相互取得訊息。
今天要來透過介紹的是簡單的聊天室功能,讓大家了解 node.js 在 socket.io 運作的方式,以下是本章節會學習到的部分:
使用 Bootstrap 規劃製作前端頁面框架
使用 express 套功能
使用 socket.io 套件
開發聊天室功能
本章節程式同步放置於 :https://github.com/weijutu/nodejs-simple-chatroom
首先我們先定義好需要的動向有:
進入聊天室之前需要先輸入姓名
進入聊天室頁面之後,左邊區域負責聊天的顯示部分,右邊區域負責登入的名單
聊天功能會有個按鈕,當使用者輸入訊息後,按下按鈕送出
瀏覽器關閉時,也等於退出聊天功能
首先建立一個 nodejs-simple-chatroom 資料夾,安裝 express, socket.io 相依套件,package.json 檔案內容為:
1 2 3 4 5 6 7 8 9 { "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 前端框架來美化一下聊天室外觀
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <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 裏面,作為控制像的事件觸發操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <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) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 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(); }); });
結果
已上圖來講,原本有三個聊天者,當某一個使用者關閉瀏覽器 離開聊天室時,將會把使用者從名單上移除。 本章節是個簡易的聊天室功能,當然也可以進階一點做一些顯示上的呈現,或者將這些的留言都使用資料庫(例如:mongodb)儲存這些訊息。
參考資料 http://getbootstrap.com/ http://expressjs.com http://socket.io/ https://www.ptt.cc/bbs/joke/M.1412577767.A.0EB.html http://segmentfault.com/a/1190000000479518