寫過網頁的大大們,大致上都了解 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 檔案內容為:
{
"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