Node.js之網(wǎng)絡(luò)通訊模塊實現(xiàn)淺析
前言
想必我們在用Node.js用的最多的應(yīng)該是創(chuàng)建http服務(wù),所以對于每個Web開發(fā)工程師而言,Node.js的網(wǎng)絡(luò)相關(guān)模塊學(xué)習(xí)是必不可少。
Node.js的網(wǎng)絡(luò)模塊架構(gòu)
在Node.js的模塊里面,與網(wǎng)絡(luò)相關(guān)的模塊有Net、DNS、HTTP、TLS/SSL、HTTPS、UDP/Datagram,除此之外,還有v8底層相關(guān)的網(wǎng)絡(luò)模塊有tcp_wrap.cc
、udp_wrap.cc
、pipe_wrap.cc
、stream_wrap.cc
等等,在Javascript層以及C++層之間通過process.binding進行橋接相互通信。
Net模塊
Net模塊提供了一些用于底層的網(wǎng)絡(luò)通信接口,包括創(chuàng)建服務(wù)器以及客戶端,其中HTTP模塊也是基于Net模型的上層封裝,在Net模塊里面主要提供net.Server以及net.Socket
創(chuàng)建TCP服務(wù)端
創(chuàng)建一個TCP服務(wù)器,可以通過使用構(gòu)造函數(shù)new net.Server
或者使用工廠方法net.createServer
,這兩個方法都會返回一個net.Server類,可接收兩個可選參數(shù)。
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; }); // 執(zhí)行后:server run at { address: '::', family: 'IPv6', port: 56200 }
在listen監(jiān)聽的時候沒有指定端口的話會自動隨意監(jiān)聽一個端口,創(chuàng)建完成一個TCP服務(wù)器后,使用tenlent 0.0.0.0 56200
,鏈接后可與服務(wù)器進行數(shù)據(jù)通信。通過createServer
實例化一個服務(wù)后,服務(wù)會去監(jiān)聽客戶端請求,與客戶端建立了鏈接之后會在回調(diào)里面拋出建鏈的net.Socket
對象。
創(chuàng)建TCP客戶端
創(chuàng)建一個TCP客戶端鏈接可以使用構(gòu)造函數(shù)new net.Socket
或者其工廠方法net.createConnection
,創(chuàng)建成功后都會返回一個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'); });
Socket
socket是啥這里就不做詳細的闡述了,下面主要了解下net.Socket這個構(gòu)造體主要有提供一些什么方法、監(jiān)聽事件的使用。
相關(guān)事件
- connect : 當(dāng)客戶端與服務(wù)端成功建立鏈接之后觸發(fā),如果鏈接不上服務(wù)器直接拋出error事件錯誤然后退出node進程。
- data : 當(dāng)客戶端收到服務(wù)器傳送過來的數(shù)據(jù)或者是客戶端傳送給服務(wù)器的數(shù)據(jù)的時候觸發(fā)回調(diào)。
- end : 當(dāng)另外一側(cè)發(fā)送FIN包斷開的時候觸發(fā),默認情況下面 (
allowHalfOpen == false
)socket會自我銷毀(如果寫入待處理隊列里面還沒正式響應(yīng)回包),但是我們可以設(shè)置allowHalfOpen
參數(shù)為true,這樣可以繼續(xù)往該socket里面寫數(shù)據(jù),但是我們需要自己去調(diào)用 end 方法去消耗這個socket,不然可能會造成句柄泄漏。 - close : 鏈接斷開的時候觸發(fā),但是如果在傳輸?shù)倪^程中有錯誤的話這里會在回調(diào)函數(shù)里面拋出 error。
- timeout : 當(dāng)socket超時空閑的時候觸發(fā),如果要在隊列里面銷毀需要手動去調(diào)close方法。
- lookup : 域名解析完成的時候觸發(fā)。
- drain : 寫完緩存的時候觸發(fā),可使用在上傳大小限制中。
相關(guān)方法
- write() : 服務(wù)端給客戶端發(fā)送數(shù)據(jù)或者是客戶端給服務(wù)端發(fā)送數(shù)據(jù)。
- address() : 獲取服務(wù)綁定的socket的IP地址,返回對象有三個屬性,分別為端口、host以
- 及IPvX版本。
- end() : 半關(guān)閉socket,會發(fā)送一個FIN包,服務(wù)器仍然可能發(fā)送一些數(shù)據(jù),也可以這樣調(diào)用socket.end(data,encoding)。
- pause() : 暫停讀取數(shù)據(jù),可以用作對數(shù)據(jù)上傳限制。
- resume() : 繼續(xù)數(shù)據(jù)讀取。
- setEncoding() : 設(shè)置數(shù)據(jù)流的獲取格式。
- setKeepAlive() : 允許/禁止keep-alive功能。
- setNoDelay() : 禁止Nagele算法,TCP鏈接默認使用Nagle算法,它們在發(fā)送之前數(shù)據(jù)會被緩存。這是為true的話在每次socket.write()的時候會立即發(fā)送數(shù)據(jù),默認為true。
- setTimeout() : 當(dāng)一個空閑的socket在多少秒后不活躍會被接受到timeout事件,但是該socket不會停止銷毀,需要手動調(diào)用end()或者destroy()。表示禁止空閑超時。
相關(guān)屬性
- bufferSize : 當(dāng)前緩存的等待被發(fā)送的字符串的數(shù)量。
- bytesRead : 收到的字節(jié)的數(shù)量。
- bytesWritten : 發(fā)送的字節(jié)的數(shù)量
- destroyed : 標(biāo)識鏈接是否已經(jīng)被破壞,一旦被破環(huán),就不用使用該鏈接來傳輸數(shù)據(jù)。
- localAddress : 遠程客戶端鏈接本地地址的host。如果我們監(jiān)聽服務(wù)的host是0.0.0.0,而客戶端鏈接的是'192.168.1.1',最后的值是后者。
- localPort : 本地的端口。
- remoteAddress : 客戶端IP,如果socket已經(jīng)是destryed的話,該值為
undefined
。 - remoteFamily : 客戶端是IPvX
回包異常處理
服務(wù)器從客戶端接受到需要處理的數(shù)據(jù)后進入處理環(huán)節(jié),再業(yè)務(wù)邏輯處理完成之前如果socket以外斷開的話,待服務(wù)器再給客戶端回報的時候會直接響應(yīng)error
事件并報錯Error : This socket has benn ended by the other part
,所以在回報之前服務(wù)端需要先判斷該socket是否被銷毀,如果沒有被銷毀則回包,如果已經(jīng)斷開則銷毀:
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; });
限制客戶端數(shù)據(jù)大小
對請求大小限制是服務(wù)安全里面比不可少的一個環(huán)節(jié),服務(wù)端不能無限大小的去接受客戶端發(fā)送過來的所有數(shù)據(jù),而限制大小就是第一道門檻。
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; });
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Node.js中的package.json與cnpm命令行工具介紹
這篇文章介紹了Node.js中的package.json與cnpm命令行工具,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06nodejs操作mongodb的填刪改查模塊的制作及引入實例
下面小編就為大家分享一篇nodejs操作mongodb的填刪改查模塊的制作及引入實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01如何用node.js作為后臺,讀寫xml文件,Node.js的文件系統(tǒng)的Api
這篇文章主要介紹了如何用node.js作為后臺,讀寫xml文件,Node.js的文件系統(tǒng)的Api,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08