聊天服務器開發
npm install --save express
npm install --save socket.io
文件闡述
index.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.send('<h1>Welcome Realtime Server</h1>');
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
var onlineUsers = new Array();
var users = {};
io.sockets.on('connection', function (socket) {
io.sockets.emit('connect',{'status':'正確'});
console.log("new conns join ..");
socket.on('private message', function (from,to,msg) {
console.log('I received a private message by ', from, ' say to ',to, msg);
if(to in users){
users[to].emit('to'+to,{mess:msg});
}
});
socket.on('new user',function(data){
//console.log("socket標示爲:"+socket.id);
if(data in users){
}else{
var nickname = data;
users[nickname]= socket;
onlineUsers.push(data);
console.log('用戶'+data+'加入聊天室');
console.log('當前的在線用戶有 '+onlineUsers);
}
io.sockets.emit('online users',onlineUsers);
});
socket.on('disconnect', function () {
var logoutUserName ;
for(var obj in users){
console.log("obj的值是:"+obj);
if(users[obj] == socket){
console.log(obj+"用戶退出聊天室");
logoutUserName = obj;
delete users[obj];
}
}
for(var i = 0; i<onlineUsers.length; i++){
if(onlineUsers[i] == logoutUserName){
onlineUsers.splice(i,i);
}
}
console.log("當前在線人員:"+onlineUsers);
//更新在線用戶
io.sockets.emit('online users',onlineUsers);
io.sockets.emit('user disconnected');
});
});
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="format-detection" content="telephone=no"/>
<meta name="format-detection" content="email=no"/>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport">
<title>多人聊天室</title>
<!--[if lt IE 8]><script src="./json3.min.js"></script><![endif]-->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="http://127.0.0.1:3000/socket.io/socket.io.js"></script>
</head>
<body>
<script>
$(function(){
var onlineUsers;
var socket;
var userName;
$("#login").on("click",function(){
socket = io.connect('ws://localhost:3000');
socket.on('connect', function (data) {
userName = $('#user_name').val();
socket.emit('new user',userName);
$('#div').show();
//接受聊天信息
socket.on('to'+userName, function (data) {
alert("來消息了"+data.mess);
console.log("來消息了"+data.mess);
$message_list.append('<li>'+data.from+'說'+data.message+'</li><li>');
});
//獲得當前在線人員
socket.on("online users",function(data){
onlineUsers = data;
console.log("刷新在線人數");
$("#select").empty();
for(var j=0; j< onlineUsers.length; j++){
var option = $("<option value='"+onlineUsers[j]+"'>"+onlineUsers[j]+"</option>");
$("#select").append(option);
}
});
});
});
$("#send").click(function(e){
var msg = $('#message').val(),
to = $('#select').val(),
$message_list = $('#message_list');
socket.emit('private message',userName,to,msg);
});
});
</script>
<div>
姓名:<input id="user_name" type="text"><br>
<input type="button" value="登陸" id="login">
</div>
<div id="div" style="display:none">
在線用戶:<select id="select"></select><br>
消息內容:<input type="text" id="message"><button type="button" id="send">發送</button>
</div>
<ul id="message_list">
</ul>
</body>
</html>
1) JS 對象處理
//js 對象
var a = {};
//特殊的方法對對象進行復制
a['a'] = "aaa";
a['b'] = "bbb";
a['c'] = "ccc";
console.log(a);
//結果
//{a:'aaa',b:'bbb',c:'ccc'}
for(var obj in a){
//遍歷對象中所有的屬性
if(a[obj] == "bbb"){
console.log(a[obj]);
//刪除該屬性
delete a[obj];
}
if(obj == 'function'){
console.log('這是一個方法');
}
console.log(obj);
}
2)socketJs 事件語法
服務器信息傳輸
// send to current request socket client
socket.emit('message', "this is a test");
// sending to all clients except sender
socket.broadcast.emit('message', "this is a test");
// sending to all clients in 'game' room(channel) except sender
socket.broadcast.to('game').emit('message', 'nice game');
// sending to all clients, include sender
io.sockets.emit('message', "this is a test");
// sending to all clients in 'game' room(channel), include sender
io.sockets.in('game').emit('message', 'cool game');
// sending to individual socketid
io.sockets.socket(socketid).emit('message', 'for your eyes only');
請注意,由於版本問題如果上述的io.sockets.scoket(socketid).emit(),該方法不能正常使用,請這樣寫,io.sockets.connected[socketid].emit('message',msg);
上述集中方式爲socket.io常用的數據傳輸方式
io.sockets.on('connection', function (socket) {
});
回調函數的socket參數爲一個 client
與服務器的連接標示,不同的 client
會有不同的連接標示。
不分組,數據傳輸
-
socket.emit
socket.emit
信息傳輸對象爲當前socket
對應的client
,各個client socket
相互不影響。 -
socket.broadcast.emit
socket.broadcast.emit
信息傳輸對象爲所有client
,排除當前socket
對應的client
。 -
io.sockets.emit
信息傳輸對象爲所有client
。
分組數據傳輸
類似於之前提過的 of
方法生成命名空間來管理用戶, socket.io
可以使用分組方法, socket.join()
,以及與之對應的 socket.leave()
。
io.sockets.on('connection', function (socket) {
socket.on('firefox', function (data) {
socket.join('firefox');
});
socket.on('chrome',function(data){
socket.join('chrome');
});
});
假設有兩個聊天室,一個名爲firefox,另一個爲chrome,客戶端操作
socket.emit('firefox')
,就可以加入 firefox
聊天室; socket.emit('chrome')
,就可以加入 chrome
聊天室;
向一個分組傳輸消息,有兩種方式:
socket.broadcast.to('chrome').emit('event_name', data);
//emit to 'room' except this socket client
io.sockets.in('chrome').emit('event_name', data)
//emit to all socket client in the room
broadcast
方法允許當前 socket
client
不在該分組內。
可能有一個疑問,一個 socket
是否可以同時存在於幾個分組,等效於一個用戶會同時在幾個聊天室活躍,答案是”可以“, socket.join()
添加進去就可以了。官方提供了訂閱模式的示例:
socket.on('subscribe', function(data) {
socket.join(data.room);
})
socket.on('unsubscribe', function(data) {
socket.leave(data.room);
})
後臺處理訂閱/退訂事件
socket = io.connect('http://127.0.0.1:1338/');
socket.emit('subscribe',{"room" : "chrome"};
socket.emit('unsubscribe',{"room" : "chrome"};
前端觸發訂閱/退訂事件,就可以加入對應的聊天室。 通過 of
方法也可以通過劃分命名空間的方式,實現聊天室功能,但不如分組管理來的方便。
Socket.io難點大放送(暫時沒有搞定)
- 授權驗證
socket
連接需要添加權限驗證,讓已登錄的用戶socket
連接到服務器,未登錄的用戶無條件拒絕。全局授權管理如下:
io.sockets.authorization(function (handshakeData, callback) {
callback(null, true);
}).
callback
函數有兩個參數,第一個爲 error
,第二個參數爲是否授權bool值,通過授權回調函數應爲 callback(null,true)
,其它情況下都爲拒絕建立連接。
按照web的開發方式,檢測是否登錄首選 cookie-session
來實現,問題也是出在這裏。 websocket
握手階段屬於 HTTP
協議,簡單來說是可以讀到cookie,就可以實現session。
+ 精準單用戶推送
理論上來說
// sending to individual socketid
io.sockets.socket(socketid).emit('message', 'for your eyes only');
就可以向一個特定用戶推送消息,但是如何獲得這個 socketId
,就是生成一個哈希數組,key爲username,值爲socket.id,這樣就可以通過用戶名獲取對應的id,進而可以向特定client推送消息。
如果是想關機的時候讓服務不間斷,後臺運行,nohup node index.js(文件名) &如果是想簡單啓動,寫個啓動腳本,將nohup node index.js(文件名) 寫入.sh文件