做爲程序員的咱們天天都在和網絡請求打交道,而前端程序員接觸的最多的就是HTTP請求。平時工做中,處理網絡請求之類的操做是最多的了。可是一個請求從客戶端發出到被服務端處理、再回送響應,再被客戶端接收這一個閉環的底層細節可能並無深究過,本篇文章是個人一篇讀書筆記,總結出來剛好涉及到了這一過程,分享出來但願能夠對你們有所啓發。html
文中某些點若是表述有誤,歡迎指出來,不勝感激。前端
從輸入URL到頁面展示的過程程序員
這4個步驟包含了一個HTTP請求的完整生命週期,文章着重介紹第2步和第3步,也就是請求是如何在兩個物理端點之間進行通訊的。數據的發出和接收必然會經歷一些處理、解析的過程,這些過程在系統的不一樣層次進行。面試
一個HTTP請求從源端發出到在終端接收的處理過程都是要通過如下四層。其中每一層都有各自的協議。windows
咱們先來理解一下協議是什麼,協議是通過約定,雙方共同認可,而且須要共同遵照的規則。上面的每一層,都有各自的協議,協議的執行者是通訊鏈路兩端內的對應層。每一層經過協議來理解數據,並進行處理。api
上圖中只舉例出了最多見的協議,實際上每一層都有細分的協議:瀏覽器
應用層:應用程序負責將數據以相應規則(協議)進行包裝,發給傳輸層緩存
傳輸層:負責將應用層傳過來的數據進行分組,爲確保終端接收數據的順序和完整性,會對每一個分組進行標記,交給網絡層安全
網絡層:負責將傳輸層發來的數據分組發送到目標終端服務器
IP:網際協議
鏈路層:爲網絡層發送和接收數據單元
數據在通過每一層的時候都要被對應的協議包裝,到達終端的時候,要一層一層的解包。這兩個過程叫封裝和分用。
發送時,用戶數據被HTTP封裝爲報文,每一層會將上層傳過來的報文做爲本層的數據塊,並添加本身的首部,其中包含了協議標識,這一總體做爲本層報文向下傳遞。
接收時,數據自下而上流動,通過每一層時被去掉報文首部,根據報文標識肯定正確的上層協議,最終到應用層被應用程序處理。
源端發送HTTP報文時,報文會以數據流的形式經過一條已經打開的TCP鏈接按序傳輸,TCP收到數據流後會將其分割成小的數據塊,每一個小塊被添加的TCP首部與數據塊共同組成了TCP分組,分組經由網絡層發送,網絡層遵循IP協議,當收到分組發送請求後,會將分組其放入IP數據報,填充報頭,將數據報發經由鏈路層發送出去。
這一過程通過每層的時候都會被增長一些首部信息,有時還須要增長尾部信息,每一層都會把數據封裝到各自的報文中,
並在報文首部添加協議標識,這個過程叫封裝。
終端接收到一個以太網數據幀時,數據自底層向上流動,去掉髮送時各層協議加上的報文首部,每層協議都要檢查報文首部的協議標識,從而肯定上層協議,保證數據被正確處理,這個過程叫分用。
終端從鏈路層接收到數據請求後,進入網絡層對數據進行解析,交給給傳輸層,校驗分組順序和完整性,從數據塊中取出數據,獲得HTTP報文,交給應用層進行處理。這個過程會逐層剝離報頭還原數據。
咱們已經知道,數據是從源端自上而下到終端自下而上被一層層處理的,如今就來看一下每層都作了什麼事情。
HTTP屬於應用層,用戶觸發交互所產生的行爲數據和服務端對此的響應都由它封裝成HTTP報文,再交由下層協議進行處理。報文的做用是客戶端與服務端溝通的載體,雙方都要遵循統一規則對信息進行處理,這一規則稱爲HTTP。
客戶端與服務端的交互每每很是複雜,爲了使雙方都能高效、明確、安全地通訊(例如傳遞意圖與狀態、承載數據、攜帶認證信息、控制鏈接行爲與緩存),須要依賴報文中的結構來實現,下面先從結構開始看。
HTTP報文的結構分爲請求和響應兩種,請求報文封裝用戶操做產生的動做,告知服務器應採起什麼行爲,響應報文來告知客戶端請求的結果。
請求報文格式:
<method> <request-url> <version> // 起始行格式 <headers> // 首部 <body> // 實體
響應報文格式:
<method> <status> <reason-phrase> // 起始行格式 <headers> // 首部 <body> // 實體
報文的起始行代表了報文的開始,請求和響應各自的起始行的格式也不相同。
請求報文的起始行說明要作什麼,結構爲方法 + 請求URL + 協議版本,中間用空格作分隔:
GET /api/nht/blog/example HTTP/1.1
響應報頭的起始行說明發生了什麼,結構爲協議版本 + 狀態碼 + 描述文本,中間用空格作分隔:
HTTP/1.1 200 OK
方法來告訴服務端請求報文要作的事情,狀態碼來通知客戶端服務端依據請求報文完成動做以後的大體結果。
常見的HTTP方法以下:
方法 | 含義 | 有無主體 |
---|---|---|
GET | 從服務端獲取資源 | 無 |
HEAD | 只獲取資源頭部 | 無 |
POST | 向服務端發送數據 | 有 |
PUT | 將客戶端發送的數據存到服務端,應用場景多爲修改 | 有 |
OPTIONS | 對服務端進行預檢,例如服務端支持哪些方法 | 無 |
DELETE | 從服務端刪除資源 | 無 |
請求完成時,響應報文中會有一個狀態碼,用來表示這次請求的狀態,是成功了仍是失敗了,或者時須要重定向。狀態碼的範圍從100到599,
其中有部分是已經定義的。不一樣的範圍表示的含義也不一樣:
範圍 | 已定義範圍 | 含義 |
---|---|---|
100~199 | 100~101 | 信息提示 |
200~299 | 200~206 | 成功 |
300~399 | 300~305 | 重定向 |
400~499 | 400~415 | 客戶端錯誤 |
500~599 | 500~505 | 服務端錯誤 |
首部是請求和響應報文中的一些信息,形式爲鍵值對,每對鍵值結尾是CRLF換行符,它決定了請求或者響應報文的屬性,好比Content-Type代表了請求主體的數據類型,Date說明了請求的建立時間。
客戶端與服務端經過首部來協商具體行爲。能夠根據請求、響應、結構等,將首部分爲五種。
HTTP/1.0 200 OK Server: xxxxxxx Date: Sun,17 Sep 2019 02:01:16 GMT --------------------------------實體首部 Content-Type: text/plain Content-length: 18 --------------------------------實體主體 Hi! I'm a message! --------------------------------
實體部分是可選的,它被用來運送請求或者響應的數據,實體由實體首部 + 實體主體組成,實體首部對實體主體作描述。HTTP/1.1定義瞭如下的基本實體首部字段:
以上是HTTP報文包含的主要結構,當請求報文到達服務器時,服務器會對報文中的內容解析出來,根據方法、資源路徑、首部、和主體來處理請求,而後經過對請求資源的訪問結果,來構建響應,回送給客戶端。
HTTP鏈接是創建在TCP鏈接的基礎之上的,TCP提供可靠的數據鏈接。當要傳輸一個HTTP報文時,報文數據會以流的形式經過一條已經打開的TCP鏈接按順序傳輸,TCP會將收到的數據分紅小塊,每塊是一個TCP分組。
因爲數據是分紅小塊發送的,因此完整可靠的數據傳輸主要體如今:分組是否完整、分組順序是否正常、分組是否損壞、分組數據是否重複。這些能夠經過TCP的檢驗和、序列號、確認應答、重發控制、鏈接管理和窗口機制來控制。
TCP是傳輸控制協議,傳輸控制主要依賴首部包含的6個標誌,它們控制報文的傳輸狀態,以及發送端和接收端應對數據採起的動做。當它們的值爲1時,標誌對應的各自功能才容許被執行,好比當URG爲1時,報文首部的緊急指針部分纔有效。
源端口和目的端口: 標識發送方和接收方的端口號,一個TCP鏈接經過4個值確認:源IP、源端口、目的IP、目的端口
,其中源IP和目的IP包含在IP分組內。
首部長度: 表示TCP首部的字節長度,也能標記出從多少個字節開始,纔是須要傳輸的數據。
TCP段序號: 本段報文發送的數據第一個字節的序號,每段報文中的數據的每一個字節都有序號,第一個字節的序號從0開始,依次加1,加到2的32次方減1後再次從0開始。
TCP段確認序號 : 當首部標誌ACK爲1時
,確認序號有效。TCP段被接收端接收後,會回送給發送端一個確認號,爲上次接受的最後一個字節序號加1。
檢驗和: 由發送端計算,接收端驗證,若是接收方檢測到檢驗和不正確,代表該TCP段可能有損壞,會被丟棄,同時接收端向回送一個重複的確認號(與最近的一次正確的報文傳輸的確認號重複),代表接收到的TCP段是錯誤的,並告知本身但願收到的序號。這時發送端須要當即重傳出錯的TCP段。
緊急指針: 當首部標誌URG爲1時
,緊急指針有效,表示發送端向接收端要發送緊急數據。緊急指針是一個正偏移量,它和TCP段序號相加,計算出緊急數據的最後一個字節的序號。好比接收方接收到數據,從序號爲1000的字節開始讀取,緊急指針爲1000,那麼緊急數據就是序號從1000到2000之間的字節。這些數據由接收方決定如何處理。
窗口尺寸: 決定了TCP一次成塊數據流的吞吐量。須要注意的是,它表示的是發送一方的容許對方發送的數據量,好比發送方首部中的窗口大小爲1000,就表示發送方最多能夠接受對方發來的1000個字節的數據量。這與發送方的數據緩存空間有關,會影響TCP的性能。
首部標誌PSH: 若是須要告訴接收方將數據當即所有提交給接收進程,發送方須要將PSH置爲1,這裏的數據是和PSH一塊兒傳送的數據以及以前接收到的所有數據。若是接收方收到了PSH爲1的標誌,須要當即將數據提交給接收進程,不用再等待有沒有其餘數據進來。
復位標誌RST: 當RST爲1時,表示鏈接出現了異常狀況,接收方將終止鏈接,通知應用層從新創建鏈接。
同步序號SYN: 用來創建鏈接,涉及到TCP的三次握手。
在第三步的確認分組中,是能夠攜帶要發送的數據的。
鏈接終止標誌FIN: 用來關閉鏈接,當一端完成數據發送任務後會發送一個FIN標誌來終止鏈接,但由於TCP在兩個方向(C-S,S-C)上會有數據傳遞,每一個方向有各自的發送FIN & 確認關閉流程,因此會有四次交互,也稱爲四次揮手。
TCP段序號與確認序號保證了數據的順序,檢驗和確保數據的完整性,緊急指針保證緊急數據可被及時處理。另外,TCP還有一些超時重傳、
擁塞避免、慢啓動的機制,均可以保證分組數據按照順序完整的傳到目標端。
若是說TCP分組是包裝貨物的集裝箱,那麼IP就是運送集裝箱的卡車。IP協議提供了兩個節點之間的鏈接,保證將TCP數據儘量快地從源端送到終端,但卻不能保證傳輸的可靠性。
IP層會將上層傳過來的TCP分組封裝,帶上本身的首部,再進行選路、是否分片以及重組的工做,最終到達目的地,這個過程當中,IP首部起了重要的做用,下面讓咱們看一下首部的結構。
版本: 表示當前IP協議的版本,目前版本號是4,還有一種是6,也就是IPV4和IPV6,若是發送和接收這兩端的版本不一致,那麼當前IP數據報會被丟棄。
首部長度: 整個首部的長度,最長爲60字節。
服務類型(TOS): 用來區分服務的類型,但其實IP層在工做的時候一直沒有實際使用過,現有的TOS只有4bit的子字段,和1bit的未用位。未用位必須置爲0。TOS的4個bit中只能將一個置成1,用來表示當前服務類型。4bit對應的4個服務類型分別爲:最小時延、最大吞吐量、最高可靠性和最小費用。
總長度: 表示當前的數據報報文的總長度,單位爲字節,能夠結合首部長度計算出報文內數據的大小以及起始位置。
下面這三個首部字段涉及到IP數據報的分片與重組過程,因爲網絡層通常會限制每一個數據幀的最大長度,IP層發送數據報會在選路的同時查詢當前設備網絡層的每一個數據幀的最大傳輸長度,一旦超出,數據報就會被進行分片,到達目的地以後再進行重組,此時就會用如下三個字段做爲重組依據。須要注意的是:由於存在選路的過程,數據報通過的每層路由設備對於數據幀的最大傳輸長度都不一樣,因此分片可能發生在任意一次選路的過程當中。
分組標識: 這個標識至關於ID,每成功發送一個分片,IP層就會把這個分組ID加1。
標誌: 共佔用三位,分別是R、D、M,R目前尚未被使用,有用的是D、和M。這個字段表示了數據報的分片行爲。D若是爲1的話,表示數據無需分片,一次傳輸完;M若是爲1,表示數據是分片的,後邊還有數據,當它爲0時,就表示當前數據報是最後一個分片,或者只有這一個分片。
片偏移: 標識了當前分片距離原始數據報開始處的位置,分片以後,每一片的總長度會改爲這一片的長度值,而不是整個數據報的長度。
生存時間:(TTL) 能夠決定數據報是否被丟棄。由於IP發送數據是逐跳的,數據有可能在被設置了路由功能的不一樣的IP層之間轉發,因此生存時間表示了數據報最多個能夠通過多少個處理過它的路由,每通過一層路由,值減去1,當值爲0時數據報就被丟棄,而且發送一個帶有錯誤消息的報文(ICMP,IP層的組成部分,被用來傳遞一些錯誤信息)給源端。生存時間能夠有效解決數據報在一個路由環路中一直轉發的問題。
首部檢驗和: 校驗數據報的完整性,發送端對首部進行求和,將結果存在檢驗和中,接收端再計算一遍,若是計算結果與存在檢驗和中的結果一致,則說明傳輸過程是OK的,不然這個數據報就會被丟棄。
上層協議: 決定了接收端在分用的時候將數據交給哪一個上層協議去處理,例如TCP或者UDP。
源IP: 記錄了發送端的IP,在回送錯誤消息時用到。
目的IP: 表示目的IP,每一次選路都要以它來作決策。
由於IP首部只包含了目的IP地址,並不體現完整的路徑,當向外發送數據時,IP層會根據目的IP在本機路由表中的查詢結果來作出選路決策,數據報會逐跳地被運送到目的地,這裏的每一跳,就是一次路由選擇。
IP層既可配置成路由器,也能夠配置成主機。當配置成路由功能時,能夠對數據報進行轉發,配置成主機時,若是目的IP不是本機IP,數據報會被丟棄。
具備路由功能的IP層在當目標IP不是本機地址的時候是根據什麼判斷轉發到哪一站呢?要理解這個問題,須要先明白路由表的結構,如下是IP層維護的路由表,(windows系統能夠在控制檯輸入netstat -r來查看路由表)
Destination | Gateway | Flags | Refcnt | Use | Interface |
---|---|---|---|---|---|
140.252.13.65 | 140.252.13.35 | UGH | 0 | 0 | emd0 |
127.0.0.1 | 127.0.0.1 | UH | 1 | 0 | lo0 |
default | 140.252.13.33 | UG | 0 | 0 | emd0 |
140.252.13.32 | 140.252.13.34 | U | 4 | 25043 | emd0 |
(路由表數據來源於《TCP/IP詳解卷一:協議》)
Flags(標誌):表示當前這一條路由記錄的屬性,具體用五個不一樣的標誌來表示:
每收到一個數據報時候,IP層就會根據目的IP在路由表裏查詢,根據查詢狀態會導向三種結果:
要是上邊三個都沒有結果,那麼數據報就不能被髮送。IP數據報就是這樣一跳一跳地被送往目的主機的,但數據報有固有的長度,一旦超出了目的主機的MTU,就會被分片。
TCP在進行握手的時候,會根據目的端IP層的最大傳輸單元(MTU)來決定TCP數據每次能傳輸的最大數據量(MSS),以後TCP會對數據依照MSS來進行分組,每一個分組會被包裝進一個IP數據報內。當IP數據報通過選路過程當中的任意一層路由時,有可能被MTU限制住從而被分片,這時IP首部的3bit標誌中的M標誌被置爲1,表示須要分片。每一個分片的首部基本同樣,只是片偏移有所不一樣。依據片偏移,這些分片在目的端被重組成一個完整的IP數據報(一個TCP分組)。IP傳輸是無序的,因此獲得的數據報也是無序的,但若是數據完整,TCP會根據首部中的字段對其進行排序。一旦IP分片丟失,IP層沒法組成完整的數據報,就會告訴TCP層,TCP進行重傳。
當IP層將數據封裝好以後,只有目標主機的IP地址。光有IP地址並不能直接把數據報發送過去,由於每一臺硬件設備都有本身的MAC地址,是一個48bit的值。如今知道目標IP的地址,須要找到這個IP對應的MAC地址。這個過程要經過查詢路由表,再結合鏈路層的ARP協議,最終得到目標IP對應的MAC地址。
IP只能讓數據在邏輯端點之間流動,可是IP之下還有網絡接口層,這一層也有本身的地址(MAC地址:用於在網絡中惟一標識一個網卡),從IP地址到MAC地址須要一個轉換的過程,ARP就是提供這一服務的。
ARP協議實現了從IP地址到MAC地址的映射。一開始,起點並不知道目標的MAC地址,只有目標IP,要獲取這個地址就涉及到了ARP的請求和應答。一樣,ARP也有本身的分組,先看一下分組格式。
以太網目的地址: 目的端的MAC地址,當ARP緩存表中沒有的時候,這裏爲廣播地址。
以太網源地址: 發送端的MAC地址。
幀類型: 不一樣的幀類型有不一樣的格式和MTU值,不一樣的類型有不一樣的編號,這裏ARP對應的編號是0x0806。
硬件類型: 指鏈路層網絡類型,1爲以太網。
協議類型: 指的是要轉換的地址類型,0x0800爲IP地址。好比將以太網地址轉換爲IP地址。
op(操做類型): 有四種,分別是ARP請求(1),ARP應答(2),RARP請求(3),RARP應答(4)。
源MAC地址: 表示發送端MAC地址。
源IP地址: 表示發送端IP地址。
目的以太網地址: 表示目標設備的MAC物理地址。
目的IP地址: 表示目標設備的IP地址。
當兩臺設備發送報文以前,源端的鏈路層會用ARP協議去詢問目的端的MAC地址,ARP會將一個請求廣播出去,以太網上的每個主機都會收到這份廣播,廣播的目的是詢問目標IP的MAC地址,內容主要是先介紹本身的IP和MAC地址,再詢問若是你有目標IP,請回復你的硬件地址。若是一個主機收到廣播後看到本身有這個IP,而且請求內有源IP和MAC地址,那麼就會向源主機迴應一個ARP應答。若是沒有目標IP,就會丟棄這個請求。能夠看出請求是向外廣播的,而應答是單獨迴應的。
但不能每次通訊以前都去經歷一次請求-應答過程,在成功地接收到應答以後,IP和MAC地址的映射關係就會緩存在ARP緩存表中,有效期通常爲20分鐘,便於網絡層下次直接進行封裝,因此,完整的過程應該是:
IP層接收到TCP分組後,發送或者封裝以前,經過查詢路由表:
上面全部東西都準備好了,封裝發送的實際上是以太網數據幀。以太網目的地址、以太網源地址、幀類型這三者組成了幀首部。在首部以前還會插入前同步碼和幀開始定界符,告知接收端作一些準備工做。幀檢驗序列 FCS被添加進尾部,用來檢測幀是否出錯。
前同步碼: 協調終端接收適配器的時鐘頻率,讓它與發送端頻率相同。
幀開始定界符: 幀開始的標誌,表示幀信息要來了,準備接收。
目的地址: 接收幀的網絡適配器的MAC地址,接收端收到幀時,會首先檢查目的地址與本機地址是否相符,不是的話就會丟棄。
源地址: 發送端設備的MAC地址。
類型: 決定接收到幀以後將數據交由那種協議處理。
數據: 交給上層的數據。在本文的場景中指IP數據報。
幀檢驗序列: 檢測這一幀是否出錯,發送方計算幀的循環冗餘碼校驗(CRC)值,把這個值寫到幀裏。接收方計算機從新計算 CRC,與 FCS 字段的值進行比較。若是兩個值不相同,則表示傳輸過程當中發生了數據丟失或改變。這時,就須要從新傳輸這一幀。
一個網絡請求從源端一層層封裝,再到終端一層層拆分,最後的全部過程基本梳理清楚,文章只是簡單梳理了一下大概流程,而且只以HTTP報文經過TCP協議通過IP傳送這一過程爲例,實際還有不少概念沒有覆蓋,好比鏈路層的尾部封裝、IP的動態選路、逆地址解析協議RARP、UDP協議相關的概念,建議你們能夠閱讀下面列出的參考資料,相信會有更多收穫。
參考文章:
《HTTP權威指南》
《TCP/IP詳解 卷一:協議》