HTTP最通俗的理解,別再背了

導(dǎo)語(yǔ)

一屏長(zhǎng)文,更深入的了解HTTP協(xié)議。對(duì)于入門(mén)前端不久的同學(xué)來(lái)說(shuō),可能學(xué)習(xí)前端,就從HTML,CSS,JS學(xué)起,然后再入手一個(gè)框架,但對(duì)于http的理解可能還僅在知道一些面試中關(guān)于http的考題或比較少在代碼層面去真正理解一些理論的知識(shí),看完本篇希望你能對(duì)http有一個(gè)較為深入的理解,并且能在開(kāi)發(fā)中對(duì)你有所幫助

進(jìn)入HTTP

http經(jīng)典圖

[圖片上傳失敗...(image-ee3110-1583916328292)]

瀏覽器輸入U(xiǎn)RL后HTTP請(qǐng)求返回的完整過(guò)程

網(wǎng)絡(luò)協(xié)議分層

經(jīng)典五層模型

[圖片上傳失敗...(image-d92d66-1583916328292)]

后續(xù)小節(jié)我們會(huì)涉及到的知識(shí)點(diǎn)就是應(yīng)用層和傳輸層。

  • 物理層:主要作用就是定義物理設(shè)備如何傳輸數(shù)據(jù)

  • 數(shù)據(jù)鏈路層:在通信的實(shí)體間建立數(shù)據(jù)鏈路連接

  • 網(wǎng)絡(luò)層:在節(jié)點(diǎn)之間傳輸創(chuàng)建邏輯鏈路

傳輸層

它旨在向用戶(hù)提供可靠的端到端的服務(wù),數(shù)據(jù)傳輸過(guò)程可能涉及到分片分包等,以及傳輸過(guò)去如何組裝等,這個(gè)無(wú)需讓開(kāi)發(fā)者來(lái)做,因此傳輸層向高層屏蔽了下層數(shù)據(jù)通信的細(xì)節(jié)。正因?yàn)槿绱耍斫鈧鬏攲拥募?xì)節(jié)能夠讓我們實(shí)現(xiàn)一個(gè)性能更高HTTP實(shí)現(xiàn)方式

應(yīng)用層

它幫我們實(shí)現(xiàn)了http協(xié)議,為應(yīng)用層提供了很多服務(wù),并且構(gòu)建與TCP協(xié)議之上,屏蔽網(wǎng)絡(luò)傳輸相關(guān)細(xì)節(jié)

http的三次握手

http只有請(qǐng)求和響應(yīng)的概念,創(chuàng)建連接是屬于TCP的操作,而連接的請(qǐng)求和響應(yīng)是在tcp連接之上的。這是新手很容搞混的一點(diǎn)。在http1.1中連接可以保持,這樣的好處是因?yàn)閔ttp三次握手是有開(kāi)銷(xiāo)的。http2.0中請(qǐng)求可以在同一個(gè)tcp連接中并發(fā),也是大大節(jié)省了建立連接的開(kāi)銷(xiāo)。具體后續(xù)將詳講,現(xiàn)在說(shuō)回http三次握手,如下圖

image

首先客戶(hù)端發(fā)送一個(gè)要?jiǎng)?chuàng)建連接的數(shù)據(jù)包請(qǐng)求到服務(wù)端,包含一個(gè)標(biāo)志位SYN=1和seq=Y。

然后服務(wù)端會(huì)開(kāi)啟一個(gè)TCP的socket端口,返回一個(gè)標(biāo)志位SYN=1,確認(rèn)位ACK=x+1和seq=y的數(shù)據(jù)包

最后客戶(hù)端再發(fā)送一個(gè)ACK=Y+1,Seq=Z的數(shù)據(jù)包到服務(wù)端

這就是HTTP的三次握手全過(guò)程,三次握手的原因是防止服務(wù)端開(kāi)啟一些無(wú)用連接,因?yàn)榫W(wǎng)絡(luò)連接是有延遲的,如果沒(méi)有第三次連接,由于網(wǎng)絡(luò)延遲,客戶(hù)端關(guān)閉了連接,而服務(wù)端一直在等待客戶(hù)端請(qǐng)求發(fā)送過(guò)來(lái),這就造成了資源浪費(fèi),有了三次握手,就能確認(rèn)請(qǐng)求發(fā)送和響應(yīng)請(qǐng)求沒(méi)有問(wèn)題。

HTTP報(bào)文

[圖片上傳失敗...(image-ada0fb-1583916328292)]

請(qǐng)求報(bào)文中首行包括一些請(qǐng)求方法 請(qǐng)求資源地址和http協(xié)議版本。

響應(yīng)報(bào)文中首行包括協(xié)議版本、http狀態(tài)碼和狀態(tài)碼含義等

HTTP方法

用來(lái)定義對(duì)于資源的操作

  • HTTP方法:GET, POST,HEAD,OPTIONS,PUT,DELETE,TRACE和CONNECT

  • GET: 通常用于請(qǐng)求服務(wù)器發(fā)送某些資源

  • HEAD: 請(qǐng)求資源的頭部信息, 并且這些頭部與 HTTP GET 方法請(qǐng)求時(shí)返回的一致. 該請(qǐng)求方法的一個(gè)使用場(chǎng)景是在下載一個(gè)大文件前先獲取其大小再?zèng)Q定是否要下載, 以此可以節(jié)約帶寬資源

  • OPTIONS: 用于獲取目的資源所支持的通信選項(xiàng)

  • POST: 發(fā)送數(shù)據(jù)給服務(wù)器

  • PUT: 用于新增資源或者使用請(qǐng)求中的有效負(fù)載替換目標(biāo)資源的表現(xiàn)形式

  • DELETE: 用于刪除指定的資源

  • PATCH: 用于對(duì)資源進(jìn)行部分修改

  • CONNECT: HTTP/1.1協(xié)議中預(yù)留給能夠?qū)⑦B接改為管道方式的代理服務(wù)器

  • TRACE: 回顯服務(wù)器收到的請(qǐng)求,主要用于測(cè)試或診斷

參考:面試官(9):可能是全網(wǎng)最全的http面試答案

Http Code碼

2XX 成功

  • 200 OK,表示從客戶(hù)端發(fā)來(lái)的請(qǐng)求在服務(wù)器端被正確處理

  • 201 Created 請(qǐng)求已經(jīng)被實(shí)現(xiàn),而且有一個(gè)新的資源已經(jīng)依據(jù)請(qǐng)求的需要而建立

  • 202 Accepted 請(qǐng)求已接受,但是還沒(méi)執(zhí)行,不保證完成請(qǐng)求

  • 204 No content,表示請(qǐng)求成功,但響應(yīng)報(bào)文不含實(shí)體的主體部分

  • 206 Partial Content,進(jìn)行范圍請(qǐng)求

3XX 重定向

  • 301 moved permanently,永久性重定向,表示資源已被分配了新的 URL

  • 302 found,臨時(shí)性重定向,表示資源臨時(shí)被分配了新的 URL

  • 303 see other,表示資源存在著另一個(gè) URL,應(yīng)使用 GET 方法丁香獲取資源

  • 304 not modified,表示服務(wù)器允許訪(fǎng)問(wèn)資源,但因發(fā)生請(qǐng)求未滿(mǎn)足條件的情況

  • 307 temporary redirect,臨時(shí)重定向,和302含義相同

4XX 客戶(hù)端錯(cuò)誤

  • 400 bad request,請(qǐng)求報(bào)文存在語(yǔ)法錯(cuò)誤

  • 401 unauthorized,表示發(fā)送的請(qǐng)求需要有通過(guò) HTTP 認(rèn)證的認(rèn)證信息

  • 403 forbidden,表示對(duì)請(qǐng)求資源的訪(fǎng)問(wèn)被服務(wù)器拒絕

  • 404 not found,表示在服務(wù)器上沒(méi)有找到請(qǐng)求的資源

  • 408 Request timeout, 客戶(hù)端請(qǐng)求超時(shí)

  • 409 Confict, 請(qǐng)求的資源可能引起沖突

5XX 服務(wù)器錯(cuò)誤

  • 500 internal sever error,表示服務(wù)器端在執(zhí)行請(qǐng)求時(shí)發(fā)生了錯(cuò)誤

  • 501 Not Implemented 請(qǐng)求超出服務(wù)器能力范圍,例如服務(wù)器不支持當(dāng)前請(qǐng)求所需要的某個(gè)功能,或者請(qǐng)求是服務(wù)器不支持的某個(gè)方法

  • 503 service unavailable,表明服務(wù)器暫時(shí)處于超負(fù)載或正在停機(jī)維護(hù),無(wú)法處理請(qǐng)求

  • 505 http version not supported 服務(wù)器不支持,或者拒絕支持在請(qǐng)求中使用的 HTTP 版本

通過(guò)node創(chuàng)建一個(gè)簡(jiǎn)單的node服務(wù)

server.js


const http = require('http')

http.createServer(function(request, response) {

    console.log('request come',request.url)

    response.end('hello world')

}).listen(8888)

console.log('server.listening on 8888')

終端進(jìn)入到server.js文件下,執(zhí)行node server.js 瀏覽器輸入localhost:8888,即可看見(jiàn)'hello world'

HTTP特性總覽

瀏覽器就是最常見(jiàn)的客戶(hù)端,瀏覽器為了保證數(shù)據(jù)傳輸?shù)陌踩?,具有同源策略,所謂同源是指:域名、協(xié)議、端口相同

同源策略又可以分為以下兩種:

  • DOM同源策略:禁止對(duì)不同源頁(yè)面DOM進(jìn)行操作。這里主要場(chǎng)景就是iframe跨域的情況,不同域名的iframe是限制互相訪(fǎng)問(wèn)的

  • XMLHttpRequest同源策略: 靜止使用XHR對(duì)象向不同源的服務(wù)器發(fā)起HTTP請(qǐng)求

了解了瀏覽器同源策略的作用,如果不同源發(fā)出請(qǐng)求,就會(huì)產(chǎn)生跨域。但是在實(shí)際開(kāi)發(fā)中,我們很多時(shí)候需要突破這樣的限制,方法有以下幾種(后面會(huì)有方法實(shí)踐):

  • JSONP: 利用script的src標(biāo)簽不受同源限制,動(dòng)態(tài)創(chuàng)建script標(biāo)簽

  • CORS: 服務(wù)端設(shè)置access-allow-origin

  • 通過(guò)window.name跨域

  • 通過(guò)document.domain

  • 通過(guò)Html5的postMessage

跨域知識(shí)詳細(xì)可參考前端跨域整理

通過(guò)代碼來(lái)看下具體是怎么樣的

cors跨域

創(chuàng)建server.js


const http = require('http')

const fs = require('fs')

http.createServer(function (request, response) {

    console.log('request come', request.url)

    const html = fs.readFileSync('test.html','utf8')

    response.writeHead(200, {

        'Content-Type': 'text/html'

    })

    response.end(html)

}).listen(8888)

server.js同目錄下創(chuàng)建hello.html,js代碼如下(地址換成自己電腦ip地址)


var xhr = new XMLHttpRequest()

xhr.open('GET','http://0.0.0.0:8887')

xhr.send()

同目錄下創(chuàng)建server2.js


const http = require('http')

http.createServer(function (request, response) {

    console.log('request come',request.url)

    response.end('hello world')

}).listen(8887)

console.log('server listening on 8887')

分別啟動(dòng)server.js和server2.js,并在瀏覽器輸入localhost:8888

[圖片上傳失敗...(image-b633cb-1583916328292)]

解決方案:在server2.js中加入


response.writeHead(200, {

    'Access-Control-Allow-Origin': '*'

})

跨域請(qǐng)求成功

<font color='red'> 注意 </font>:當(dāng)我們沒(méi)有加跨域請(qǐng)求頭的時(shí)候,可以發(fā)現(xiàn)服務(wù)端(也就是運(yùn)行server2.js的終端)依然能收到請(qǐng)求,只是返回的內(nèi)容在瀏覽器端沒(méi)有接收到,因此跨域并不是發(fā)不出請(qǐng)求,只是返回的內(nèi)容被瀏覽器攔截了而已

CORS跨域限制以及預(yù)檢請(qǐng)求驗(yàn)證

修改hello.html,js改為


fetch('http://192.168.0.106:8887/', {

    method: 'POST',

    headers: {

        'Test-Cors': '123'

    }

})

瀏覽器訪(fǎng)問(wèn)localhost:8888,出現(xiàn)

[圖片上傳失敗...(image-94a6dd-1583916328292)]

原因是什么呢,且聽(tīng)我慢慢道來(lái)

瀏覽器的請(qǐng)求在跨域的時(shí)候默認(rèn)允許的方法為

GET、HEAD、POST,其他方法不允許,需要有預(yù)檢請(qǐng)求

  • 允許的Content-Type為

  • text/plain

  • multipart/form-data

  • application/x-www-form-urlencoded

其他Type也需要預(yù)檢請(qǐng)求

其他限制包括header 詳見(jiàn)默認(rèn)允許header,XMLHttpRequestUpload對(duì)象均沒(méi)有注冊(cè)任何事件監(jiān)聽(tīng)器以及請(qǐng)求中沒(méi)有使用ReadableStream對(duì)象。后兩個(gè)實(shí)際接觸不多,可以不深究

說(shuō)回預(yù)檢請(qǐng)求,先看下圖

[圖片上傳失敗...(image-3b5b54-1583916328292)]

注意:新版chorme瀏覽器改了,在network里面看不到了,換個(gè)瀏覽器

如果我們需要這個(gè)請(qǐng)求頭,在server2.js中的response.writeHead里面添加

'Access-Control-Allow-Headers': 'X-Test-Cors'

同理,如果需要添加允許的方法,可以添加

'Access-Control-Allow-Headers': 'Delete,PUT'

如果我們希望在某一段時(shí)間內(nèi)發(fā)送的跨域請(qǐng)求不再發(fā)送預(yù)檢請(qǐng)求,可以在response.writeHead中設(shè)置

'Access-Control-Max-Age': '100'

JSONP跨域

去掉server.js中的請(qǐng)求頭,并修改hello.html中js為

<script src="http://192.168.0.107:8887/"></script>

這就是一個(gè)簡(jiǎn)單的jsonp跨域,具體的可以參考上面的跨域文章

瀏覽器的緩存

為了減少請(qǐng)求,加快頁(yè)面訪(fǎng)問(wèn)速度。開(kāi)發(fā)者可以根據(jù)需要對(duì)資源進(jìn)行緩存。分為強(qiáng)緩存和協(xié)商緩存,通過(guò)http首部字段進(jìn)行設(shè)置

強(qiáng)緩存

Expires是一個(gè)絕對(duì)時(shí)間,即服務(wù)器時(shí)間。瀏覽器檢查當(dāng)前時(shí)間,如果還沒(méi)到失效時(shí)間就直接使用緩存

但是該方法存在一個(gè)問(wèn)題:服務(wù)器時(shí)間與客戶(hù)端時(shí)間可能不一致。因此該字段已經(jīng)很少使用

cache-control中的max-age保存一個(gè)相對(duì)時(shí)間。例如Cache-Control: max-age = 484200,表示瀏覽器收到文件,緩存在484200S內(nèi)均有效。如果同時(shí)存在cache-control和Expires,瀏覽器總是優(yōu)先使用cache-control

協(xié)商緩存

last-modified是第一次請(qǐng)求資源時(shí),服務(wù)器返回的字段,表示最后一段更新的時(shí)間。下一次瀏覽器

請(qǐng)求資源時(shí)就發(fā)送if-modified-since字段。服務(wù)器用本地last-modified時(shí)間與if-modified-since

時(shí)間比較,如果不一致則認(rèn)為緩存已過(guò)期并返回新資源給瀏覽器;如果時(shí)間一致則發(fā)送304狀態(tài)碼,讓瀏覽器繼續(xù)使用緩存

Etag 資源的實(shí)體標(biāo)識(shí)(哈希字符串),當(dāng)資源內(nèi)容更新時(shí),Etag會(huì)改變。服務(wù)器會(huì)判斷Etag是否發(fā)送變化。如果變化則返回新資源,否則返回304

接下來(lái)我們?cè)敿?xì)看下Cache-Control

  1. 可緩存性

public、private、no-cache、no-store

  • public指的是http返回的內(nèi)容所經(jīng)過(guò)的任何路徑(包括代理服務(wù)器和客戶(hù)端瀏覽器)當(dāng)- 中都可以被緩存

  • private指的是只有發(fā)起請(qǐng)求的瀏覽器才可以緩存

  • no-cache可以在本地緩存,可以在代理服務(wù)器緩存,但是這個(gè)緩存要服務(wù)器驗(yàn)證才可以使用

  • no-store 徹底得禁用緩沖,本地和代理服務(wù)器都不緩沖,每次都從服務(wù)器獲取

  1. 到期

指的緩存時(shí)間,最常用的就是max-age,單位是秒,指的就是緩存的有效期是多長(zhǎng)時(shí)間
s-max-age這個(gè)是代理服務(wù)器的緩存時(shí)間,只在代理服務(wù)器生效

  1. 重新驗(yàn)證

must-revalidate如果設(shè)置的緩存已經(jīng)過(guò)期了,必須去原服務(wù)端請(qǐng)求,然后重新驗(yàn)證數(shù)據(jù)是否已經(jīng)過(guò)期

proxy-revalidate應(yīng)用于代理服務(wù)器緩存

理論說(shuō)完了,接下來(lái)我們通過(guò)實(shí)戰(zhàn)看看

修改test.html,js部分修改為

<script src="./script.js"></script>

修改server.js


const http = require('http')

const fs = require('fs')

http.createServer(function (request, response) {

    console.log('request come',request.url)

    if (request.url == '/') {

        const html = fs.readFileSync('test.html', 'utf8')

        response.writeHead(200, {

            'Content-Type': 'text/html'

        })

        response.end(html)

    }

    if (request.url == '/script.js') {

            response.writeHead(200, {

                'Content-Type': 'text/javascript',

                'Cache-Control':'max-age=2020',

              // 'Last-Modified': '2020',

                //'Etag': '20200217'

            })

        response.end('console.log("script loaded")')

    }



}).listen(8888)

console.log('server start on the 8888')

打開(kāi)開(kāi)發(fā)者工具,我們可以看到scripts第一次加載之后,再請(qǐng)求就會(huì)從緩存中獲取,看下圖黃色圈中部分,注意需要把紅色勾選去掉

[圖片上傳失敗...(image-ac4970-1583916328292)]

再看下響應(yīng)的header

[圖片上傳失敗...(image-6ec159-1583916328292)]

如果沒(méi)有設(shè)置緩存,每次請(qǐng)求都會(huì)從服務(wù)器獲取。需要驗(yàn)證可以自行測(cè)試下

緩存命中可以查看這張圖

[圖片上傳失敗...(image-3c9111-1583916328292)]

協(xié)商緩存驗(yàn)證頭(Last-Modified,Etag)

現(xiàn)在我們并不是真正需要驗(yàn)證資源,而是為了驗(yàn)證瀏覽器是否會(huì)把驗(yàn)證頭帶過(guò)來(lái),因此我們可以隨便設(shè)個(gè)Last-Modified和Etag,在server.js中修改response.writeHead


response.writeHead(200, {

    'Content-Type': 'text/javascript',

    'Cache-Control':'max-age=2020, no-cache',

    'Last-Modified': '2020',

    'Etag': '20200217'

})

啟動(dòng)服務(wù),下圖是第一次請(qǐng)求,可以看到響應(yīng)頭里面有Last-Modify和Etag

[圖片上傳失敗...(image-50dd66-1583916328292)]

再發(fā)送請(qǐng)求,可以看到在Request Headers中出現(xiàn),if-Modified-since和if-None-Match

[圖片上傳失敗...(image-b31b81-1583916328292)]

到這里還沒(méi)有結(jié)束,當(dāng)我們驗(yàn)證緩存完,如果還沒(méi)有過(guò)期,我們希望直接拿緩存,但是我們?cè)倏聪挛覀兊膔esponse

[圖片上傳失敗...(image-e93fbc-1583916328292)]

由圖發(fā)現(xiàn)response中還是有資源返回,并且code碼是200,這是為啥呢,原因很簡(jiǎn)單,我們?cè)诜?wù)端還沒(méi)有對(duì)if-Modified-since和if-None-Match進(jìn)行處理,我們把server.jshttp.createServer修改為


http.createServer(function (request, response) {

    console.log('request come',request.url)

    if (request.url == '/') {

        const html = fs.readFileSync('test.html', 'utf8')

        response.writeHead(200, {

            'Content-Type': 'text/html'

        })

        response.end(html)

    }

    if (request.url == '/script.js') {

        const etag = request.headers['if-none-match']

        if (etag === '20200217') {

            response.writeHead(304, {

                'Content-Type': 'text/javascript',

                'Cache-Control': 'max-age=2020,no-cache',

                'Last-Modified': '2020',

                'Etag': '20200217'

            })

            response.end('')

        } else {

            response.writeHead(200, {

                'Content-Type': 'text/javascript',

                'Cache-Control': 'max-age=2020,no-cache',

                'Last-Modified': '2020',

                'Etag': '20200217'

            })

            response.end('console.log("script loaded twice")')

        }

    }



}).listen(8888)

不管是否需要傳資源,我們都要在最后response.end,不然本次請(qǐng)求一直沒(méi)有結(jié)束。修改完之后,我們可以看到請(qǐng)求code碼變成了304,時(shí)間縮短了,但是在response中還是有資源,這又是什么情況,這時(shí)候我們確實(shí)成功驗(yàn)證了緩存,并拿取的是緩存資源,在瀏覽器的response中,瀏覽器會(huì)自動(dòng)把拿到的緩存資源顯示出來(lái),并沒(méi)有在服務(wù)器獲取。如果需要驗(yàn)證,可以自行在第一個(gè)response.end中添加其他內(nèi)容,再看瀏覽器接口的response

剛才讓瀏覽器去做協(xié)商緩存,是因?yàn)槲覀冊(cè)O(shè)置了no-cahce,我們把no-cache刪除,瀏覽器應(yīng)該是直接拿緩存(因?yàn)槲覀冊(cè)O(shè)置的max-age=2020),驗(yàn)證之前,我們得在剛才打開(kāi)的頁(yè)面去清楚瀏覽器的緩存,然后刪除代碼中的no-cache,重復(fù)刷新,都可以看到script.js 是from mermory cache。no-store也可再自行驗(yàn)證下

最后再提一下關(guān)于last-modify和Etag,last-modify我們可以在把數(shù)據(jù)庫(kù)取出的時(shí)候,拿取一個(gè)時(shí)間,最為數(shù)據(jù)的update time.Etag的話(huà),數(shù)據(jù)取出的時(shí)候做個(gè)數(shù)據(jù)簽名,存入Etag

cookie和session

http是不保存狀態(tài)的協(xié)議,因此我們需要一個(gè)身份能來(lái)證明訪(fǎng)問(wèn)服務(wù)器的是誰(shuí),這里我們用到的就是cookie和session

  • cookie的特性:通過(guò)Set-Cookie設(shè)置、下次請(qǐng)求會(huì)自動(dòng)帶上、鍵值對(duì)形式,可以設(shè)置多個(gè)

  • cookie的屬性:max-age和expires設(shè)置過(guò)期時(shí)間、HttpOnly無(wú)法通過(guò)document.cookie訪(fǎng)問(wèn)

接下來(lái)通過(guò)代碼看下cookie,在server.js中修改response.writeHead


{

    'Content-Type': 'text/html',

    'Set-Cookie': ['id=123;max-age=2','time=2020']

}

啟動(dòng)服務(wù)后,可以在application中的cookie看到兩個(gè)cookie或者network中的接口中。id=123這個(gè)cookie設(shè)置了過(guò)期時(shí)間,過(guò)一會(huì)兒再刷新可以看到id=123這個(gè)cookie消失了

前面說(shuō)過(guò),cookie跨域不共享,但是如果我想一級(jí)域名下的二級(jí)域名共享cookie,這時(shí)候我可以通過(guò)設(shè)置document.domain來(lái)實(shí)現(xiàn),具體如下


{

    'Content-Type': 'text/html',

    'Set-Cookie': ['id=123;max-age=2','time=2020;domain=test.com']

}

修改后,可添加host自行驗(yàn)證下

HTTP長(zhǎng)連接

長(zhǎng)連接指的是在一次請(qǐng)求完成后,是否要關(guān)閉TCP連接。如果TCP連接一直開(kāi)著,會(huì)有一定資源消耗,但是如果還有請(qǐng)求,就可以繼續(xù)在本次TCP連接上發(fā)送,這樣可以不用再三次握手,節(jié)省了時(shí)間。實(shí)際情況中,網(wǎng)站并發(fā)量比較大,因此是保持長(zhǎng)連接的,并且長(zhǎng)連接是可以設(shè)置超時(shí)時(shí)間的,如果在這個(gè)時(shí)間里都沒(méi)有發(fā)送請(qǐng)求了,那么連接就會(huì)關(guān)閉

接下來(lái)我們可以分析下實(shí)際場(chǎng)景,以百度首頁(yè)為例,打開(kāi)開(kāi)發(fā)者面板,然后network中,右擊name屬性,勾選Connection ID

我們看到大部分連接都有復(fù)用,在http1.1中,一個(gè)域名下最大TCP連接數(shù)為6個(gè)(Chorme),因此剛開(kāi)始的時(shí)候會(huì)一下創(chuàng)建6個(gè)連接,后面的請(qǐng)求會(huì)復(fù)用這些連接。

通過(guò)代碼來(lái)驗(yàn)證下這部分內(nèi)容,首先創(chuàng)建一個(gè)test.html


<body>

    <img src="/test1.jpg" alt="">

    <img src="/test2.jpg" alt="">

    <img src="/test3.jpg" alt="">

    <img src="/test4.jpg" alt="">

    <img src="/test5.jpg" alt="">

    <img src="/test6.jpg" alt="">

    <img src="/test7.jpg" alt="">

    <img src="/test8.jpg" alt="">

</body> 

新建server.js


const http = require('http')

const fs = require('fs')

http.createServer(function (request, response) {

    console.log('request come',request.url)

    const html = fs.readFileSync('test.html', 'utf8')

    const img = fs.readFileSync('timg.jpg')

    if (request.url === '/') {

        response.writeHead(200, {

            'Content-Type': 'text/html',

            // 'Connection': 'close'

        })

        response.end(html)

    } else {

        response.writeHead(200, {

            'Content-Type': 'image/jpg',

            // 'Connection': 'close'

        })

        response.end(img)

    }



}).listen(8888)

console.log('server start on the 8888') 

啟動(dòng)服務(wù)
[圖片上傳失敗...(image-b3942d-1583916328292)]

可以看下Waterfall,網(wǎng)絡(luò)請(qǐng)求分時(shí)過(guò)程。如果需要關(guān)閉長(zhǎng)連接,Connection的值可以寫(xiě)為close

這里再簡(jiǎn)單提下http2.0現(xiàn)在使用信道復(fù)用技術(shù),只需要?jiǎng)?chuàng)建一個(gè)TCP連接,所有同域下請(qǐng)求都可以并發(fā)。如果要使用http2.0,需要保證請(qǐng)求時(shí)https協(xié)議,并且后端需要做較大的改變,因此現(xiàn)在http2.0的使用目前還沒(méi)大面積

Redirect

當(dāng)我們通過(guò)url去訪(fǎng)問(wèn)一個(gè)資源的時(shí)候,該資源已經(jīng)不再u(mài)rl指定的位置了,服務(wù)器應(yīng)通知客戶(hù)端該資源現(xiàn)在所處的位置,瀏覽器再去請(qǐng)求該資源。

通過(guò)代碼來(lái)看下,新建一個(gè)server.js


const http = require('http')

const fs = require('fs')

http.createServer(function (request, response) {

    console.log('request comme', request.url)

    if (request.url === '/') {

        response.writeHead(302, {

            'Location': '/new'

        })

        response.end()

    }

    if (request.url === '/new') {

        response.writeHead(200, {

            'Content-Type': 'text/html'

        })

        response.end('<div>hello world</div>')

    }

}).listen(8888)

console.log('server listening on 8888')

此處測(cè)試是在同域的情況下,所以只寫(xiě)了一個(gè)路由,如果不相同,則把真正的地址替換/new.啟動(dòng)服務(wù),輸入localhost:8888之后,會(huì)直接跳轉(zhuǎn)到資源真正的位置,并且在network中也可查看發(fā)現(xiàn),除了圖標(biāo),有兩個(gè)請(qǐng)求。

代碼中我們寫(xiě)的code碼是302,如果我們改成200,就會(huì)發(fā)現(xiàn)沒(méi)有辦法重定向。302是臨時(shí)重定向,301是永久重定向,前面我們已經(jīng)說(shuō)過(guò)。如果我們把上面的302code碼改成301,我們會(huì)在終端中發(fā)現(xiàn),除了第一次,不管我們后面再輸入localhost:8888多少次,終端打印請(qǐng)求都只有重定向后的請(qǐng)求,只是因?yàn)闉g覽器記住了原地址被永久重定向了,所以,不會(huì)向原路徑發(fā)起請(qǐng)求。在實(shí)際開(kāi)發(fā)中,應(yīng)當(dāng)謹(jǐn)慎使用永久重定向,因?yàn)橐坏┯谰弥囟ㄏ蛄?,?huì)在瀏覽器盡可能長(zhǎng)的時(shí)間保留定向后的資源路徑而不會(huì)請(qǐng)求原路徑

結(jié)束語(yǔ)

本次分享目的是通過(guò)代碼來(lái)把原來(lái)我們知道的一些知識(shí)點(diǎn)可以再深入一些,梳理好Http知識(shí)的來(lái)龍去脈。希望能對(duì)一些小伙伴有所幫助,如果大家喜歡我的行文風(fēng)格的話(huà),我接下來(lái)將帶入web 服務(wù)器Nginx的一些實(shí)戰(zhàn),在實(shí)際開(kāi)發(fā)中我們會(huì)用nginx做代理和一些cache,因此作為一個(gè)http服務(wù),掌握它當(dāng)然也不可或缺

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容