node.js入門 - 2.創建一個簡單聊天室


  這篇文章將通過開發一個簡單聊天室的方式,介紹node.js的net模塊。

  

  一、第一版,只向客戶端發送信息

 

  我們先實現一個簡單的版本,代碼如下:

var net=require('net');
var chatServer=net.createServer();
chatServer.on('connection',function(client){
    client.write('hi!\n');
    client.write('bye!\n');
    client.end();
})
chatServer.listen(9001);

  代碼講解:  

1.因為我們要使用tcp作為通信協議,node中tcp相關的類是放在net模塊中的,所以我需要先引用net。

2.通過net.createServer()就為我們創建了一個tcp的服務器。

3.接下來使用on方法實現對connection事件的監聽。每當有一個新的客戶端連接到我們的tcp服務器的時候,都會觸發connection對應的方法,向客戶端輸出‘hi!bye!’的文字    信息。然后通過client.end()關閉連接。

4.程序最后通過chatServer.listen(9001);實現對9001端口的監聽。

 

  運行服務器:

  使用webstorm的調試工具運行我們的服務器。

  

  運行客戶端:

  打開window的命令行工具,

輸入如下命令:

telnet 127.0.0.1 9001

回車,輸出如下結果:

 

  note:

  自己實踐上面例子的時候遇到了點小狀況,我最開始使用的是9000端口,運行的時候程序要報錯。

events.js:66
        throw arguments[1]; // Unhandled 'error' event
                       ^
Error: listen EADDRINUSE
    at errnoException (net.js:769:11)
    at Server._listen2 (net.js:909:14)
    at listen (net.js:936:10)
    at Server.listen (net.js:985:5)
    at Object.<anonymous> (D:\workspace\nodejs\chatroom\chatServer.js:8:12)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:492:10)

  ‘EADDRINUSE’這個東西讓人很費解,完全不知道是什么意思,借助有道的翻譯,意思是:錯誤地址使用。‘EADDRINUSE’應該是‘error address in use’的縮寫。后來借助google找到了合理的解釋,說是你監聽的端口已經被使用了,我把端口換成了9001,一切正常。

  當遇到異常的時候,不知道node有沒有提供幫助的地方,光靠他提供的異常信息提示真是解決不了問題啊。或許最好的幫手就是google。

 

  二、第二版,可以接收客戶端信息:

  代碼如下:

var net=require('net');
var chatServer=net.createServer();
var clientList=[];

chatServer.on('connection',function(client){
    client.write('hi!\n');
    clientList.push(client);
    client.on('data',function(data){
        for(var i= 0,len=clientList.length;i<len;i++){
            if(client!=clientList[i]){
                clientList[i].write(data);
            }
        }
        console.log(data);
    });
})
chatServer.listen(9001);

  這版比第一版有兩個變化點,一是增加了client的on方法,二是移出了client.end();方法。client的on方法添加data事件,這樣客戶端每次發送數據,服務器都可以接收到。移出client.end()是因為,如果我們關閉了鏈接,客戶端再發送新數據,服務器就無法接收了。

  我們來看運行結果,其中‘hello’是客戶端輸入信息,紅線框住的是服務器端接收到的信息,注意它是二進制數據,需要我們做相應的處理才能轉換成字符串,后面的課程會有介紹。

 

  三、第三版,實現客戶端和服務器端的相互通信:

  在這里我們需要把每個客戶端都放到一個數組變量里面緩存起來,遍歷數組,使用client.write()方法對每個客戶端做出反應。我們來看代碼:

var net=require('net');
var chatServer=net.createServer();
var clientList=[];

chatServer.on('connection',function(client){
    client.write('hi!\n');
    clientList.push(client);
    client.on('data',function(data){
        for(var i= 0,len=clientList.length;i<len;i++){
            if(client!=clientList[i]){
                clientList[i].write(data);
            }
        }
        console.log(data);
    });
})
chatServer.listen(9001);

  我們創建了clientList來存放客戶端連接,每當有新連接進來的時候,把client對象保存入數組。接下來,判斷遍歷到的client是否是當前client,不是的話輸出data。

  

  note:

  這里大家注意‘len=clientList.length’部分,我們把clientList.length存入變量len,這樣可以提高程序的性能。在 i<len 運算的時候,就不用每次再去取clientList.length了,大家可以在自己的程序里也使用這樣的方式,特別是數組比較大的時候。

  

  我們來看運行結果,要打開多個telnet鏈接,紅色為輸入信息,藍色為輸出信息:

  四、最終版

  我們增加了客戶斷開鏈接的 end 事件,在end事件和發送消息的過程中會清理不存在的客戶端。

var net=require('net');
var chatServer=net.createServer();
var clientList=[];

chatServer.on('connection',function(client){
    client.name=client.remoteAddress+':'+client.remotePort;
    broadcast('hi,'+ client.name +' join!\r\n',client);
    client.write('hi,'+ client.name +'!\r\n');
    clientList.push(client);

    client.on('data',function(data){
        broadcast(client.name+' say:'+ data+'\r\n',client);
    });

    client.on('end',function(){
        broadcast('hi,'+ client.name +' quit!\r\n',client);
        clientList.splice(clientList.indexOf(client),1);
    });
})

function broadcast(message, client) {
    var cleanup=[];
    for(var i= 0,len=clientList.length;i<len;i++){
        if(client!==clientList[i]){
            if(clientList[i].writable){
                clientList[i].write(message);
            }else{
                cleanup.push(clientList[i]);
                clientList[i].destroy();
            }
        }
    }

    for(var i= 0,len=cleanup.length;i<len;i++){
        clientList.splice(clientList.indexOf(cleanup[i]),1);
    }
}

chatServer.listen(9001);

  運行效果:

三個客戶鏈接到服務器,其中一個向另外兩個說‘hello’。

其中一個客戶關閉鏈接:

 

  今天的例子到此為止。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM