Nodejs學習筆記之NET模組_node.js

來源:互聯網
上載者:User

一,開篇分析

從今天開始,我們來深入具體的模組學習,這篇文章是這個系列文章的第三篇,前兩篇主要是以理論為主,相信大家在前兩篇的學習中,

對NodeJS也有一個基本的認識,沒事!!!趁熱打鐵,讓我們繼續將NodeJS進行到底,好了廢話不多說,直接進入今天的主題 “Net模組” ,那麼”Net“應該如何理解那?

它是做什麼用的那?(Net模組可用於建立Socket伺服器或Socket用戶端。NodeJS 的資料通訊,最基礎的兩個模組是 Net 和 Http,前者是基於 Tcp 的封裝,後者本質還是 Tcp 層,只不過做了比較多的資料封裝,我們視為表現層)。

這裡參考一下NodeJS “http.js” 中的源碼:

從圖中不難看出 HttpServer繼承了Net類,具有了相關的通訊能力,做了比較多的資料封裝,我們視為更進階的表現層。

擴充知識(以下是“inherits”的源碼):

複製代碼 代碼如下:

exports.inherits = function(ctor, superCtor) {
  ctor.super_ = superCtor;
  ctor.prototype = Object.create(superCtor.prototype, {
    constructor: {
      value: ctor,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
};

功能是實現繼承複用。

剛才做了一個簡要的概述,裡面有一些常用的概念,這裡做個簡短的概念普及介紹:

  (1),TCP/IP------TPC/IP協議是傳輸層協議,主要解決資料如何在網路中傳輸。

  (2),Socket------socket則是對TCP/IP協議的封裝和應用(程式層面)。

  (3),Http------HTTP是應用程式層協議,主要解決如何封裝資料。

  (4),網路七層模型------物理層、資料連結層、網路層、傳輸層、會話層、展示層和應用程式層。

  總結一下:Socket是對TCP/IP協議的封裝,Socket本身並不是協議,而是一個調用介面(API)。

  從而形成了我們知道的一些最基本的函數介面,比如Create、Listen、Connect、Accept、Send、Read和Write等等。

  TCP/IP只是一個協議棧,就像作業系統的運行機制一樣,必須要具體實現,同時還要提供對外的操作介面

  實際上,傳輸層的TCP是基於網路層的IP協議的,而應用程式層的HTTP協議又是基於傳輸層的TCP協議的,而Socket本身不算是協議,就像上面所說,它只是提供了一個針對TCP或者UDP編程的介面。

二,體驗一把

好了,概念我們也有了,來個例子:

1,建立server.js

複製代碼 代碼如下:

var net = require('net') ;
var server = net.createServer(function(c) { // Connection監聽器
  console.log("伺服器已串連") ;
  c.on("end", function() {
    console.log("伺服器已斷開") ;
  }) ;
  c.write("Hello,Bigbear !\r\n") ;
  c.pipe(c) ;
}) ;
server.listen(8124, function() { // Listening監聽器
  console.log("伺服器已綁定") ;
}) ;

2,建立client.js

複製代碼 代碼如下:

var net = require('net') ;
var client = net.connect({
    port: 8124
},function(){ // connect監聽器
  console.log("用戶端已串連") ;
  client.write('Hello,Baby !\r\n') ;
});
client.on("data", function(data) {
  console.log(data.toString()) ;
  client.end() ;
});
client.on("end", function(){
  console.log("用戶端中斷連線") ;
}) ;

分析一下:

服務端------net.createServer建立一個 TCP 服務,這個服務綁定(server.listen)在 8124 這個連接埠上,建立 Server 後我們看到有一個回呼函數,

在調用上面函數的時候傳入一個參數,這個參數也是函數,並且接受了 socket ,這個由其他方法構造的一個管道(pipe),他的作用就是用來資料互動的。

pipe 是需要 Client 跟 Server 打招呼才能建立的,如果此刻沒有用戶端訪問 Server,這個 socket 就不會存在了。

用戶端------net.connect顧名思義,就是串連到服務端,第一個參數是對象,設定連接埠(port)為 8124,也就是我們伺服器監聽的連接埠,由於沒有設定 host 參數,那預設就是 localhost (本地)。

在 Server 中,socket 是管道的一端,而在 client 中,client 本身就是管道的一端,如果是多個用戶端串連 Server,Server 會建立多個 socket,每個 socket 對應一個 client。

運行結果:

三,案例引入

(1),下面代碼僅僅是伺服器向用戶端輸出一段文本,完成服務端到用戶端的單向通訊。

複製代碼 代碼如下:

//  Sever --> Client 的單向通訊
var net = require('net');
var chatServer = net.createServer();
chatServer.on('connection', function(client) {
  client.write('Hi!\n'); // 服務端向用戶端輸出資訊,使用 write() 方法
  client.write('Bye!\n');
  client.end(); // 服務端結束該次會話
});
chatServer.listen(9000);

Telnet測試一下:telnet127.0.0.1:9000

執行 telnet後,與服務點串連,反饋 Hi! Bye! 的字元,並立刻結束服務端程式終止串連。

如果我們要服務端接到到用戶端的資訊?

可以監聽 server.data 事件並且不要中止串連(否則會立刻結束無法接受來自用戶端的訊息)。

(2),監聽 server.data 事件並且不要中止串連(否則會立刻結束無法接受來自用戶端的訊息)。

複製代碼 代碼如下:

// 在前者的基礎上,實現 Client --> Sever 的通訊,如此一來便是雙向通訊
var net = require('net');
var chatServer = net.createServer(),   
    clientList = [];
chatServer.on('connection', function(client) {
  // JS 可以為對象自由添加屬性。這裡我們添加一個 name 的自訂屬性,用於表示哪個用戶端(用戶端的地址+連接埠為依據)
  client.name = client.remoteAddress + ':' + client.remotePort; 
  client.write('Hi ' + client.name + '!\n'); 
  clientList.push(client); 
  client.on('data', function(data) {   
     broadcast(data, client);// 接受來自用戶端的資訊 
  });
});
function broadcast(message, client) { 
    for(var i=0;i<clientList.length;i+=1) {   
      if(client !== clientList[i]) {     
        clientList[i].write(client.name + " says " + message);   
      } 
    }
}
chatServer.listen(9000);

上面是不是一個完整功能的代碼呢?我們說還有一個問題沒有考慮進去:那就是一旦某個用戶端退出,卻仍保留在 clientList裡面,這明顯是一個null 指標。

(3),處理clientList

複製代碼 代碼如下:

chatServer.on('connection', function(client) {
  client.name = client.remoteAddress + ':' + client.remotePort
  client.write('Hi ' + client.name + '!\n');
  clientList.push(client)
  client.on('data', function(data) {
    broadcast(data, client)
  })
  client.on('end', function() {
    clientList.splice(clientList.indexOf(client), 1); // 刪除數組中的制定元素。
  })
})

NodeTCPAPI已經為我們提供了 end 事件,即用戶端中止與服務端串連的時候發生。

(4),最佳化broadcast

複製代碼 代碼如下:

function broadcast(message, client) {
  var cleanup = []
  for(var i=0;i<clientList.length;i+=1) {
    if(client !== clientList[i]) {
      if(clientList[i].writable) { // 先檢查 sockets 是否可寫
        clientList[i].write(client.name + " says " + message)
      } else {
        cleanup.push(clientList[i]) // 如果不可寫,收集起來銷毀。銷毀之前要 Socket.destroy() 用 API 的方法銷毀。
        clientList[i].destroy()
      }
    }
  }  //Remove dead Nodes out of write loop to avoid trashing loop index
  for(i=0;i<cleanup.length;i+=1) {
    clientList.splice(clientList.indexOf(cleanup[i]), 1)
  }
}

注意的是一旦 “end” 沒有被觸發,會出現異常,所以才做最佳化工作。

(5),NetAPI中還提供一個 error 事件,用於捕捉用戶端的異常

複製代碼 代碼如下:

client.on('error', function(e) {
  console.log(e);
});

四,總結一下

1,理解開篇的相關概念

2,認識Http與Net模組之間的關係

3,結合本文的例子,查閱相關api去實踐

4,socket用戶端與伺服器端之間的通訊思想

5,有興趣可以完善一下那個聊天室的例子

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.