NodeJS網絡與部署

共創做者:張曉旭
掘金主頁:juejin.cn/user/852876…javascript

網絡

TCP/IP網絡協議

聊TCP/IP協議以前, 我們先看一下OSI七層模型.html

第 7 層:應用層 爲操做系統或網絡應用程序提供訪問網絡服務的接口。應用層協議的表明包括: HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等。 第 6 層:表示層 把數據轉換爲接受者可以兼容而且適合傳輸的內容,好比數據加密,壓縮,格式轉換等。 第 5 層:會話層 負責數據傳輸中設置和維持網絡設備之間的通訊鏈接。管理主機之間的會話進程,還能夠利用在數據中插入校驗點來實現數據的同步。 第 4 層:傳輸層 把傳輸表頭加至數據造成數據包,完成端到端的數據傳輸。傳輸表頭包含了協議等信息,好比: TCP,UDP 等。 第 3 層:網絡層 負責對子網間的數據包進行尋址和路由選擇,還能夠實現擁塞控制,網際互聯等功能。網絡層的協議包括:IP,IPX 等。好比路由器就在這 第 2 層:數據鏈路層 在不可靠的物理介質上提供可靠的傳輸,主要主要爲:物理地址尋址、數據封裝成幀、流量控制、數據校驗、重發等。好比交換機就在這 第 1 層:物理層 在局域網上傳送數據幀,負責電腦通訊設備與網絡媒體之間的互通,包括針腳,電壓,線纜規範,集線器,網卡,主機適配等。java

一些面試題:node

  1. html在哪一層?

應用層linux

  1. 那麼我們一直提到的協議是啥意思? 協議是什麼?

通俗點說:是協議定義了每一層的做用是什麼, 每一層的職責是什麼, 相似於規範和約束.面試

  1. TCP/IP協議具體指什麼?

有的文章裏就是具體指的TCP協議和IP協議, 可是大多數時候提到的TCP/IP協議, 能夠理解爲互聯網通訊所須要的協議, 是一個協議家族, 以TCP、IP協議爲核心, 包含HTTP、SMTP、TELNET等各類協議。算法

  1. TCP/IP參考模型?

TCP/IP參考模型是一個抽象的分層模型,這個模型中,全部的TCP/IP系列網絡協議都歸類到4個抽象的「層」中.chrome

能夠看一下圖 OSI和TCPIP概念模型.pngnpm

  1. 咱們常說的數據包是什麼?

數據包是網絡層及以上分層中包的單位.vim

每一個分層都會對發送的數據添加一個首部, 首部包含了該層協議相關的信息, 而真正要發送的內容稱之爲數據.

也就是說每個數據包都由首部 + 數據組成.

而對於下層來講, 上層發送過來的所有內容, 都會當作本層的數據, 舉個例子:

傳輸層 TCP包:TCP包首部 + 數據 網絡層 IP包:IP包首部 + (TCP包首部 + 數據) 數據鏈路層 以太網包:以太網包首部 + (IP包首部 + (TCP包首部 + 數據))

  1. 每層在接收到數據後除了添加首部, 還要作什麼呢?

用戶1

  • 傳輸層:TCP模塊爲保證數據的可靠傳輸, 須要添加TCP首部
  • 網絡層:IP包生成後,參考路由控制表決定接受此 IP 包的路由或主機。
  • 數據鏈路層:生成的以太網數據包將經過物理層傳輸給接收端

用戶2

  • 數據鏈路層:主機收到以太網包後,首先從以太網包首部找到 MAC 地址判斷是否爲發送給本身的包,若不是則丟棄數據。

若是是發送給本身的包,則從以太網包首部中的類型肯定數據類型,再傳給相應的模塊,好比IP.

  • 網絡層:從包首部中判斷此 IP 地址是否與本身的 IP 地址匹配,若是匹配則根據首部的協議類型將數據發送給對應的模塊,好比TCP
  • 傳輸層:在 TCP 模塊中,首先會計算一下校驗和,判斷數據是否被破壞。而後檢查是否在按照順序接收數據。最後檢查端口號,肯定具體的應用程序。數據被完整地接收之後,會傳給由端口號識別的應用程序。

總結一下幾個地址:

  • 數據鏈路層的是MAC地址, 用來識別同一鏈路中的不一樣計算機
  • 網絡層的是IP地址, 用來識別TCP/IP 網絡中互連的主機和路由器
  • 傳輸層的是端口號(程序地址), 用來識別同一臺計算機中進行通訊的不一樣應用程序
  1. 那麼咱們經過這三個地址就能夠識別一次通訊了嗎?

答案是否認的.

咱們須要經過如下這幾個數據綜合來識別一次通訊:

  • IP首部:源IP地址
  • IP首部:目標IP地址
  • 協議號, TCP或者UDP
  • TCP首部:源端口號
  • TCP首部:目標端口號
  1. 咱們常說的TCP/UDP他們的區別是什麼?分別適合用在什麼場景?
  • UDP是無鏈接的,TCP必須三次握手創建鏈接
  • UDP是面向報文,沒有擁塞控制,因此速度快,適合多媒體通訊要求,好比及時聊天,支持一對一,一隊多。多對一,多對多。就像牛客網的視頻面試就是用的UDP
  • TCP只能是一對一的可靠性傳輸

那麼常見直播底層是什麼協議呢?

其實如今常見的rtmp和hls直播, 都是基於TCP的, 但願能提供穩定的直播環境.

  1. TCP經過什麼方式提供可靠性?
  • 超時重發,發出報文段要是沒有收到及時的確認,會重發。
  • 數據包的校驗,也就是校驗首部數據和。
  • 對失序的數據從新排序
  • 進行流量控制,防止緩衝區溢出
  • 快重傳和快恢復
  • TCP會將數據截斷爲合理的長度
  1. TCP如何控制擁塞?

擁塞控制就是防止過多的數據注入網絡中,這樣可使網絡中的路由器或鏈路不致過載。 發送方維持一個叫作擁塞窗口cwnd(congestion window)的狀態變量。 爲了防止cwnd增加過大引發網絡擁塞,還需設置一個慢開始門限ssthresh狀態變量。ssthresh的用法以下: 當cwnd<ssthresh時,使用慢開始算法。也就是乘法算法 當cwnd>ssthresh時,改用擁塞避免算法。也就是加法算法 當cwnd=ssthresh時,慢開始與擁塞避免算法任意。 當出現擁塞的時候就把心的門限值設爲此時窗口大小的通常,窗口大小設置爲1,再從新執行上面的步驟。 當收到連續三個重傳的時候這就須要快重傳和快恢復了,當收到連續三個重傳 這個時候發送方就要重傳本身的信息,而後門限減半可是這個時候並非網絡阻塞,窗口只會減半執行擁塞避免算法。

  1. TCP協議的一次數據傳輸, 從創建鏈接到斷開鏈接都有哪些流程?

第一次握手:創建鏈接。客戶端發送鏈接請求報文段,將SYN位置爲1,Sequence Number爲x;而後,客戶端進入SYN_SEND狀態,等待服務器的確認; 第二次握手:服務器收到客戶端的SYN報文段,須要對這個SYN報文段進行確認,設置Acknowledgment Number爲x+1(Sequence Number+1);同時,本身本身還要發送SYN請求信息,將SYN位置爲1,Sequence Number爲y;服務器端將上述全部信息放到一個報文段(即SYN+ACK報文段)中,一併發送給客戶端,此時服務器進入SYN_RECV狀態; 第三次握手:客戶端收到服務器的SYN+ACK報文段。而後將Acknowledgment Number設置爲y+1,向服務器發送ACK報文段,這個報文段發送完畢之後,客戶端和服務器端都進入ESTABLISHED狀態,完成TCP三次握手。

完成了三次握手,客戶端和服務器端就能夠開始傳送數據。以上就是TCP三次握手的整體介紹。通訊結束客戶端和服務端就斷開鏈接,須要通過四次分手確認。

第一次分手:主機1(可使客戶端,也能夠是服務器端),設置Sequence Number和Acknowledgment Number,向主機2發送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有數據要發送給主機2了; 第二次分手:主機2收到了主機1發送的FIN報文段,向主機1回一個ACK報文段,Acknowledgment Number爲Sequence Number加1;主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我「贊成」你的關閉請求; 第三次分手:主機2向主機1發送FIN報文段,請求關閉鏈接,同時主機2進入LAST_ACK狀態; 第四次分手:主機1收到主機2發送的FIN報文段,向主機2發送ACK報文段,而後主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段之後,就關閉鏈接;此時,主機1等待2MSL後依然沒有收到回覆,則證實Server端已正常關閉,那好,主機1也能夠關閉鏈接了。

  1. IP地址

IP 地址(IPv4 地址)由32位正整數來表示,在計算機內部以二進制方式被處理。平常生活中,咱們將32位的 IP 地址以每8位爲一組,分紅4組,每組以 「.」 隔開,再將每組數轉換成十進制數

IP地址包含網絡標識和主機標識, 好比172.112.110.11

172.112.110就是網絡標識, 同一網段內網絡標識必須相同 11就是主機標識, 同一網段內主機標識不能重複

  1. IPv6

IPv6(IP version 6)是爲了根本解決 IPv4 地址耗盡的問題而被標準化的網際協議。IPv4 的地址長度爲 4 個 8 位字節,即 32 比特。而 IPv6 的地址長度則是原來的 4 倍,即 128 比特,通常寫成 8 個 16 位字節。

  1. DNS

咱們平時訪問一個網站, 一個應用程序, 並非用ip來訪問的, 而是用一個域名. 那麼域名是怎麼和ip地址創建聯繫的呢?

就是經過dns, Domain Name System. 好比wiki上的一個例子

以訪問 zh.wikipedia.org 爲例:

客戶端發送查詢報文"query zh.wikipedia.org"至DNS服務器,DNS服務器首先檢查自身緩存,若是存在記錄則直接返回結果。 若是記錄老化或不存在,則: DNS服務器向根域名服務器發送查詢報文"query zh.wikipedia.org",根域名服務器返回頂級域 .org 的頂級域名服務器地址。 DNS服務器向 .org 域的頂級域名服務器發送查詢報文"query zh.wikipedia.org",獲得二級域 .wikipedia.org 的權威域名服務器地址。 DNS服務器向 .wikipedia.org 域的權威域名服務器發送查詢報文"query zh.wikipedia.org",獲得主機 zh 的A記錄,存入自身緩存並返回給客戶端。

如何使用Nodejs來建立一個TCP服務?

在這以前我們要先來了解一下Socket的概念,

咱們常常把socket翻譯爲套接字,socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操做抽象爲幾個簡單的接口供應用層調用已實現進程在網絡中通訊, 好比create,listen,accept,connect,read和write等等。

node裏有各類網絡相關的模塊, http爲應用層模塊,主要按照特定協議編解碼數據;net爲傳輸層模塊,主要負責傳輸編碼後的應用層數據;https是個綜合模塊(涵蓋了http/tls/crypto等),主要用於確保數據安全性

  1. 建立tcp服務端
const net = require('net');

const HOST = '127.0.0.1';
const PORT = 7777;

// 建立一個TCP服務器實例,調用listen函數開始監聽指定端口
// net.createServer()有一個參數, 是監聽鏈接創建的回調
net.createServer((socket) => {
    const remoteName = `${socket.remoteAddress}:${socket.remotePort}`;
    // 創建成功了一個鏈接, 這個回調函數裏返回一個socket對象.
    console.log(`${remoteName} 鏈接到本服務器`);

    // 接收消息
    socket.on('data', (data) => {
        console.log(`${remoteName} - ${data}`)
        // 給客戶端發消息
        socket.write(`你剛纔說啥?是${data}嗎?`);
    });

    // 關閉
    socket.on('close', (data) => {
        console.log(`${remoteName} 鏈接關閉`)
    });

}).listen(PORT, HOST);

console.log(`Server listening on ${HOST}:${PORT}`);
複製代碼
  1. 建立tcp客戶端
const net = require('net');

const HOST = '127.0.0.1';
const PORT = 7777;

const client = new net.Socket();
const ServerName = `${HOST}:${PORT}`;
let count = 0;

client.connect(PORT, HOST, () => {
    console.log(`成功鏈接到 ${ServerName}`);
    // 向服務端發送數據
    const timer = setInterval(() => {
        if (count > 10) {
            client.write('我沒事了, 告辭');
            clearInterval(timer);
            return;
        }
        client.write('馬冬梅' + count++);
    }, 1000)
});

// 接收消息
client.on('data', (data) => {
    console.log(`${ServerName} - ${data}`);
    // 關閉鏈接
    // client.destroy();
});

// 關閉事件
client.on('close', () => {
    console.log('Connection closed');
});

client.on('error', (error) => {
    console.log(error);
})
複製代碼
  1. 運行一下

node tcp-server.js

node tcp-client.js

如何使用NodeJs來建立一個UDP服務?

  1. 建立udp服務端
const dgram = require('dgram');
const server = dgram.createSocket('udp4');

server.on('message', (msg, remote) => {
    console.log(`${remote.address}:${remote.port} - ${msg}`)
    server.send(`收到!`, remote.port, remote.address);
})

server.on('listening', () => {
    const address = server.address()
    console.log(`Server listening on ${address.address}:${address.port}`);
})


server.bind(44444);
複製代碼
  1. 建立udp客戶端
const dgram = require('dgram')
const message = Buffer.alloc(5, 'robozy')
const client = dgram.createSocket('udp4')

client.send(message, 0, message.length, 44444, 'localhost',
    (err, bytes) => {
        console.log(`發送成功${bytes}字節`);
        // client.close()
    }
)

client.on('message', (buffer) => {
    console.log(buffer.toString())
})
複製代碼
  1. 運行一下

node udp-server.js

node udp-client.js

HTTP

HTTP協議(HyperText Transfer Protocol,超文本傳輸協議)是用於從WWW服務器傳輸超文本到本地瀏覽器的傳輸協議。它可使瀏覽器更加高效,使網絡傳輸減小。它不只保證計算機正確快速地傳輸超文本文檔,還肯定傳輸文檔中的哪一部分,以及哪部份內容首先顯示(如文本先於圖形)等。 HTTP是客戶端瀏覽器或其餘程序與Web服務器之間的應用層通訊協議。在Internet上的Web服務器上存放的都是超文本信息,客戶機須要經過HTTP協議傳輸所要訪問的超文本信息。HTTP包含命令和傳輸信息,不只可用於Web訪問,也能夠用於其餘因特網/內聯網應用系統之間的通訊,從而實現各種應用資源超媒體訪問的集成。 咱們在瀏覽器的地址欄裏輸入的網站地址叫作URL (Uniform Resource Locator,統一資源定位符)。就像每家每戶都有一個門牌地址同樣,每一個網頁也都有一個Internet地址。當你在瀏覽器的地址框中輸入一個URL或是單擊一個超級連接時,URL就肯定了要瀏覽的地址。瀏覽器經過超文本傳輸協議(HTTP),將Web服務器上站點的網頁代碼提取出來,並翻譯成漂亮的網頁。

一次完整的HTTP通訊是什麼樣子的?

  1. 創建 TCP 鏈接

在HTTP工做開始以前,客戶端首先要經過網絡與服務器創建鏈接,該鏈接是經過 TCP 來完成的。HTTP 是比 TCP 更高層次的應用層協議,根據規則,只有低層協議創建以後,才能進行高層協議的鏈接,所以,首先要創建 TCP 鏈接,通常 TCP 鏈接的端口號是80;

  1. 客戶端向服務器發送請求命令

一旦創建了TCP鏈接,客戶端就會向服務器發送請求命令; 例如:GET/info HTTP/1.1

  1. 客戶端發送請求頭信息

客戶端發送其請求命令以後,還要以頭信息的形式向服務器發送一些別的信息,以後客戶端發送了一空白行來通知服務器,它已經結束了該頭信息的發送;

  1. 服務器應答

客戶端向服務器發出請求後,服務器會客戶端返回響應; 例如: HTTP/1.1 200 OK 響應的第一部分是協議的版本號和響應狀態碼

  1. 服務器返回響應頭信息

正如客戶端會隨同請求發送關於自身的信息同樣,服務器也會隨同響應向用戶發送關於它本身的數據及被請求的文檔;

  1. 服務器向客戶端發送數據

服務器向客戶端發送頭信息後,它會發送一個空白行來表示頭信息的發送到此爲結束,接着,它就以 Content-Type 響應頭信息所描述的格式發送用戶所請求的實際數據;

  1. 服務器關閉 TCP 鏈接

通常狀況下,一旦服務器向客戶端返回了請求數據,它就要關閉 TCP 鏈接,而後若是客戶端或者服務器在其頭信息加入了這行代碼 Connection:keep-alive ,TCP 鏈接在發送後將仍然保持打開狀態,因而,客戶端能夠繼續經過相同的鏈接發送請求。保持鏈接節省了爲每一個請求創建新鏈接所需的時間,還節約了網絡帶寬。

HTTP協議有哪些特色?

  1. 經過請求和響應的交換達成通訊

協議規定, 請求從客戶端發出, 服務端響應請求並返回.

  1. 無狀態

HTTP 是一種無狀態協議, 在單純HTTP這個層面,協議對於發送過的請求或響應都不作持久化處理

  1. 使用Cookie作狀態管理

服務端返回的頭信息上有可能會攜帶Set-Cookie, 那麼當客戶端接收到響應後, 就會在本地種上cookie. 在下一次給服務端發送請求的時候, 就會攜帶上這些cookie。

  1. 經過URL定位資源

這裏區分一下URI和URL的概念.

URI: 統一資源標識符, 好比你身份證號是xxxxxxx, 在全部人中是獨一無二的, 這個身份證號就能標識你的身份, 那麼它就是URI URL: 統一資源定位符, 好比北京市/朝陽區/xxxx/xxxx/xxxxx, 經過這一串信息能夠定位到你, 那麼這個就是URL

URL有點相似於經過定位實現的URI.

就像有個父類叫作URI, 他要實現的是惟一肯定一個id. 有的人喜歡繼承URI, 經過location來實現; 有的人喜歡繼承URI, 經過name來實現.

  1. 經過各類方法來標識本身的意圖

這裏指的是各類HTTP方法, 好比GET POST PUT DELETE等.

  1. 持久鏈接

HTTP 協議的初始版本中,每進行一個 HTTP 通訊都要斷開一次 TCP 鏈接,增長了不少不必的創建鏈接的開銷。 爲了解決上述 TCP 鏈接的問題,HTTP/1.1 支持持久鏈接。其特色是,只要任意一端沒有明確提出斷開鏈接,則保持 TCP 鏈接狀態。旨在創建一次 TCP 鏈接後進行屢次請求和響應的交互。在 HTTP/1.1 中,全部的鏈接默認都是持久鏈接。

也就是說默認狀況下創建 TCP 鏈接不會斷開,只有在請求報頭中聲明 Connection: close 纔會在請求完成後關閉鏈接。

  1. 管道機制

1.1版本引入pipelining機制, 即在同一個TCP鏈接裏面,客戶端能夠同時發送多個請求。

舉例來講,客戶端須要請求兩個資源。之前的作法是,在同一個TCP鏈接裏面,先發送A請求,而後等待服務器作出迴應,收到後再發出B請求。管道機制則是容許瀏覽器同時發出A請求和B請求,可是服務器仍是按照順序,先回應A請求,完成後再回應B請求。

可是現代瀏覽器通常沒開啓這個配置, 這個機制可能會形成隊頭阻塞. 由於響應是有順序的, 若是一個TCP鏈接中的第一個HTTP請求響應很是慢, 那麼就會阻塞後續HTTP請求的響應.

因此現實中默認狀況下, 一個TCP鏈接同一時間只發一個HTTP請求.

有的同窗會問, 我怎麼據說chrome最大支持6個同域名請求呢?

那是由於chrome最大支持同時開啓6個TCP鏈接。

那麼HTTP 1.0/1.1/2.0在併發請求上主要區別是什麼?

  1. HTTP/1.0

每次TCP鏈接只能發送一個請求,當服務器響應後就會關閉此次鏈接,下一個請求須要再次創建TCP鏈接.

  1. HTTP/1.1

默認採用持續鏈接(TCP鏈接默認不關閉,能夠被多個請求複用,不用聲明Connection: keep-alive). 增長了管道機制,在同一個TCP鏈接裏,容許多個請求同時發送,增長了併發性,進一步改善了HTTP協議的效率, 可是同一個TCP鏈接裏,全部的數據通訊是按次序進行的。迴應慢,會有許多請求排隊,形成"隊頭堵塞"。

  1. HTTP/2.0

加了雙工模式,即不只客戶端可以同時發送多個請求,服務端也能同時處理多個請求,解決了隊頭堵塞的問題。 使用了多路複用的技術,作到同一個鏈接併發處理多個請求,並且併發請求的數量比HTTP1.1大了好幾個數量級。 增長服務器推送的功能,不經請求服務端主動向客戶端發送數據。

各類Headers

Cache-Control

經過指定首部字段 Cache-Control 的指令,就能操做緩存的工做機制。

  1. Cache-Control: public

當指定使用 public 指令時,則明確代表其餘用戶也可利用緩存。

  1. Cache-Control: private

當指定 private 指令後,響應只以特定的用戶做爲對象,這與 public 指令的行爲相反。緩存服務器會對該特定用戶提供資源緩存的服務,對於其餘用戶發送過來的請求,代理服務器則不會返回緩存。

  1. Cache-Control: no-cache

能夠在客戶端存儲資源,每次都必須去服務端作過時校驗,來決定從服務端獲取新的資源(200)仍是使用客戶端緩存(304)。也就是所謂的協商緩存。

  1. Cache-Control: no-store

永遠都不要在客戶端存儲資源,永遠都去原始服務器去獲取資源。

  1. Cache-Control: max-age=604800(單位:秒)

當客戶端發送的請求中包含 max-age 指令時,若是斷定緩存資源的緩存時間數值比指定的時間更小,那麼客戶端就接收緩存的資源。另外,當指定 max-age 的值爲0,那麼緩存服務器一般須要將請求轉發給源服務器。

HTTP/1.1 版本的緩存服務器遇到同時存在 Expires 首部字段的狀況時,會優先處理 max-age 指令,並忽略掉 Expires 首部字段

  1. Cache-Control: s-maxage=604800(單位:秒)

s-maxage 指令的功能和 max-age 指令的相同,它們的不一樣點是 s-maxage 指令只適用於供多位用戶使用的公共緩存服務器(通常指代理)。 當使用 s-maxage 指令後,則直接忽略對 Expires 首部字段及 max-age 指令的處理

Connection

  1. Connection: close

HTTP/1.1 版本的默認鏈接都是持久鏈接。當服務器端想明確斷開鏈接時,則指定 Connection 首部字段的值爲 close。

  1. Connection: Keep-Alive

HTTP/1.1 以前的 HTTP 版本的默認鏈接都是非持久鏈接。爲此,若是想在舊版本的 HTTP 協議上維持持續鏈接,則須要指定 Connection 首部字段的值爲 Keep-Alive。

Date

代表建立 HTTP 報文的日期和時間。 Date: Mon, 10 Jul 2021 15:50:06 GMT HTTP/1.1 協議使用在 RFC1123 中規定的日期時間的格式。

Pragma

Pragma 首部字段是 HTTP/1.1 版本以前的歷史遺留字段,僅做爲與 HTTP/1.0 的向後兼容而定義。

  1. Pragma: no-cache

該首部字段屬於通用首部字段,但只用在客戶端發送的請求中,要求全部的中間服務器不返回緩存的資源。

全部的中間服務器若是都能以 HTTP/1.1 爲基準,那直接採用 Cache-Control: no-cache 指定緩存的處理方式最爲理想。可是要總體掌握全部中間服務器使用的 HTTP 協議版本倒是不現實的,因此,發送的請求會同時包含下面兩個首部字段:

Cache-Control: no-cache
Pragma: no-cache
複製代碼

Accept

  1. Accept: text/html, application/xhtml+xml, application/xml;

Accept 首部字段可通知服務器,用戶代理可以處理的媒體類型及媒體類型的相對優先級。可以使用 type/subtype 這種形式,一次指定多種媒體類型。

  1. Accept-Encoding: gzip, deflate

Accept-Encoding 首部字段用來告知服務器用戶代理支持的內容編碼及內容編碼的優先順序,並可一次性指定多種內容編碼 也可以使用星號(*)做爲通配符,指定任意的編碼格式。

gzip 代表實體採用 GNU zip 編碼 compress 代表實體採用 Unix 的文件壓縮程序 deflate 代表實體採用 zlib 的格式壓縮的 identity 代表沒有對實體進行編碼,當沒有 Content-Encoding 首部字段時,默認採用此編碼方式

Host

  1. Host: www.baidu.com
  • 告知服務器,請求的資源所處的互聯網主機和端口號。
  • Host 首部字段是 HTTP/1.1 規範內惟一一個必須被包含在請求內的首部字段。
  • 若服務器未設定主機名,那直接發送一個空值便可 Host: 。

If-Modified-Since

形如 If-xxx 這種樣式的請求首部字段,均可稱爲條件請求。服務器接收到附帶條件的請求後,只有判斷指定條件爲真時,纔會執行請求。

  1. If-Modified-Since: Mon, 10 Jul 2021 15:50:06 GMT

用於確認代理或客戶端擁有的本地資源的有效性。 在指定 If-Modified-Since 字段值的日期時間以後,若是請求的資源都沒有過更新,則返回狀態碼 304 Not Modified 的響應

ETag

  1. ETag: "aaaa-1234"

首部字段 ETag 能告知客戶端實體標識。它是一種可將資源以字符串形式作惟一性標識的方式。服務器會爲每份資源分配對應的 ETag 值。 另外,當資源更新時,ETag 值也須要更新。生成 ETag 值時,並無統一的算法規則,而僅僅是由服務器來分配。

If-None-Match

  1. If-None-Match: "robozy"

用於指定 If-None-Match 字段值的實體標記(ETag)值與請求資源的 ETag 不一致時,它就告知服務器處理該請求。

User-Agent

  1. User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36

首部字段 User-Agent 會將建立請求的瀏覽器和用戶代理名稱等信息傳達給服務器。 由網絡爬蟲發起請求時,有可能會在字段內添加爬蟲做者的電子郵件地址。此外,若是請求通過代理,那麼中間也極可能被添加上代理服務器的名稱。

Allow

  1. Allow: GET, HEAD

首部字段 Allow 用於通知客戶端可以支持 Request-URI 指定資源的全部 HTTP 方法。 當服務器接收到不支持的 HTTP 方法時,會以狀態碼 405 Method Not Allowed 做爲響應返回。與此同時,還會把全部能支持的 HTTP 方法寫入首部字段 Allow 後返回。

Content-Encoding

  1. Content-Encoding: gzip

首部字段 Content-Encoding 會告知客戶端服務器對實體的主體部分選用的內容編碼方式。內容編碼是指在不丟失實體信息的前提下所進行的壓縮。

Content-Type

  1. Content-Type: text/html; charset=UTF-8

首部字段 Content-Type 說明了實體主體內對象的媒體類型。和首部字段 Accept 同樣,字段值用 type/subtype 形式賦值。

Expires

  1. Expires: Mon, 10 Jul 2021 15:50:06 GMT

首部字段 Expires 會將資源失效的日期告知客戶端。 緩存服務器在接收到含有首部字段 Expires 的響應後,會以緩存來應答請求,在 Expires 字段值指定的時間以前,響應的副本會一直被保存。當超過指定的時間後,緩存服務器在請求發送過來時,會轉向源服務器請求資源。

Set-Cookie

  1. Set-Cookie: userId=11111; expires=Mon, 10 Jul 20121 15:50:06 GMT; path=/;
  • NAME=VALUE: cookie名稱和值
  • expires=DATE: Cookie 的有效期(若不明確指定則默認爲瀏覽器關閉前爲止)
  • path=PATH: 用於限制指定 Cookie 的發送範圍的文件目錄。
  • domain=域名: cookie有效的域名 (若不指定則默認爲建立 Cookie的服務器的域名)
  • Secure: 僅在 HTTPS 安全通訊時纔會發送 Cookie
  • HttpOnly: 使 Cookie 不能被 JavaScript 腳本訪問

如何使用NodeJs建立HTTP服務?

  1. http-server.js
const http = require('http')
http.createServer(function (req, res) {
    res.writeHead(200, {
        'Content-Type': 'text/plain'
    })
    res.end('Hello World')
}).listen(80, '127.0.0.1')

console.log('Server running at http://127.0.0.1:80/')
複製代碼
  1. 瀏覽器訪問

http://127.0.0.1:80/

  1. 用curl訪問

curl -v http://127.0.0.1:80

看一下請求報文

// 三次握手
* Rebuilt URL to: http://127.0.0.1:80/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)

// 客戶端向服務端發送請求報文
> GET / HTTP/1.1
> Host: 127.0.0.1:80
> User-Agent: curl/7.54.0
> Accept: */*
>

// 服務端響應客戶端內容
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Date: Wed, 04 Aug 2021 15:55:55 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Transfer-Encoding: chunked
<
* Connection #0 to host 127.0.0.1 left intact
Hello World%
複製代碼
  1. htttp-client.js
const http = require('http')

const options = {
    hostname: '127.0.0.1',
    port: 80,
    path: '/',
    method: 'GET'
}
const req = http.request(options, (res) => {
    console.log(`Status=${res.statusCode}, Headers=${JSON.stringify(res.headers)}`);
    res.setEncoding('utf8')
    res.on('data', (data) => {
        console.log(data)
    })
})
req.end()
複製代碼

部署

服務器

談到部署, 確定得先有一個本身的服務器...

www.aliyun.com/daily-act/e…

選ecs服務器, 按量付費/月/年 都行, 隨便選個鏡像便可.

Linux安裝Nodejs

  1. 下載安裝包

wget https://nodejs.org/dist/v10.9.0/node-v10.9.0-linux-x64.tar.xz

  1. 解壓

tar xf node-v10.9.0-linux-x64.tar.xz

  1. 設置軟連接

ln -s /root/node-v10.9.0-linux-x64/bin/node /usr/local/bin/node ln -s /root/node-v10.9.0-linux-x64/bin/npm /usr/local/bin/npm

  1. 查看Node版本和npm版本

node -v npm -v

  1. 設置npm源

npm config set registry https://registry.npm.taobao.org

  1. 服務器安裝pm2

npm install -g pm2 ln -s /root/node-v10.9.0-linux-x64/bin/pm2 /usr/local/bin/

  1. 配置ssh
  • 本地生成祕鑰對: ssh-keygen -t rsa  demo_id_rsa
  • 將公鑰放到服務器上: scp ~/.ssh/demo_id_rsa.pub root@39.107.238.161:/root/.ssh/authorized_keys
  • 修改ssh配置 vi ~/.ssh/config
Host robozy
HostName 39.107.238.161
User root
Port 22
IdentityFile ~/.ssh/demo_id_rsa
複製代碼
  • 服務器上修改ssh配置 vim /etc/ssh/sshd_config

PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys

  • 最後就能夠ssh登陸了! ssh robozy
  1. 將本地代碼同步到服務器

rsync -avzp -e "ssh" ./Internet/ robozy:/root/app

  1. 服務器上啓動http

pm2 start /root/app/http-server.js

  1. 本地修改發佈命令

10.1 新建deploy.sh文件

#!/bin/bash

HOST=robozy

rsync -avzp -e "ssh" ./Internet/ $HOST:/root/app
ssh $HOST "pm2 restart /root/app/http-server.js"

echo 'deploy success'
複製代碼

10.2 初始化npm命令

npm init 新增scripts "deploy": "./deploy.sh"

10.3 發佈

npm run deploy

  1. 修改http-server的監聽host
const http = require('http')
const host = '0.0.0.0';
const port = 80;
http.createServer(function (req, res) {
    res.writeHead(200, {
        'Content-Type': 'text/plain'
    })
    res.end('Hello World')
}).listen(port, host)

console.log(`Server running at http://${host}:${port}/`)
複製代碼
  1. ECS安全組添加80端口
  2. 查看服務器上是否已正常監聽80端口

netstat -tpln

  1. 經過ip+端口訪問

39.107.238.161:80

共創做者:張曉旭
掘金主頁:juejin.cn/user/852876…

相關文章
相關標籤/搜索