之前在 f2e-server 里面做了一個(gè)有關(guān)監(jiān)聽(tīng)文件更新专挪,實(shí)時(shí)刷新預(yù)覽頁(yè)面的功能滩愁,基于這個(gè)原理做一個(gè)實(shí)時(shí)消息的推送檢測(cè)也是挺不錯(cuò)的谜叹。
- 首先創(chuàng)建一個(gè)基本的HTTP服務(wù)器, 輸出javascript格式的文本數(shù)據(jù)
http.createServer(function (req, resp){
var res = querystring.parse( url.parse(req.url).query ); // 格式化GET請(qǐng)求數(shù)據(jù)
resp.writeHead(200, {"Content-Type": 'application/javascript'}); //輸出的mime-type是javascript
resp.end( (res.callback || "callback") + '('+JSON.stringify(res)+');' ); //輸出結(jié)果咏窿,方法名可以是傳遞進(jìn)來(lái)的
}).listen(8973);
- 服務(wù)端計(jì)劃僅使用query識(shí)別請(qǐng)求,不關(guān)心path路徑
- name: "昵稱", // 消息發(fā)送來(lái)源, 昵稱
- time: 1746389859, //接收從time時(shí)刻開(kāi)始的所有服務(wù)端消息
- msg: "加入聊天室", //客戶端發(fā)出的消息
- method: "pop" // pop-獲取消息素征;push-發(fā)送消息集嵌; exit-退出聊天室
- msgList: [] //客戶端從服務(wù)端獲取的信息列表,附加在res上返回給客戶端
- 完成基本的保存信息和信息輸出方法
function out(res, resp){
resp.end( (res.callback || "callback") + '('+JSON.stringify(res)+');' );
}
function saveMsg(msg){
mtime = msg.time = +new Date;
list.push({ // list是所有信息列表
name: msg.name,
msg: msg.msg,
time: mtime
});
}
- 根據(jù)不同情況識(shí)別發(fā)生事件
- 如果沒(méi)有傳入時(shí)間御毅,或者請(qǐng)求類型不是GET根欧,或者沒(méi)有提供method參數(shù)
識(shí)別為首次進(jìn)入聊天室,將msg參數(shù)修改成 "加入聊天室"亚享,保存記錄咽块,并且將結(jié)果輸出。
- 如果沒(méi)有傳入時(shí)間御毅,或者請(qǐng)求類型不是GET根欧,或者沒(méi)有提供method參數(shù)
if( !res.time || req.method === "POST" || !res.method ){
res.msg = res.msg || "加入聊天室";
res.msgList = [];
saveMsg(res);
out(res,resp);
}
* 如果是消息提交欺税,直接將信息保存侈沪,也不用返回啥信息列表了,反正客戶端也用不著
else if( "push" === res.method ){
saveMsg(res);
out(res,resp);
}
* 退出的時(shí)候晚凿,事實(shí)上也是進(jìn)行一個(gè)消息提交亭罪,只不過(guò)把msg修改成 **"離開(kāi)聊天室"**
else if( "exit" === res.method ){
res.msg = "離開(kāi)聊天室";
saveMsg(res);
out(res,resp);
}
* 其他情況識(shí)別為獲取信息列表,根據(jù)`res.time`返回?cái)?shù)據(jù)
這里是監(jiān)聽(tīng)更新的核心部分
1. 通過(guò)判斷提交的time和服務(wù)端最近更新時(shí)間mtime是否相等歼秽,
決定是否直接返回響應(yīng)時(shí)間范圍的消息列表应役。
if( res.time != mtime){
res.msgList = list.filter(function(m){
return m.time > res.time;
});
res.time = mtime; // 更新time,返回給客戶端燥筷,客戶端每次獲取列表后更新本地的time參數(shù)
out(res,resp);
}
2. 如果這兩個(gè)時(shí)間相等箩祥,500ms后繼續(xù)回調(diào)自己,回調(diào)夠60次肆氓,才返回一個(gè)空的消息列表袍祖。
else if(repeat >= 60){
res.msgList = [];
out(res,resp);
}else{
repeat++;
setTimeout(getMsg,500);
}
- 數(shù)據(jù)信息的持久化
事實(shí)上到這里,聊天系統(tǒng)的服務(wù)端已經(jīng)可用了谢揪,但是為了將所有的消息記錄都能在服務(wù)端存儲(chǔ)下來(lái)
我們將list信息定時(shí)的存儲(chǔ)到文件里面:
function saveList(){
if( mtime === stime ){ // stime 記錄上一次文件保存的時(shí)間
// 如果最新修改都已經(jīng)保存,等待下次保存
}else{ //否則更新存儲(chǔ)時(shí)間為最新修改時(shí)間
stime = mtime;
var n = 0, tmp;
while(list.length > 100){
tmp = list.splice(0,100); // 將list分割成100條一組蕉陋,存儲(chǔ)到指定文件名文件
fs.writeFile( "data/"+stime+"-"+(n++)+".json", JSON.stringify(tmp,null,4) );
} //剩下不夠100條的還存儲(chǔ)在基本文件下捐凭。
fs.writeFile( "data.json", JSON.stringify(list,null,4) );
}
setTimeout(saveList, 1000*60*2);
}
saveList();
- 客戶端代碼:
- 發(fā)送消息
$("#form").on("submit",function(e){
e.preventDefault();
var data = {
name: $("#name").val(),
method:"push",
msg: $("#msg").val()
};
$("#msg").val("");
$.jsonp(root,data,function(d){
console.log( '發(fā)送成功!' );
$("#msg").focus();
});
});
* 接收消息
window.get = function(){
$.jsonp(root,{
name:$("#name").val(),
method:"pop",
time: time
},function(d){
time = d.time
d.msgList.forEach(function(m){
chat.append( $('<li></li>').text(m.name+":"+m.msg) );
});
window.get();
});
}
window.get();
* 退出頁(yè)面
window.onbeforeunload = function(){
$.jsonp(root,{
name:$("#name").val(),
method:"exit",
time: time
});
};
PS:
在線聊天室: http://shy2850.github.io/wfQuery/demo/09.chatRoom.html
服務(wù)端完整代碼 https://github.com/shy2850/wfQuery/blob/master/demo/node/chat.js
客戶端代碼 https://github.com/shy2850/wfQuery/blob/master/demo/09.chatRoom.html