Node.js、Express、Socket.io 入門
前言
周末斷斷續續的寫了第一個socket.ioDemo。初次socket.io是從其官網看到的,看著get started做了一遍,根據官網的Demo能提供簡單的服務端和客戶端通訊。 這個Demo的過程中用到最多的就是訂閱事件、觸發事件、廣播事件。
根據官網完成Demo后,看到下面提到了幾個問題,又繼續實現了四個功能,其它幾個還要繼續實現。
①、當有新用戶登錄或離開時廣播消息。
②、添加昵稱。我在demo中的做法是把用戶輸入的第一條消息作為昵稱。
③、發送消息時自己發送的消息不再給自己發送,其實也就是只調用廣播(socket.broadcast.emit)消息的方法即可。
④、顯示當前在線用戶和在線人數。
開發環境
node:0.12.7
express:4.13.7
socket.io:1.3.7
官網Demo中遇到的問題
①、客戶端html頁面<script src="/socket.io/socket.io.js"></script>這樣引用js代碼沒搞明白,百度搜索了一下,有人解釋說是因為express提供的框架轉化了路徑,所以你這樣做是可以引用到該js的,剛接觸應該會感到意外,但是我實驗如果你只是 引用了express 而不是 用express創建項目 的話可能還是不管用。
②、在體驗Demo并且看其他人寫的例子中發現,很多情況下客戶端和服務端觸發的事件名稱都相同,不理解這種情況他們是同一個事件嗎? 會不會產生沖突呢?
效果圖
1、系統初始化,當你打開頁面時會提示你連接至服務器,你輸入第一條消息就是昵稱。
2、輸入昵稱,以同樣的方式再打開幾個Tab頁,輸入昵稱。
3、兩個客戶端聊天
服務端實現
var express=require('express');
var app=express();
var http=require('http').Server(app);
var io=require('socket.io')(http);
app.get('/',function(req,res){
res.sendFile(__dirname+'/index.html');
});
var onlineUserCount=0; //客戶端連接數量
var onlineUsers={}; //統計客戶端登錄用戶
io.on('connection',function(socket){
socket.emit('open'); //通知客戶端已連接
//構造客戶端對象
var client={
socket:socket,
name:false
}
//監聽客戶端的chat message事件, 該事件由客戶端觸發
//當服務端收到消息后,再把該消息播放出去,繼續觸發chat message事件, 然后在客戶端監聽chat message事件。
socket.on('chat message',function(msg){
console.log('chat message:'+msg);
var obj={time:getTime()}; //構建客戶端返回的對象
//判斷是不是第一次連接,以第一條消息作為昵稱
if(!client.name){
onlineUserCount++;
client.name=msg;
obj['text']=client.name;
obj['author']='Sys';
obj['type']='welcome';
obj['onlineUserCount']=onlineUserCount;
socket.name=client.name; //用戶登錄后設置socket.name, 當退出時用該標識刪除該在線用戶
if(!onlineUsers.hasOwnProperty(client.name)){
onlineUsers[client.name]=client.name;
}
obj['onlineUsers']=onlineUsers; //當前在線用戶集合
console.log(client.name+' login,當前在線人數:'+onlineUserCount);
//返回歡迎語
socket.emit('system',obj); //發送給自己的消息
//廣播新用戶已登錄
socket.broadcast.emit('system',obj); //向其他用戶發送消息
}else{
//如果不是第一次聊天,則返回正常的聊天消息
obj['text']=msg;
obj['author']=client.name;
obj['type']='message';
console.log(client.name+' say:'+msg);
socket.emit('chat message',obj); //發送給自己的消息 , 如果不想打印自己發送的消息,則注釋掉該句。
socket.broadcast.emit('chat message',obj); //向其他用戶發送消息
}
//io.emit('chat message',msg);
});
socket.on('disconnect',function(){
onlineUserCount--;
if(onlineUsers.hasOwnProperty(socket.name)){
delete onlineUsers[client.name];
}
var obj={
time:getTime(),
author:'Sys',
text:client.name,
type:'disconnect',
onlineUserCount:onlineUserCount,
onlineUsers:onlineUsers
};
//廣播用戶退出
socket.broadcast.emit('system',obj); //用戶登錄和退出都使用system事件播報
console.log(client.name+' disconnect,當前在線人數:'+onlineUserCount);
});
});
http.listen(3000,function(){
console.log('server begin...');
});
var getTime=function(){
var date = new Date();
return date.getHours()+":"+date.getMinutes()+":"+date.getSeconds();
}
var getColor=function(){
var colors = ['aliceblue','antiquewhite','aqua','aquamarine','pink','red','green',
'orange','blue','blueviolet','brown','burlywood','cadetblue'];
return colors[Math.round(Math.random() * 10000 % colors.length)];
}<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
div { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
div input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
div button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
p{padding:5px 10px;}
</style>
</head>
<body>
<p id="onlineUser">在線人數:0</p>
<ul id="messages"></ul>
<div action="">
<input id="m" autocomplete="off" /><button>Send</button>
</div>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script type="text/javascript">
var myName=false;
var socket= io('http://localhost:3000');
socket.on('open',function(){
$('#messages').append($('<li>').text('已連接至服務器,請輸入昵稱'));
});
//監聽system事件,判斷welcome或者disconnect,打印系統消息
socket.on('system',function(json){
var sep='';
var onlinehtml='';
var onlineUsers=json.onlineUsers;
for(key in onlineUsers){
if(onlineUsers.hasOwnProperty(key)){
onlinehtml+=sep+onlineUsers[key];
sep='、';
}
}
if(json.type==='welcome'){
$('#messages').append($('<li>').text('Sys('+json.time+')welcome '+json.text));
$('#onlineUser').text('在線人數:'+json.onlineUserCount+'。在線列表:'+onlinehtml);
}else if(json.type==='disconnect'){
$('#messages').append($('<li>').text('Sys('+json.time+')bye '+json.text+''));
$('#onlineUser').text('在線人數:'+json.onlineUserCount+'。在線列表:'+onlinehtml);
}
});
//監聽服務端的chat message事件,接受每一條消息
socket.on('chat message',function(json){
$('#messages').append($('<li>').text(json.author+'('+json.time+')'+':'+json.text));
});
$('#m').keydown(function(e){
if(e.keyCode===13){
socket.emit('chat message',$('#m').val());
//socket.send($('#m').val());
$('#m').val('');
if(myName===false){
myName=$('#m').val();
}
}
})
</script>
</body>
</html>總結
做這個Demo的過程中,感覺目前用到最多的就是 訂閱事件 和 發布事件 ,然后客戶端和服務端接受相應的參數,另外一個就是服務端和客戶端通信可以完全用JSON格式傳參,的確很方便。 目前掌握的方法就是socket.emit()和socket.broadcast.emit(),還沒有搞明白emit()和send()的區分。
提供代碼下載地址: http://pan.baidu.com/s/1mgm12Rm