HTTP 連接詳解

概述

世界上幾乎所有的 HTTP 通信都是由 TCP/IP 承載的,客戶端可以打開一條TCP/IP連接,連接到任何地方的服務器。一旦連接建立,客戶端和服務器之間交換的報文就永遠不會丟失、受損或失序

TCP(Transmission Control Protocol)傳輸控制協議,是一種面向連接的、可靠的、基於字節流的傳輸層通信協議。通俗來講,TCP就是雙方通信的一個規範標準,負責對數據的傳輸進行一定的控制

HTTP 要傳送一條報文時,會以流的形式將報文數據的內容通過一條打開的 TCP 連接按序傳輸。TCP 收到數據流之後,會將數據流分成被稱作段的小數據塊,並將段封裝在 IP 分組中,通過因特網進行傳輸。所有這些工作都是由 TCP/IP 軟件來處理的、HTTP 程序員什麼都看不到

每個 TCP 段都是由 IP 分組承載,從一個 IP 地址發送到另一個 IP 地址,每個 IP 分組包括:

  • IP 分組首部(通常爲 20 字節)
  • TCP 段首部(通常爲 20 字節)
  • TCP 數據塊(0 個或多個字節)

IP 首部包含了源和目的 IP 地址、長度和其他一些標記。TCP 段的首部包含了 TCP 端口號、TCP 控制標記,以及用於數據排序和完整性檢查的一些數字值

TCP 段首部格式如下:

  • 源端口號就是指本地端口,目的端口號就是遠程端口

  • 序號,也稱序列號(Sequence Number),用於 TCP 通信過程中某一傳輸方向上字節流的每個字節的編號,以防止亂序問題。簡單來說,就是在傳輸過程中用序列號來標記自己的位置,保證數據能按序傳輸

  • 確認序號,也稱確認序列號(Acknowledgment Numbe),是接收確認端所期望收到的下一序列號。確認序號應當是上次已成功收到數據字節序號加 1,只有當標誌位中的 ACK 標誌爲 1 時該確認序列號的字段纔有效。主要用來解決不丟包的問題

  • 標誌位,TCP Flag,TCP 首部中有 6 個標誌比特,它們中的多個可同時被設置爲 1,主要是用於操控 TCP 的狀態機,依次爲 URG,ACK,PSH,RST,SYN,FIN

    • ACK

      表示應答域有效,這個標識可以理解爲發送端發送數據到接收端,發送的時候 ACK 爲 0,標識接收端還未應答,一旦接收端接收數據之後,就將 ACK 置爲 1,發送端接收到之後,就知道了接收端已經接收了數據

    • SYN

      表示同步序列號,用來建立 TCP 連接。SYN 標誌位和 ACK 標誌位搭配使用,當連接請求的時候,SYN = 1,ACK = 0;連接被響應的時候,SYN = 1,ACK = 1;這個標誌的數據包經常被用來進行端口掃描。掃描者發送一個只有 SYN 的數據包,如果對方主機響應了一個數據包回來 ,就表明這臺主機存在這個端口

    • FIN

      表示發送端已經達到數據末尾,也就是說雙方的數據傳送完成,沒有數據可以傳送了,發送 FIN 標誌位的 TCP 數據包後,連接將被斷開。這個標誌的數據包也經常被用於進行端口掃描

  • 窗口大小(Window Size),也稱爲滑動窗口大小,用來進行流量控制


TCP 三次握手

TCP 三次握手,即建立 TCP 連接,需要客戶端和服務端總共發送 3 個包以確認連接的建立。在 Socket 編程中,這一過程由客戶端執行 connect 來觸發

TCP 握手的目的有三個:

  • 確認雙方的接收與發送能力是否正常
  • 初始化序列號,爲後面的可靠傳送做準備
  • 進行數字證書的驗證以及加密密鑰的生成

  • 第一次握手:客戶端發送請求報文將 SYN = 1 同步序列號和初始化序列號 seq = J 發送給服務端,發送完之後客戶端處於發送等待狀態
  • 第二次握手:服務端受到 SYN 請求報文之後,如果同意連接,會以自己的同步序列號 SYN = 1、初始化序列號 seq = K 和確認序列號(期望下次收到的數據包)ack = J + 1 以及確認號 ACK = 1 報文作爲應答,此時服務器爲確認接收狀態
  • 第三次握手:客戶端接收到服務端的 SYN + ACK 之後,知道可以發送下一序列的數據包了,然後發送同步序列號 ack = K + 1 和數據包的序列號 seq = J + 1 以及確認號 ACK = 1 確認包作爲應答,客戶端轉爲確認連接狀態

爲什麼是三次握手,而不是一次、二次呢?因爲有可能出現這種情況:客戶端發送了一個連接請求,但出現網絡延遲,導致客戶端沒有及時收到服務端的響應,就會認爲本次請求失效。而這時原本延遲的請求又來到服務端,服務端確認並保持等待狀態,但實際上此時客戶端並沒有與服務端連接的意思,這就會導致服務器一直處於等待狀態,造成資源浪費


TCP 四次揮手

TCP 四次揮手,即終止 TCP 連接,需要客戶端和服務端總共發送 4 個包以確認連接的斷開。在 Socket 編程中,這一過程由客戶端或服務端任一方執行 close 來觸發

  • 第一次分手:第一次分手無論是客戶端還是服務端都可以發起,因爲 TCP 是全雙工的。假如客戶端發送的數據已經發送完畢,發送 FIN = 1 告訴服務端,客戶端所有數據已經全發完了,服務端可以關閉接收了,但如果服務端還有數據要發給客戶端,客戶端照樣可以接收的。此時客戶端處於 FIN_WAIT_1 等待關閉狀態
  • 第二次分手:服務端接收到客戶端的釋放請求連接之後,知道客戶端沒有數據要發給自己了,然後服務端發送 ACK = 1 告訴客戶端已經收到發給自己的信息,此時服務端處於 CLOSE_WAIT 等待關閉狀態
  • 第三次分手:此時服務端已經沒有數據向客戶端發送了,然後發送一個 FIN = 1,用於告訴客戶端,服務端的所有數據發送完畢,客戶端可以關閉接收數據連接了。此時服務端狀態處於 LAST_ACK 確認關閉狀態
  • 第四次分手:此時如果客戶端收到了服務端發送完的信息之後,就發送 ACK = 1,告訴服務端,客戶端已經收到了服務端的信息,服務端處於 CLOSED 狀態,四次揮手全部完成

爲什麼是四次揮手呢?因爲關閉連接時,己方收到對方的 FIN 報文,僅僅表示對方不再向自己發送數據,但還能接受數據。己方可能還有數據要發送給對方,所以不能向三次握手一樣直接把 ACK 和 SYN 放一起發送,而是先發送 ACK,直到沒有數據要發送了,纔是 FIN 確認關閉連接


TCP 性能優化

HTTP 位於 TCP 上層,所以 HTTP 事務的性能在很大程度上取決於底層 TCP 通道的性能

1. 延遲確認

爲了避免網絡延遲導致的數據丟失,TCP 實現了自己的確認機制來確保數據的成功傳輸

每個 TCP 段都有一個序列號和數據完整性校驗和。每個段的接收者收到完好的段時,都會向發送者回送小的確認分組。如果發送者沒有在指定的窗口時間內收到確認信息,發送者就認爲分組已被破壞或損毀,並重發數據

由於確認報文很小,所以 TCP 允許將返回的確認信息與輸出的數據分組結合在一起,更有效地利用網絡。爲了增加確認報文找到同向傳輸數據分組的可能性,TCP 實現了一種【延遲確認】算法,延遲確認算法會在一個特定的窗口時間(通常是 100-200 毫秒)內將輸出確認存放在緩衝區中,以尋找能夠捎帶它的輸出數據分組。如果在那個時間段內沒有輸出數據分組,就將確認信息放在單獨的分組中傳送

2. TCP 慢啓動

TCP 數據傳輸的性能還取決於 TCP 連接的使用期,TCP 連接會隨着時間進行自我調節,起初會限制連接的最大速度,如果數據成功傳輸,會隨着時間的推移提高傳輸的速度,這種調節被稱爲 TCP 慢啓動,用於防止因特網的突然過載和擁塞

TCP 慢啓動限制了一個 TCP 端點在任意時刻可以傳輸的分組數。簡單來說,每成功接收一個分組,發送端就有了發送另外兩個分組的權限。如果某個 HTTP 事務有大量數據要發送,是不能一次將所有分組都發送出去的,必鬚髮送一個分組,等待確認,然後發送兩個分組,每個分組都必須被確認,然後發送四個分組,以此類推,這種方式被稱爲【打開擁塞窗口】

由於存在這種擁塞控制特性,所以新連接的傳輸速度會比已經交換過一定量數據的連接慢一些

3. Nagle 算法與 TCP_NODELAY

TCP 有一個數據流接口,應用程序可以通過它將任意尺寸的數據放入 TCP 棧中,即使一次只放一個字節。但是,每個 TCP 段都至少裝載了40個字節的標記和首部如果 TCP 發送了大量包含少量數據的分組,網絡的性能就會嚴重下降

Nagle 算法試圖在發送一個分組之前,將大量 TCP 數據綁定在一起,以提高網絡效率。Nagle 算法鼓勵發送全尺寸的分組,只有當所有其他分組都被確認之後,Nagle 算法才允許發送非全尺寸的分組。如果其他分組仍然在傳輸過程中,就將那部分數據緩存起來。只有當掛起分組被確認,或者緩存中積累了足夠發送一個全尺寸分組的數據時,纔會將緩存的數據發送出去

Nagle 算法會引發幾種 HTTP 性能問題,首先,小的 HTTP 報文可能無法填滿一個分組,可能會因爲等待那些永遠不會到來的額外數據而產生時延。其次,Nagle 算法與延遲確認之間的交互存在問題,Nagle 算法會阻止數據的發送,直到有確認分組抵達爲止,但確認分組自身會被延遲確認算法延遲 100-200 毫秒

HTTP應用程序常常會禁用 Nagle 算法,如果要使用的話,一定要確保會向TCP寫入大塊的數據,不會產生一堆小分組

4. TIME_WAIT 累積與端口耗盡

當某個 TCP 端點關閉 TCP 連接時,會在內存中維護一個小的控制塊,用來記錄最近所關閒連接的 IP 地址和端口號。這類信息只會維持一小段時間,通常是所估計的最大分段使用期的兩倍(稱爲 2MSL,通常爲兩分鐘左右),以確保在這段時間內不會創建具有相同地址和端口號的新連接

2MSL 的連接關閉延遲在某些情況下會出現問題,例如客戶端每次連接到服務器時,都會獲得一個新的端口,以實現連接的唯一性,但由於可用端口的數量有限(比如 60000 個),而且在 2MSL 秒(比如 120 秒)內連接是無法重用的,連接率就被限制在 60000/120=500 次/秒,如果服務器的連接率高於 500 次/秒,就會遇到端口耗盡問題


HTTP 連接處理

1. 串行事務處理延時

如果只對連接進行簡單的管理,TCP 的性能時延可能會疊加起來。比如,假設有一個包含了三個嵌入圖片的 Web 頁面,測覽器需要發起四個 HTTP 事務來顯示此頁面:一個用於頂層的 HTML 頁面,三個用於嵌入的圖片。如果每個事務都需要建立一條新的連接,那麼連接時延和慢啓動時證就會疊加起來

除了串行加載引入的實際時延之外,加載一幅圖片時,頁面上其他地方都沒有動靜也會讓人覺得速度很慢,用戶更希望能夠同時加載多幅圖片。並行加載的另一個缺點是,有些溝覽器在對象加載完畢之前無法獲知對象的尺寸,而它們可能需要尺寸信息來決定將對象放在屏幕的什麼位置,所以在加載了足夠多的對象之前,無法在屏靠上顯示任何內容

2. 並行連接

HTTP 允許客戶端打開多條連接,並行地執行多個 HTTP 事務,提高加載速度,但並不是絕對的,在帶寬較小的情況下,並行執行多個 HTTP 事務帶來的性能提升就很小,甚至沒什麼提升。而且,打開大量連接會消耗大量的資源

3. 持久連接

Web 客戶端經常會打開到同一個站點的連接,比如,一個 Web 頁面上的大部分內嵌圖片通常都來自同一個 Web 站點。因此,對某服務器 HTTP 請求的應用程序很可能會在不久的將來發起更多的請求。因此,HTTP/1.1 允許 HTTP 設備在事務處理結束之後會將 TCP 連接保持在打開狀態,以便在未來重用現存的連接發起 HTTP 請求,稱爲持久連接,直到客戶端或服務器決定將其關閉爲止。持久連接可以避免緩慢的連接建立階段,以及慢啓動的擁塞適應階段,以便更快速地進行數據傳輸。通常情況下,持久連接和並行連接配合使用

實現 HTTP/1.0 的客戶端可以通過包含 Connection:Keep-Alive 首部請求將一條連接保持在打開狀態,如果服務器願意爲下一條請求將連接保持在打開狀態,就在響應中包含相同的首部。如果響應中沒有 Connection:Keep-Alive 首部,客戶端就認爲服務器不支特 Keep-alive,會在收到響應報文之後關閉連接

可以用 Keep-A1ive 通用首部中指定的,由逗號分隔的選項來調節 keep-alive 的行爲

Connection: Keep-Alive
Keep-Alive: max=5, timeout=120
  • timeout:估計服務器希望將連接保持在活躍狀態的時間,這並不是一個承諾值
  • max:估計服務器還希望爲多少個事務保持此連接的活躍狀態,這並不是一個承諾值

HTTP/1.1 逐漸停止對 keep-alive 連接的支持,用一種名爲持久連接(persistentconnection)的改進型設計取代了它。,HTTP/1.1 默認所有連接都是持久的,要在事務處理結束之後關閉連接,必須向報文中顯式地添加一個 Connection: close 首部。客戶端收到響應後,除非響應中包含 connection: close 首部,否則連接就維持在打開狀態。但是,客戶端和服務端仍然可以隨時關閉空閒的連接,不發送 connection: close 並不意味着服務端承諾永遠將連接保持在打開狀態

4. 管道化連接

HTTP/1.1 允許在持久連接上可選的使用管道,在響應到達之前,可以將多條請求放入隊列,當第一條請求到達服務器,第二條和第三條請求也可以開始發送了。在高時延網絡條件下,這樣可以降低網絡的環回時間,提高性能

對管道化連接有幾點注意事項:

  • 如果 HTTP 客戶端無法確認連接是持久的,就不應該使用管道
  • 必須按照與請求相同的順序回送 HTTP 響應,HTTP 報文中沒有序列號標籤,如果收到的響應失序了,就沒辦法將其與請求匹配起來了
  • HTTP 客戶端必須做好連接會在任意時刻關閉的準備,還要準備好重發所有未完成的管道化請求。如果客戶端打開了一條持久連接,並立即發出了十條請求,服務器可能只處理了五條請求之後關閉連接,客戶端必須能夠應對過早關閉連接的情況,重新發送請求
  • HTTP 客戶端不應該用管道化的方式發送會產生副作用的請求(比如 POST),如果出錯,管道化方式會阻礙客戶端了解服務器執行的是一系列管道化請求中的哪一個
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章