node net模塊

node net模塊

node中網絡通信支持 UDPTCP 兩大傳輸協議;

我們今天來講 TCP .(TCP 協議在node的net 模塊中)

TCP

大致瞭解一下 TCP (網絡傳輸協議)

  • 支持面向連接的傳輸服務
  • 支持字節流的傳輸
  • 支持全雙工
  • 支持多個同時併發的TCP連接
  • 支持可靠的傳輸服務

支持面向連接的傳輸服務

端到端的傳輸: 程序雙方都通過端口號來標識
應用程序在使用TCP傳輸數據之前, 需要進行 3次握手 , 確保程序之間通信的可靠性;

TCP之所以這麼做, 是因爲 IP 協議是無連接不可靠的; 在IP 協議之上來保證通信的
可靠性, 就只能讓TCP協議自己來做;

支持字節流的傳輸

流(stream) : 相當於一個管道, 從一端放入什麼內容, 從另一端可以原樣的取出來; 它描述了一個不出現丟失, 重複和亂序的數據傳輸過程.

應用程序和TCP協議每次交互的 數據長度可能都不相同 . 但是TCP協議是將應用程序提交的數據看作
一連串的, 無結構的 字節流 . 因此, 接受端的應用程序數據字節的起始與終結位置必須由程序自己確定.

爲了能支持字節流傳輸, 發送端和接受端都需要使用 緩存 cache ; (這個會在後面 data事件中驗證)

爲啥不直接發送呢?
肯定不行啊! 你想你在客戶端輸入一個字符就發送一個請求, 那豈不是太浪費了!

TCP 報文長度在 20 ~ 60 B 固定長度 20B, 選項和填充 40B

支持全雙工

全雙工: 接受端和發送端可以互相通信

TCP 協議允許通信雙方的應用程序在任何時候都可以進行發送數據.
由於雙方都設有發送和接受的 緩衝區 ;

發送方把數據發送到TCP的發送端緩存區, 但不會立刻發送; 發送的時機由TCP來控制;
而接受端接受到數據之後, 會把數據放到緩衝區中; 由高層應用程序讀取.

支持多個同時併發的TCP連接

TCP 協議支持同時建立多個連接. 這一點是毋庸置疑的;
尤其是在 接受端 (server)

支持可靠的傳輸服務

TCP是一種可靠的傳輸服務協議. 它使用確認機制來檢查數據是否安全完整地到達, 並且提供擁塞控制功能;
主要是靠對發送和接受的數據進行數據跟蹤, 確認和重傳;

Socket

套接字(socket)是一個抽象層,應用程序可以通過它發送或接收數據,可對其進行像對文件一樣的打開、讀寫和關閉等操作。套接字允許應用程序將I/O插入到網絡中,並與網絡中的其他應用程序進行通信。網絡套接字是IP地址與端口的組合。

上面摘自百度百科;
簡書上有講 socket很好的文章

https://www.jianshu.com/p/066d99da7cbd

當然, 在nodejs中的Socket肯定進行一些封裝;

下面有個node socket關於事件的大致流程圖;
其中缺少了timeout和lookup事件; (一圖頂千言)

在這裏插入圖片描述

我們可以通過vs code提供的ts文件, 看到Socket中的屬性

分配兩個緩衝區,輸入緩衝區和輸出緩衝區。

// 寫入而緩衝的字符數
readonly bufferSize: number;
// 接收的字節數量
readonly bytesRead: number;
// 發送的字節數量
readonly bytesWritten: number;
// 它將保持爲真,直到 socket 連接,然後設置爲 false 並觸發 'connect' 事件
readonly connecting: boolean;
readonly destroyed: boolean;
readonly localAddress: string;
readonly localPort: number;
// 三元組
readonly remoteAddress?: string;
// 遠程 IP 協議。'IPv4' 或 'IPv6'
readonly remoteFamily?: string;
readonly remotePort?: number;

Socket 繼承 Stream
Stream 是啥,這展開來講就複雜了,涉及到IO方面的知識。
以後會補充上去。

可以看鏈接的文章瞭解一些。

https://www.runoob.com/nodejs/nodejs-stream.html

Server

Socket 在客戶端和服務端有不同的功能。

服務端會監聽 listen 客戶端的請求;

從api層面上我們也可以看的出來。

// net 模塊
function createServer(connectionListener ? : (socket: Socket) => void): Server;

function createServer(options ? : {
    allowHalfOpen ? : boolean,
    pauseOnConnect ? : boolean
}, connectionListener ? : (socket: Socket) => void): Server;

function createConnection(options: NetConnectOpts, connectionListener ? : () => void): Socket;

function createConnection(port: number, host ? : string, connectionListener ? : () => void): Socket;

function createConnection(path: string, connectionListener ? : () => void): Socket;

創建一個 Connection 就是創建一個 Socket
創建一個 Server 其中也是需要傳入一個 Socket 的。

因爲,TCP這一塊是通過socket進行傳輸的。

我們可以再看一下 Server

 class Server extends events.EventEmitter {
     constructor(connectionListener ? : (socket: Socket) => void);
     constructor(options ? : {
         allowHalfOpen ? : boolean,
         pauseOnConnect ? : boolean
     }, connectionListener ? : (socket: Socket) => void);
     // 省去以一些方法
 }

底層對網絡的處理都是通過node底層去做的。
要看的話就得跟到很深了。 這裏就不展開了。

node會幫我們處理好,當網絡請求到了,會觸發響應的事件,結束了,也會觸發事件。
我們處理好內容就好。

當然了,如果有精力也可以去看一下node源碼。

一個請求的流程

代碼的註釋非常詳細

# 要先啓動server纔行
node server.js

node client

server

const net = require('net');
let count = 1;

/**
 * 創建一個 server 用於監聽client的請求
 * 裏面傳入的是 connection的監聽器,處理conneciton 事件
 * 
 */
const server = net.createServer((clientSocket) => {
    console.log(`客戶端已連接,接受到ip :${clientSocket.remoteAddress}  port: ${clientSocket.remotePort} 的socket`)

    // 當有數據傳輸進來時; data的觸發因爲有緩存和網絡的原因,不一定會觸發幾次
    clientSocket.on('data', function (buffer) {
        console.log('data事件觸發 No : ' + count)
        count++
        console.log('傳輸的數據 : ' + buffer.toString())
    })

    clientSocket.write('來自server的信息: HELLO CLIENT! ');

    // 可讀流 -> 可寫流 (socket是Duplex )
    // 這樣會把從客戶端拿到的data ,再返回給客戶端
    // socket.pipe(socket); 

    clientSocket.on('end', () => {
        console.log('client 調用了end');
    });
});


console.log('-----------調用listen之前------------')
// 此時listeing = false 說明還未監聽
// 不要在 'listening' 事件觸發之前調用 server.address()
// 調用了也沒有用 輸出null
console.log('server.listening: ' + server.listening)
console.log('server.address(): ' + server.address())

// 指定端口,監聽成功後,會調用回調 和下面的listening相同
server.listen(8124, () => {
    // 
    console.log('server.listen(8123) ');
});

console.log('-----------調用listen之後------------')
console.log('server.listening: ' + server.listening)
console.log('server.address(): ' + JSON.stringify(server.address()))

// 監聽成功後,會調用; 和server.listen()的回調屬於同一個事件
server.on('listening', () => {
    console.log(`server.on('listening',()=>{} `);
})

// 當出錯的時候調用,如果沒有處理node就崩了
server.on('error', (err) => {
    throw err;
});

// 當 server 關閉的時候觸發。 如果有連接存在,直到所有的連接結束纔會觸發這個事件。
server.on('close', () => {
    console.log(`server.on('close')`)
})


// 當有一個新連接到來的時候,觸發
server.on('connection', socket => {
    console.log(`server.on('connection')`)
})

client

const net = require('net');

/**
 * Socket 是全雙工的,也就意味着,數據可以相互傳遞
 */

// 指定port 要和server listen的一致
// 回調是 connnection的事件
const clientSocket = net.createConnection({ port: 8124 }, () => {
  // 連接到 server端之後
  console.log('------已連接到服務器-----');
  clientSocket.write('來自clien的信息: 你好世界!');

  // 這裏循環輸出了3次,但是隻觸發了一次事件
  for (let i = 0; i < 3; i++) {
    clientSocket.write('i = ' + i)
  }

  // 這裏調用end 會觸發
  clientSocket.end()
});

/**
 * events.EventEmitter
 *   1. close 關閉連接
 *   2. connect 連接
 *   3. data 數據
 *   4. drain 寫入緩衝區變爲空時觸發。可以用來做上傳節流
 *   5. end data傳輸結束時
 *   6. error 出現錯誤
 *   7. lookup 在找到主機之後創建連接之前觸發。不可用於 Unix socket。
 *   8. timeout 超時
 *  我們主要看 connect ,data ,end ,close
 */

clientSocket.on('data', (data) => {
  console.log('data : ' + data.toString());
});

clientSocket.on('end', () => {
  console.log('已從服務器斷開');
});

參考文檔

  • 計算機網絡 清華大學出版社 吳功宜
  • nodejs 官網API net
  • 百度百科 Socket
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章