Node.js之網絡通訊模塊淺析
前言
想必我們在用Node.js用的最多的應該是創建http服務,所以對于每個Web開發工程師而言,Node.js的網絡相關模塊學習是必不可少。
Node.js的網絡模塊架構
在Node.js的模塊里面,與網絡相關的模塊有 Net 、 DNS 、 HTTP 、 TLS/SSL 、 HTTPS 、 UDP/Datagram ,除此之外,還有v8底層相關的網絡模塊有 tcp_wrap.cc 、 udp_wrap.cc 、 pipe_wrap.cc 、 stream_wrap.cc 等等,在Javascript層以及C++層之間通過 process.binding 進行橋接相互通信。
Net模塊
Net模塊提供了一些用于底層的網絡通信接口,包括創建服務器以及客戶端,其中HTTP模塊也是基于Net模型的上層封裝,在Net模塊里面主要提供 net.Server 以及 net.Socket
創建TCP服務端
創建一個TCP服務器,可以通過使用構造函數 new net.Server 或者使用工廠方法 net.createServer ,這兩個方法都會返回一個net.Server類,可接收兩個可選參數。
var net = require('net');
var server = net.createServer(function(socket){
socket
.on('data',function(data){
console.log('socket data',data.toString());
socket.write( data.toString() );
})
.on('end',function(){
console.log('socket end')
})
.on('error',function(error){
console.log('socket error',error);
});
});
server.listen(56200,function(){
console.log('server run at ',server.address());
});
server.on('error',function(err){
throw err;
});
// 執行后:server run at { address: '::', family: 'IPv6', port: 56200 }</code></pre>
在listen監聽的時候沒有指定端口的話會自動隨意監聽一個端口,創建完成一個TCP服務器后,使用 tenlent 0.0.0.0 56200 ,鏈接后可與服務器進行數據通信。通過 createServer 實例化一個服務后,服務會去監聽客戶端請求,與客戶端建立了鏈接之后會在回調里面拋出建鏈的 net.Socket 對象。
創建TCP客戶端
創建一個TCP客戶端鏈接可以使用構造函數 new net.Socket 或者其工廠方法 net.createConnection ,創建成功后都會返回一個 net.Socket 實例。
var net = require('net');
var client = net.createConnection({port:56200,host:'localhost'});
client.on('connect',function(){
console.log('client connect');
});
client.on('data',function(data){
console.log('client data',toString());
});
client.on('error',function(error){
throw error;
});
client.on('close',function(){
console.log('client close');
});</code></pre>
Socket
socket是啥這里就不做詳細的闡述了,下面主要了解下 net.Socket 這個構造體主要有提供一些什么方法、監聽事件的使用。
相關事件
-
connect: 當客戶端與服務端成功建立鏈接之后觸發, 如果鏈接不上服務器直接拋出error事件錯誤然后退出node進程 。
-
data: 當客戶端收到服務器傳送過來的數據或者是客戶端傳送給服務器的數據的時候觸發回調。
-
end: 當另外一側發送FIN包斷開的時候觸發,默認情況下面 (allowHalfOpen == false) socket會自我銷毀(如果寫入待處理隊列里面還沒正式響應回包),但是我們可以設置 allowHalfOpen 參數為true,這樣可以繼續往該socket里面寫數據,但是我們需要自己去調用 end 方法去消耗這個socket,不然可能會造成句柄泄漏。
-
close: 鏈接斷開的時候觸發,但是 如果在傳輸的過程中有錯誤的話這里會在回調函數里面拋出 error 。
-
timeout: 當socket超時空閑的時候觸發, 如果要在隊列里面銷毀需要手動去調close方法 。
-
lookup: 域名解析完成的時候觸發。
-
drain: 寫完緩存的時候觸發,可使用在上傳大小限制中。
相關方法
-
write(): 服務端給客戶端發送數據或者是客戶端給服務端發送數據。
-
address(): 獲取服務綁定的socket的IP地址,返回對象有三個屬性,分別為端口、host以
及IPvX版本。
-
end(): 半關閉socket,會發送一個FIN包,服務器仍然可能發送一些數據,也可以這樣調用socket.end(data,encoding)。
-
pause(): 暫停讀取數據,可以用作對數據上傳限制。
-
resume(): 繼續數據讀取。
-
setEncoding(): 設置數據流的獲取格式。
-
setKeepAlive(): 允許/禁止keep-alive功能。
-
setNoDelay(): 禁止 Nagele算法 ,TCP鏈接默認使用Nagle算法,它們在發送之前數據會被緩存。這是為true的話在每次socket.write()的時候會立即發送數據,默認為true。
-
setTimeout(): 當一個空閑的socket在多少秒后不活躍會被接受到timeout事件,但是該socket不會停止銷毀,需要手動調用end()或者destroy()。表示禁止空閑超時。
相關屬性
-
bufferSize: 當前緩存的等待被發送的字符串的數量。
-
bytesRead: 收到的字節的數量。
-
bytesWritten: 發送的字節的數量
-
destroyed: 標識鏈接是否已經被破壞,一旦被破環,就不用使用該鏈接來傳輸數據。
-
localAddress: 遠程客戶端鏈接本地地址的host。如果我們監聽服務的host是0.0.0.0,而客戶端鏈接的是'192.168.1.1',最后的值是后者。
-
localPort: 本地的端口。
-
remoteAddress: 客戶端IP,如果socket已經是destryed的話,該值為 undefined 。
-
remoteFamily: 客戶端是IPvX
回包異常處理
服務器從客戶端接受到需要處理的數據后進入處理環節,再業務邏輯處理完成之前如果socket以外斷開的話,待服務器再給客戶端回報的時候會直接響應 error 事件并報錯 Error : This socket has benn ended by the other part ,所以在回報之前服務端需要先判斷該socket是否被銷毀,如果沒有被銷毀則回包,如果已經斷開則銷毀:
var net = require('net');
var biz = require('./biz');
var server = net.createServer(function(socket){
socket
.on('data',function(data){
biz.do(data)
.then(function(){
if( !socket.destroyed ) {
socket.write( data.toString() );
} else {
// do some report
socket.destry();
}
})
.catch(function(){
!socket.destroyed && socket.end('server handler error');
});
})
.on('end',function(){
console.log('socket end')
})
.on('error',function(error){
console.log('socket error',error);
});
});
server.listen(56200,function(){
console.log('server run at ',server.address());
});
server.on('error',function(err){
throw err;
});</code></pre>
限制客戶端數據大小
對請求大小限制是服務安全里面比不可少的一個環節,服務端不能無限大小的去接受客戶端發送過來的所有數據,而限制大小就是第一道門檻。
var net = require('net');
var MAX_REQUEST_BYTES = 2 1024 1024; // 2M
var server = net.createServer(function(socket){
socket
.on('data',function(data){
if(data.bytesRead > MAX_REQUEST_BYTES) {
socket.pause();
socket.end('data is too big, forbidden');
// do some report
}
})
.on('end',function(){
console.log('socket end')
})
.on('error',function(error){
console.log('socket error',error);
});
});
server.listen(56200,function(){
console.log('server run at ',server.address());
});
server.on('error',function(err){
throw err;
});</code></pre>
來自:https://segmentfault.com/a/1190000008908077