最近又開始研究Node.JS,也因此開始接觸一些 Module 。
(實際上很久之前就接觸了,只是人懶拉……)
這次接觸的是有名的MVC Framework – Express 和 方便的 Socket.IO 。
說實在的,用來做聊天室真的「非常省時」
(想當初我慢慢寫程式,現在竟然只要半小時……)
大家自己用 NPM 安裝好 Express 後,就用 express -t ejs 生成一個專案吧!
(以下只會針對 Socket.IO 對聊天室製作的程式碼做說明喔!)
app.js
var io = require("socket.io").listen(app); //app 是 var app = express.createServer();
/* .... 略 .... */
app.listen(3000);
//Chat Room
var onlineList = {}; //建立一個線上列表物件
io.sockets.on('connection', function(socket){ //照官方的處理,基本上就是當Clinet連線時要做的事情
socket.emit('reqNickname'); //向Client要求暱稱(其實應該用 join 而不是這樣,因為這樣會造成一些問題)
socket.on('setNickname', function(nickname){ //當Client設定暱稱時
if(!onlineList[nickname]){ //檢查暱稱是否重複
socket.set('nickname', nickname, function(){ //Socket.IO 支援類似 Session 的儲存,這邊用來紀錄使用者暱稱
onlineList[nickname] = socket; //這邊把 socket 物件存進去,用於之後的 Private Message 功能(Socket.IO 的每個 socket 都是對應個別 Client 的,不能搞混)
socket.broadcast.emit('system', {'msg' : '' + nickname + ' join!'}); //使用Broadcast可以發給除了自己外全部的Client (通知某使用者加入聊天室)
socket.emit('ready'); //告訴Client可以開始聊天(此時發訊息等才會解鎖,本範例沒有特別限制)
});
}else{
socket.emit('reqNickname'); //如果暱稱存在,則重新要求暱稱
}
});
socket.on('chat', function(data){ //當Client發送聊天訊息
socket.get('nickname', function(err, nickname){ //先取得暱稱
if(!err){ //檢查是否有發生錯誤
if(data.indexOf('/pm ') === 0){ //檢查是否為 /pm 的開頭,如果是判定為「私訊」
var receiver = data.substr(4); //切割字串
receiver = receiver.substr(0, receiver.indexOf(' ')); //取得收訊人的暱稱
var chatMsg = data.substr(receiver.length + 5); //把剩下文字當要傳送的訊息
if(onlineList[receiver]){ //檢查接收者是否存在
onlineList[receiver].emit('pm', {'nickname' : nickname, 'msg' : chatMsg, 'rec' : receiver}); //發送給接收者私訊
socket.emit('pm', {'nickname' : nickname, 'msg' : chatMsg, 'rec' : receiver}); //對Client也發送一次
}else{
socket.emit('system', {'msg' : '' + receiver + ' not found.'}); //接收者不存在則出現錯誤訊息
}
}else{
//一般情況正常聊天
socket.broadcast.emit('chat', {'nickname' : nickname, 'msg' : data});
socket.emit('chat', {'nickname' : nickname, 'msg' : data});
}
}
});
});
socket.on('disconnect', function(){ //使用者斷開(系統)
socket.get('nickname', function(err, nickname){ //先取得暱稱
if(!err){
if(onlineList[nickname]){ //確認是否存在使用者
socket.broadcast.emit('system', {'msg' : '' + nickname + ' exit.'}); //發送系統訊息告知其他使用者Client離線
}
}
});
});
});
index.ejs
這邊用 jQuery 輔助,沒有做版面排版(CSS則略,因為除了設定 overflow:hide 之外其他沒有特別設定)
<script src="/socket.io/socket.io.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js" type="text/javascript"></script>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
<div id="chatArea">
</div>
<div id="chatForm">
<form action="" method="post" name="charForm" id="chat">
<span id="nickname">Unknow</span> :
<input type="text" name="message" size="50" id="message" />
<input type="submit" name="send" value="Send" id="send" />
</form>
</div>
<script type="text/javascript">
function scrollChatArea()
{
}
var socket = io.connect("dmd.frost.tw", {'port':3030});
var nickname = "Unknow";
socket.on('reqNickname', function(){
nickname = prompt('Type your nickname', '');
socket.emit('setNickname', nickname);
});
socket.on('ready', function(){
$("#nickname").html("<strong>" + nickname + "</strong>");
$("#chat").submit(function(e){
var msg = $('input[name="message"]').val();
socket.emit('chat', msg);
$('input[name="message"]').val('').focus();
return false;
});
});
socket.on('chat', function(data){
$("<div><strong>" + data.nickname + "</strong> Says: " + data.msg + "</div>").hide().appendTo("#chatArea").fadeIn();
$("#chatArea").scrollTop( $("#chatArea").innerHeight() );
});
socket.on('system', function(data){
$("<div>> " + data.msg + "</div>").hide().appendTo("#chatArea").fadeIn();
$("#chatArea").scrollTop( $("#chatArea").innerHeight() );
});
socket.on('pm', function(data){
$("<div><strong>" + data.nickname + "</strong> to <strong>" + data.rec + "</strong> Says: " + data.msg + "</div>").hide().appendTo("#chatArea").fadeIn();
$("#chatArea").scrollTop( $("#chatArea").innerHeight() );
});
</script>
前端使用方法和後端大同小異,都是用 on 和 emit 處理。
如此一來,就完成一個有 PM 功能的聊天室了!
(其實 Socket.IO 還支援 Room 的概念,只要把 Client 用 .join(‘room name’) 就可以了!)
有不清楚的部份請告知一下,我會解釋……
(這篇文章就針對Server-Side做解釋而已,其他有問題還是可以問的~)
首先恭喜還活著-不論是我還是大家?
嗯-非常詳盡的注釋。(下刪十數行字)
……突然有股只回應「感謝大大分享」的衝動。
為了對得起因深思回覆內容而付出了的半小時,只好拋棄良心,離題一點兒。
果然長大後對自己的言行十分嚴謹,碼一大堆字倒頭來想想別人願不願意看,想想別人容不容易回覆,想想有沒有自我中心,一想再想……
來點幕後花絮讓睿智的看懂的沒問題的完全理解的人們回覆得更容易吧。
……例如來個跟進問題或練習……好像有不少關於專業的Blog都這樣做?雖然似乎看見回應是一色一樣的內容會感到有點不爽就是。
再不然陰人,讓讀者抓錯處也不賴啊…呵,不過若然是從Google轉過來為某個問題煩惱的人必定火大。
最後來個首尾呼應,扣緊主題……才怪:
嗯-世界上沒有完美的方法和建議,就這樣子吧。
搜尋 comet 的資來無意間來到這幢, 很喜歡這個 blog , 樣式看得很舒服, 內容也很合看。
但忍不住要報告一下, 這站的載入時間太慢了, 我從香港進來, 每一頁要用上七至十二秒, 有時侯還會停止沒回應; 瀏覽器捲軸當掉。
我用的瀏覽器是 Chrome 14.0.835.202 m。
主要慢的東西大都是外置 js 所連結的資源。
我會去檢查一下,廣告的JS可能就沒辦法去掉,畢竟那是網站營運的資金來源。