node 網絡(一)

TCP服務

  • 傳輸控制協議,網絡傳輸層
  • OSI模型: 物理層 數據鏈路層 網絡層 傳輸層 會話層 表示層 應用層

傳輸之前先要三次握手形成會話,一個套接字socket只用于一個服務

創建TCP服務

 var net = require('net');
 
//使用net.createServer(listener)創建,**listener是連接事件connection的偵聽器**
var server = net.createServer(function(socket) {
    socket.on('data', function(data) {
        socket.write("hello");
    });
    socket.on('end', function() {
        console.log('斷開');
    });
    socket.write("hello111:\n");
});
server.listen(8124, function() {
    console.log('server bound');
}); 
//或者
var server = net.createServer();

server.on('connection', function(socket) { //新的連接
});
server.listen(8124);

//可以使用$ telnet 127.0.0.1 8124,或者使用net模塊構建客戶端
var net = require('net');
var client = net.connect({  //
    port: 8124
}, function() { //'connect' listener
    console.log('client connected');
    client.write('world!\r\n');
});
client.on('data', function(data) {
    console.log(data.toString());
    client.end();
});
client.on('end', function() {
    console.log('client disconnected');
});

也可以對Domain Socket監聽 server.listen('/tmp/echo.sock');
使用 $ nc -U /tmp/echo.sock 測試

服務器事件

使用net.createServer()創建的服務器是個EventEmitter實例

  1. listening 在調用server.lisren()綁定端口或者Domain Socket后觸發server.listen(port,listeningListener)
  2. connection 每個客戶端套接字連接到服務端時觸發 net.createServer(),最后一個參數傳遞
  3. close 當服務器關閉時觸發 在調用server.close()后,服務器會停止接受新的套接字,但當前的連接不會斷,直到所有連接都斷后觸發
  4. error 服務器發生異常時觸發,如果不幀聽error事件,會拋出異常

連接事件

服務器可以同時與多個客戶端保持連接,每個連接都是可讀寫的stream對象,用于服務器和客戶端通信

  1. data 當一端調用write()發送數據時,另一端觸發data事件
  2. end 任意一端發送了FIN數據時觸發
  3. connect 用于客戶端,當套接字與服務端連接成功時觸發
  4. error 發生異常
  5. close 套接字完全關閉時觸發
  6. timeout 連接在一定時間內部活躍,觸發
//使用pipe管道 管道提供了一個輸出流到輸入流的機制。通常我們用于從一個流中獲取數據并將數據傳遞到另外一個流中。

var net = require('net');
var server = net.createServer(function(socket) {
    socket.write('Echo server\r\n');
    socket.pipe(socket);//
});
server.listen(1337, '127.0.0.1');

Nagle算法:將tcp中的小數據包緩存合并到一定數量或時間后發出,避免浪費網絡資源,但數據可能被延遲。tcp默認啟動Nagle算法,可以調用socket.setNoDelay(true)關閉,關閉后一端調用write(),另一端可能將多個小數據包合并后觸發一次data

UDP服務

  • 用戶數據包協議,網絡傳輸層
  • 一個套接字可以和多個UDP服務通信,無須連接,資源消耗低,處理快速靈活
  • 可能丟包,應用在低丟包不影響的的場景,視頻、DNS

創建UDP套接字

UDP套接字可以做客戶端也可做服務端

  • 作為服務端 調用dgram.bind(port,[address])方法對網卡和端口綁定
var dgram = require('dgram');
var server = dgram.createSocket('udp4');
server.on("message", function(msg, rinfo) {
    console.log("server got: " + msg + " from " +
        rinfo.address + ":" + rinfo.port);
});
server.on("listening", function() {
    var address = server.address();
    console.log("server listening " + address.address + ":" + address.port);
});
server.bind(41234);//綁定完成后觸發listening事件
  • 作為客戶端:使用dgram.send(buf, offset, length, port, address, [callback])發送消息到網絡;參數對應含義,buffer,buffer偏移,buffer長度,目標端口,目標地址,發送完成后的回調。它可以隨意發送數據到網絡,tcp需要重新通過套接字構建新連接
var dgram = require('dgram');
var message = new Buffer("Node.js");
var client = dgram.createSocket("udp4");
client.send(message, 0, message.length, 41234, "localhost", function(err, bytes) {
    client.close();
});

UPD套接字事件

  • UDP是個EventEmitter實例使用更簡單,TCP是個Stream實例
  1. message UDP套接字幀聽網卡端口后,接受數據時觸發,觸發攜帶的數據為Buffer對象和遠程地址
  2. listening UDP開始幀聽時觸發
  3. close 調用close()時觸發,并不再觸發message事件
  4. error 異常時觸發,不幀聽則直接拋出,進程退出

構建HTTP服務

  • 超文本傳輸協議 HyperText Transfer Protocol
  • 構建在TCP之上 屬于應用層協議 B/S模式,目前最知名的標準RFC 2616

通常的http通信的信息分三部分,TCP的3次握手,客戶端向服務器發送請求報文,服務器完成處理后向客戶端發送響應內容。
瀏覽器其實是http的代理,將用戶的行為轉化為http請求發送給服務端,服務端處理請求然后發送響應報文給代理,代理解析報文再展示給用戶。http服務只做處理http請求和發送http響應

http模塊

  • 繼承自TCP服務(net模塊)
  • 能夠與多個客戶端保持連接,因為采用事件驅動,不用給每個連接創建額外的線程、進程,內存占用低,能高并發
  • tcp以connection為單位服務,http以request為單位服務

http模塊將tcp連接的讀操作封裝為ServerRequest對象,報文頭部使用http_parser解析然后放在req.headers上傳遞給業務邏輯。報文體部分為只讀流對象,需要在數據流結束后轉字符串

function(req, res) {
    // console.log(req.headers);
    var buffers = [];
    req.on('data', function(trunk) {
        buffers.push(trunk);
    }).on('end', function() {
        var buffer = Buffer.concat(buffers); // TODO
        res.end('Hello world');
    });
}
//express中使用bodyParse,會自動解析,但要求請求頭添加Content-Type=application/json

封裝對底層連接的寫操作為ServerResponse對象
影響響應報文頭api為res.setHeader()res.writeHead(),可以調用res.setHeader()進行多次設置,只要調用writeHead后,報頭才會寫入連接

res.writeHead(200, {'Content-Type': 'text/plain'});

設置報文體API為write(),end();end()會先調用write()發送數據,再發信號告知響應結束。

一旦開始了數據發送,writeHead()和setHeader()將不再生效
結束時要調用res.end(),否則客戶端將一直處于等待狀態,無論異常與否

http服務端事件

http服務器是個EventEmitter實例

  • connection 客戶端與服務器建立底層的tcp連接時觸發。連接開啟keep-alive,可以在多次請求響應之間使用
  • request 當請求數據發送到服務端,在解析出http請求頭后觸發該事件
  • close 調用server.close()方法停止接受新連接,當已有的連接也都斷開后觸發,可以給server.close()傳個回調來快速注冊該事件
  • checkContinue 某些客戶端在發送大數據時,會先發一個頭部帶Expect: 100-continue的請求,服務器接收后觸發checkContinue。如果不監聽,就自動回復100 Continue。如果不接收就響應400 Bad Request。注意,在客戶端收到100 Continue后重新發起請求才會觸發request
  • connect 客戶端發起connect請求時觸發,通常在http代理時才會發起connect,如果不監聽,發起該請求的連接會關閉
  • upgrade 客戶端要升級連接協議時會在請求頭部帶上upgrade,服務端接收后觸發,如果不監聽,發起該請求的連接會關閉
  • clientError 連接的客戶端觸發error事件,會傳遞到服務器端并觸發

http客戶端

  • http.request(options, connect) 構建http客戶端

var options = {
    hostname: '127.0.0.1',
    port: 1334,
    path: '/',
    method: 'GET',
    localAddress //建立連接的本地網卡
    socketPath //Domain套接字路徑
    headers //請求頭對象
    auth //Basic認證 將被計算成請求頭的Authorization部分
};
// 用request 包請求 不用自己來處理on end 
var req = http.request(options, function(res) {
    console.log('STATUS: ' + res.statusCode);
    console.log('HEADERS: ' + JSON.stringify(res.headers));
    res.setEncoding('utf8');
    res.on('data', function(chunk) {
        console.log(chunk);
    });
});

req.end();

報文體的內容通過請求對象的write()寫入,end()告知報文結束
客戶端clientRequest(req)解析完響應頭就觸發response,并傳遞ClientResponse以供操作,之后報文體以只讀流提供)

http代理
  • 為了重用tcp連接(在keeplive時,一個tcp連接可以多次用于請求)
  • http里包含一個客戶端代理對象http.globalAgent,它管理每個服務端(host+port)創建的連接并創建一個連接池,默認每個服務器端5個連接

在通過ClientRequest調用http請求時會走代理。可以在options中傳遞agentX修改連接限制

var agent = new http.Agent({
    maxSockets: 10 
});
var options = {
    hostname: '127.0.0.1',
    port: 1334,
    path: '/',
    method: 'GET',
    agent: agent //設置為false就不受連接池限制
};

http客戶端事件
  • response 客戶端得到響應后觸發
  • socket 當連接池中建立的連接分配給當前的對象時觸發
  • upgrade 客戶端向服務端發起upgrade 服務端響應101 Switching Protocols時觸發
  • continue 客戶端想發大數據,頭部帶Expect: 100-continue,服務端同意并響應100 continue時觸發
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容