1. 程式人生 > >Http協議與TCP協議簡單理解後續

Http協議與TCP協議簡單理解後續

大約2年前寫了一篇關於HTTP協議與TCP協議的文章,原文連結。最近再次簡單讀了一遍《TCP/IP協議卷》,有了一些新的理解。這篇文章沒有一個很好的連貫性,都是我在讀書過程中總結的知識點,整體比較鬆散,但是個人感覺知識點都是非常重要,有很多地方讓我明白了迷惑很久的問題。

寫了這麼長時間的程式碼,發現自己對TCP/IP瞭解的並不是很透徹。雖然會用C#HttpClient類來進行網路程式設計,也可以使用Chrome的開發者工具來檢測每一次的HTTP請求的報文頭與報文體,也知道cookie的存在方式,但是對於這些資料怎麼在網路上傳輸還是很模糊,資料是怎麼從客戶端的檔案或者字串轉換為二進位制數並且傳送到伺服器端的?

為了弄明白這些問題,最近大致的讀了讀《TCP-IP詳解(卷一、二、三)》,也算是比以前清楚多了,下面是讀的過程中的一些知識點。

首先,我們要弄明白這個計算機網路分層的概念。下邊這個圖是一個經典的分層描述,記得大學時候課本上的圖也跟這個差不多。


但是我更覺得,大家思想上都有一個抽象的概念,就是分層是垂直的,從上到下的。其實,我覺得,更準確的說,這個分層應該是水平的,從左到右的,就像車間的生產線,進去一個大的需要處理的原料,經過不同的操作檯,一層一層的切割,包裝,到最後出來的時候就成為了很多精緻的小產品。

關於網路層。

網路層有不同的協議,如IPICMP,兩者的不同就是對於上層傳過來的資料根據什麼樣的格式進行切割,然後再次封裝時候遵循的準則不同。

ICMPPing命令經常用到的協議。Ping命令不是什麼特別神祕的東西,是一個程式設計師編寫的一個exe應用程式,你的電腦控制檯之所有能夠使用這個程式,是因為你的電腦上安裝了這個exe,而且在path裡邊設定了這個程式的路徑。ICMP全稱是報文控制協議。通過上邊的圖片可以看出,應用層的Ping工具,使用Ping協議,直接跳過運輸層,呼叫了網路層的ICMP協議。ICMP資料包裡邊內容,都是關於目的主機的一些資訊,因此可以用於遠端判斷一臺主機是否存在於網路上。ping程式是對兩個系統連通性進行測試的基本工具。它只利用ICMP回顯請求和回顯應答報文,而不用經過傳輸層TCP/UDP。Ping伺服器一般在核心中實現ICMP的功能。

網路上一臺主機的可達性不僅僅取決於IP層是否可達,還要取決於使用何種協議以及埠號。就比如說,一臺主機確實存在於網際網路上邊,而且一臺Client向這臺主機使用Ping工具發起ICMP協議包,這些資料包也準確到達了主機。主機在接收到這些資料包之後,從鏈路層傳到網路層一層層拆去包裝進行解析,但是主機的作業系統從網路層再往上解析的時候,發現了Ping的埠為6666(假設該主機封閉了該埠),就不會做出反應,而且默默的把這些資料吞了。那麼在Client看來,發出去的資料包失聯了,會認為這個主機找不到。

所以,總結一下Ping不同可能的原因:主機不線上,比如說關機了或者拔掉網線了。還有就是網路防火牆或者IP策略,會對ICMP報文進行過濾,ping命令無法迴應,還有就是主機本身的一些策略,會過濾掉ICMP資料包。

(個人感覺作業系統以及網絡卡是這樣工作的,所有的網路資料都是從一個入口進來的,進來之後作業系統與網絡卡相關的部件就開始從最底層開始解析這些二進位制的資料包,一層層的拆包,組裝,然後分析,直到IP層的時候,會對IP資料包進行分析,然後進行TCP層的分析,這時候就發現了埠號這個概念,那麼會根據埠號的不同,把這些資料儲存在不同的緩衝區域,每個緩衝區域屬於一個指定的應用程式(以埠號作為標識)。最終應用程式會從自己的緩衝區域來進行網路資料的讀取。)

關於TCP的通訊機制。

當TCP發出一個段後,它啟動一個定時器,等待目的端確認收到這個報文段。如果不能及時收到一個確認,將重發這個報文段。TCP將保持它首部和資料的檢驗和。這是一個端到端的檢驗和,目的是檢測資料在傳輸過程中的任何變化。如果收到段的檢驗和有差錯,TCP將丟棄這個報文段和不確認收到此報文段(希望發端超時並重發)。既然TCP報文段作為IP資料報來傳輸,而IP資料報的到達可能會失序,因此TCP報文段的到達也可能會失序。如果必要, TCP將對收到的資料進行重新排序,將收到的資料以正確的順序交給應用層。

另外,TCP對位元組流的內容不作任何解釋。TCP不知道傳輸的資料位元組流是二進位制資料,還是ASCII字元、EBCDIC字元或者其他型別資料。對位元組流的解釋由TCP連線雙方的應用層解釋。這種對位元組流的處理方式與Unix作業系統對檔案的處理方式很相似。Unix的核心對一個應用讀或寫的內容不作任何解釋,而是交給應用程式處理。對Unix的核心來說,它無法區分一個二進位制檔案與一個文字檔案。

(這裡說一句題外話,就是ASCII碼與二進位制檔案的問題。最終儲存在計算機硬碟上的資料都是二進位制資料,那麼這個二進位制資料是怎麼來的,這是一個問題。就拿txt文字檔案來說,其儲存方式就是根據ASCII碼將文字內容轉換成相應的數字,然後用二進位制的形式儲存並且儲存。但是對於word等檔案來說,比較複雜,有專門的軟體比如說Office來處理,並且有一定的演算法來生成這些二進位制。所以這就是為什麼Word檔案必須要用Office軟體來開啟。Notepad是作業系統自帶的,如果用Notepad去開啟word ,那麼notepad就會根據ASCII碼的方式去解析,最終發現要麼無法解析出來字元,要麼解析出來的字元是亂碼。)


每個TCP段都包含源端和目的端的埠號,用於尋找發端和收端應用程序。這兩個值加上IP首部中的源端IP地址和目的端IP地址唯一確定一個TCP連線。一個IP地址和一個埠號也稱為一個插口socket.

既然一個TCP連線是全雙工(即資料在兩個方向上能同時傳遞),因此每個方向必須單獨地進行關閉。這原則就是當一方完成它的資料傳送任務後就能傳送一個FIN來終止這個方向連線。當一端收到一個FIN,它必須通知應用層另一端幾經終止了那個方向的資料傳送。傳送FIN通常是應用層進行關閉的結果。

與Telnet類似,FTP最早的設計是用於兩臺不同的主機,這兩個主機可能執行在不同的作業系統下、使用不同的檔案結構、並可能使用不同字符集。但不同的是,Telnet獲得異構性是強制兩端都採用同一個標準:使用7位元ASCII碼的NVT。而FTP是採用另一種方法來處理不同系統間的差異。FTP支援有限數量的檔案型別(A S C II,二進位制,等等)和檔案結構(面向位元組流或記錄)。

在一次HTTP請求中,form表單的資料與上傳的檔案資料有什麼不同?

表單資料是根據ASCII碼轉換成的二進位制,而上傳檔案的時候,就是直接讀取的計算機硬碟上的二進位制資料。比如說上傳一個Word檔案,伺服器端接收到的會是一大段二進位制資料。其實檔案在客戶端儲存的時候就是一大段二進位制碼,那麼這個二進位制碼是怎麼生成的?那麼就要問微軟的Office客戶端了,是它根據一定的方式生成的二進位制碼然後存在了硬碟上。所以,這就是為什麼,一個exe生成的檔案另外的exe打不開,因為使用的解碼方式不一樣,不知道怎麼去分析這麼一大堆的二進位制碼,然後生成需要字串展現給使用者。

埠號,不是說一個真正存在的實體,或者說在網絡卡上有個埠啥的。其實埠號就是一個簡單的數字標識,用於區分不同的應用程式,有點類似於應用程式的ID,因為網路資料到達了一個主機上邊,怎麼知道這個資料是給哪個應用程式的呢,這時候埠號就起作用了。前面已經指出過, TCP和UDP採用16bit的埠號來識別應用程式。那麼這些埠號是如何選擇的呢?伺服器一般都是通過知名埠號來識別的。例如,對於每個TCP/IP實現來說,FTP伺服器的TCP埠號都是2 1,每個Telnet伺服器的TCP埠號都是2 3,每個TFTP (簡單檔案傳送)伺服器的UDP埠號都是69。

客戶端通常對它所使用的埠號並不關心,只需保證該埠號在本機上是唯一的就可以了。客戶埠號又稱作臨時埠號(即存在時間很短暫)。這是因為它通常只是在使用者執行該客戶程式時才存在,而伺服器則只要主機開著的,其服務就執行。

網路層( IP)提供點到點的服務,而運輸層( T C P和U D P)提供端到端的服務。

在TCP/IP協議族中,網路層IP提供的是一種不可靠的服務。也就是說,它只是儘可能快地把分組從源結點送到目的結點,但是並不提供任何可靠性保證。而另一方面, TCP在不可靠的IP層上提供了一個可靠的運輸層。為了提供這種可靠的服務, TCP採用了超時重傳、傳送和接收端到端的確認分組等機制。由此可見,運輸層和網路層分別負責不同的功能。

以前一直搞不懂,為什麼IP層是不可靠的,而TCP是建立在IP的基礎上的,卻是可靠的呢?因為做了一些冗餘的操作來保證可靠。Telnet和Rlogin這兩個互動應用要求最小的傳輸時延,因為人們主要用它們來傳輸少量的互動資料。另一方面,FTP檔案傳輸則要求有最大的吞吐量。

同一個HTML頁面,從伺服器端傳送到客戶端瀏覽器,首先是根據HTTP協議,組裝字串,組裝成一次請求回覆,這個回覆的字串包括headerbody等。然後這個字串會被轉成二進位制資料,然後給TCP層去分解,然後TCP層交給IP層,拆解成多個IP資料包。這時候這些包是無序的,不一定哪個包先到達。最終這些包再組成檔案,如img,css,js檔案。這就是為什麼圖片渲染出來的順序不一樣。

IP層的下一層是資料鏈路層,我們也可以理解為乙太網層或者令牌網。當一臺主機把乙太網資料幀傳送到位於同一區域網上的另一臺主機時,是根據48bit的乙太網地址來確定目的介面的。裝置驅動程式從不檢查IP資料報中的目的IP地址。ARP為IP地址到對應的硬體地址之間提供動態對映。我們之所以用動態這個詞是因為這個過程是自動完成的,一般應用程式使用者或系統管理員不必關心。

在硬體層次上進行的資料幀交換必須有正確的介面地址。但是,TCP/IP有自己的地址:32 bit的IP地址。知道主機的IP地址並不能讓核心傳送一幀資料給主機。核心(如乙太網驅動程式)必須知道目的端的硬體地址才能傳送資料。ARP的功能是在32bit的IP地址和採用不同網路技術的硬體地址之間提供動態對映。

獲取字串的ASCII

string A = "Hello World";

byte[] data = Encoding.ASCII.GetBytes(A);

一次Http請求,會建立一個TCP連線,然後將內容切割,分組打包,最後傳送到伺服器。

以前有個疑問,就是總覺得進行TCP通訊的AB之間有個管道。如果A在發訊息的時候,B也傳送訊息,那麼內容在管道之中不就衝突了麼。但是這種想法是錯誤的。AB之間根本沒有管道,是通過IP層這種路由方式來進行資料包的轉換的,傳送方與接收方根本都沒有指定的路線。傳送與接收都是在不同的緩衝區,一般發訊息的一方會在傳送的內容中新增一個識別符號,告訴接收方這次這一批的資料傳送完了,你去處理吧,處理完了給我個回覆。

當我們寫程式碼的時候,有個讀取網路資料的read方法,以前我一直以為是去網路上都資料。這是錯誤的,這個read呢,就是去從緩衝區讀取已經被作業系統或者網絡卡拆箱並且還原了的資料,把這個資料讀取到程式的記憶體中。

為什麼TCP建立連線會花費開銷?

這裡並不是說要佔用很多的網際網路上的頻寬,這裡的花銷主要是指電腦上的資源消耗。建立TCP連線的時候,電腦要做很多的準備工作,建立相應的緩衝區域,根據埠號建立儲存區域,還有就是IP是不可靠的,TCP要想辦法找出空間來儲存一些額外的東西來保證可靠性,這都是開銷。

還是那句話,建立TCP通道,其實根本沒有通道,走的是IP路由,建立通道主要是在電腦記憶體上開闢出相應的空間。TCP連線一直存在,說明那塊相應的快取區域一直沒有被回收。

AB之間是怎麼建立起TCP連線的?

這個就涉及到了3次握手機制。因為B機器上有程式在時刻監視著所有的IP資料包,一旦檢測到資料包中含有3次握手的內容,便會開啟一個連線,然後通過身份驗證等機制,最終建立起TCP連線。