socketIo 搭建長連接(1)

聊天服務器開發

環境搭建
首先下載node.js,前往官網下載並安裝,在此中需要下載安裝express和socket,前者爲node開發web的環境,後者爲我們開發聊天服務器的js
搭建項目
1.新建文件夾,qqDemo
2.在項目下安裝express和socket.io
npm install --save express  
npm install --save socket.io  
3.新建一個index.js作爲啓動js
4.新建index.html作爲前端展示
5.啓動 進入qqDemo文件夾中,使用命令 node index.js啓動站點服務

文件闡述

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文件

學習前人經驗

NodeJs集成Express開發web站點
http://blog.fens.me/nodejs-express3/

NodeJs和socketJs開發私人聊天
http://www.2cto.com/kf/201403/285640.html

SocketIoJs 創建連接參數
http://www.kazaff.me/2014/03/12/

socketIo三種讀寫方式,可用於發送圖片
http://blog.csdn.net/awangz/article/details/8954798

NodeJs和socketJs開發公衆聊天室
http://www.open-open.com/lib/view/open1402479198587.html

前端GruntJs開發web項目
http://www.cnblogs.com/wangfupeng1988/p/4561993.html

socket.io 入門整理,很好的一篇,包括存儲數據和接收消息,命名空間
https://cnodejs.org/topic/50a1fcc7637ffa4155b5a264










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章