網絡編程懶人入門(十四):到底什麼是Socket?一文即懂!

本文由cxuan分享,原題“原來這纔是 Socket”,有修訂。

1、引言

本系列文章前面那些主要講解的是計算機網絡的理論基礎,但對於即時通訊IM這方面的應用層開發者來說,跟計算機網絡打道的其實是各種API接口。

本篇文章就來聊一下網絡應用程序員最熟悉的Socket這個東西,拋開生澀的計算機網絡理論,從應用層的角度來理解到底什麼是Socket。

對於 Socket 的認識,本文將從以下幾個方面着手介紹:

  • 1)Socket 是什麼;
  • 2)Socket 是如何創建的;
  • 3)Socket 是如何連接的;
  • 4)Socket 是如何收發數據的;
  • 5)Socket 是如何斷開連接的;
  • 6)Socket 套接字的刪除等。

特別說明:本文中提到的“Socket”、“網絡套接字”、“套接字”,如無特殊指明,指的都是同一個東西哦。

 

2、Socket 是什麼

一個數據包經由應用程序產生,進入到協議棧中進行各種報文頭的包裝,然後操作系統調用網卡驅動程序指揮硬件,把數據發送到對端主機。

整個過程的大體的圖示如下:

我們大家知道,協議棧其實是位於操作系統中的一些協議的堆疊,這些協議包括 TCP、UDP、ARP、ICMP、IP等。

通常某個協議的設計都是爲了解決特定問題的,比如:

  • 1)TCP 的設計就負責安全可靠的傳輸數據;
  • 2)UDP 設計就是報文小,傳輸效率高;
  • 3)ARP 的設計是能夠通過 IP 地址查詢物理(Mac)地址;
  • 4)ICMP 的設計目的是返回錯誤報文給主機;
  • 5)IP 設計的目的是爲了實現大規模主機的互聯互通。

應用程序比如瀏覽器、電子郵件、文件傳輸服務器等產生的數據,會通過傳輸層協議進行傳輸。而應用程序是不會和傳輸層直接建立聯繫的,而是有一個能夠連接應用層和傳輸層之間的套件,這個套件就是 Socket

在上面這幅圖中,應用程序包含 Socket 和解析器,解析器的作用就是向 DNS 服務器發起查詢,查詢目標 IP 地址(關於DNS請見《理論聯繫實際,全方位深入理解DNS)。

應用程序的下面:就是操作系統內部,操作系統內部包括協議棧,協議棧是一系列協議的堆疊。

操作系統下面:就是網卡驅動程序,網卡驅動程序負責控制網卡硬件,驅動程序驅動網卡硬件完成收發工作。

在操作系統內部有一塊用於存放控制信息的存儲空間,這塊存儲空間記錄了用於控制通信的控制信息。其實這些控制信息就是 Socket 的實體,或者說存放控制信息的內存空間就是Socket的實體。

這裏大家有可能不太清楚所以然,所以我用了一下 netstat 命令來給大夥看一下Socket是啥玩意。

我們在 Windows 的命令提示符中輸入:

netstat-ano

# netstat 用於顯示Socket內容 , -ano 是可選選項

# a 不僅顯示正在通信的Socket,還顯示包括尚未開始通信等狀態的所有Socket

# n 顯示 IP 地址和端口號

# o 顯示Socket的程序 PID

我的計算機會出現下面結果:

如上圖所示:

  • 1)每一行都相當於一個Socket;
  • 2)每一列也被稱爲一個元組。

所以,一個Socket就是五元組:

  • 1)協議;
  • 2)本地地址;
  • 3)外部地址;
  • 4)狀態;
  • 5)PID。

PS:有的時候也被叫做四元組,四元組不包括協議。

我們來解讀一下上圖中的數據,比如圖中的第一行:

1)它的協議就是 TCP,本地地址和遠程地址都是 0.0.0.0這表示通信還沒有開始,IP 地址暫時還未確定)。

2)而本地端口已知是 135,但是遠程端口還未知,此時的狀態是 LISTENING(LISTENING 表示應用程序已經打開,正在等待與遠程主機建立連接。關於各種狀態之間的轉換,大家可以閱讀《通俗易懂-深入理解TCP協議(上):理論基礎)。

3)最後一個元組是 PID,即進程標識符,PID 就像我們的身份證號碼,能夠精確定位唯一的進程。

3、Socket 是如何創建的

通過上節的講解,現在你可能對 Socket 有了一個基本的認識,先喝口水,休息一下,讓我們繼續探究 Socket。

現在我有個問題,Socket 是如何創建的呢?

Socket 是和應用程序一起創建的。

應用程序中有一個 socket 組件,在應用程序啓動時,會調用 socket 申請創建Socket,協議棧會根據應用程序的申請創建Socket:首先分配一個Socket所需的內存空間,這一步相當於是爲控制信息準備一個容器,但只有容器並沒有實際作用,所以你還需要向容器中放入控制信息;如果你不申請創建Socket所需要的內存空間,你創建的控制信息也沒有地方存放,所以分配內存空間,放入控制信息缺一不可。至此Socket的創建就已經完成了。

Socket創建完成後,會返回一個Socket描述符給應用程序,這個描述符相當於是區分不同Socket的號碼牌。根據這個描述符,應用程序在委託協議棧收發數據時就需要提供這個描述符。

4、Socket 是如何連接的

Socket創建完成後,最終還是爲數據收發服務的。但是,在數據收發之前,還需要進行一步“連接”(術語就是 connect),建立連接有一整套過程。

這個“連接”並不是真實的連接(用一根水管插在兩個電腦之間?不是你想的這樣。。。)。

實際上這個“連接”是應用程序通過 TCP/IP 協議標準從一個主機通過網絡介質傳輸到另一個主機的過程。

Socket剛剛創建完成後,還沒有數據,也不知道通信對象。

在這種狀態下:即使你讓客戶端應用程序委託協議棧發送數據,它也不知道發送到哪裏。所以瀏覽器需要根據網址來查詢服務器的 IP 地址(做這項工作的協議是 DNS),查詢到目標主機後,再把目標主機的 IP 告訴協議棧。至此,客戶端這邊就準備好了。

在服務器上:與客戶端一樣也需要創建Socket,但是同樣的它也不知道通信對象是誰,所以我們需要讓客戶端向服務器告知客戶端的必要信息:IP 地址和端口號

現在通信雙方建立連接的必要信息已經具備,可以開始“連接”過程了。

首先:客戶端應用程序需要調用 Socket 庫中的 connect 方法,提供 socket 描述符和服務器 IP 地址、端口號。

以下是connect的僞碼調用:

connect(<描述符>、<服務器IP地址和端口號>)

這些信息會傳遞給協議棧中的 TCP 模塊,TCP 模塊會對請求報文進行封裝,再傳遞給 IP 模塊,進行 IP 報文頭的封裝,然後傳遞給物理層,進行幀頭封裝。

之後通過網絡介質傳遞給服務器,服務器上會對幀頭、IP 模塊、TCP 模塊的報文頭進行解析,從而找到對應的Socket。

Socket收到請求後,會寫入相應的信息,並且把狀態改爲正在連接。

請求過程完成後:服務器的 TCP 模塊會返回響應,這個過程和客戶端是一樣的(如果大家不太清楚報文頭的封裝過程,可以閱讀《快速理解TCP協議一篇就夠)。

在一個完整的請求和響應過程中,控制信息起到非常關鍵的作用:

  • 1)SYN 就是同步的縮寫,客戶端會首先發送 SYN 數據包,請求服務端建立連接;
  • 2)ACK 就是相應的意思,它是對發送 SYN 數據包的響應;
  • 3)FIN 是終止的意思,它表示客戶端/服務器想要終止連接。

由於網絡環境的複雜多變,經常會存在數據包丟失的情況,所以雙方通信時需要相互確認對方的數據包是否已經到達,而判斷的標準就是 ACK 的值。

上面的文字不夠生動,動畫可以更好的說明這個過程:

PS:這個“連接”的詳細理論知識,可以閱讀《理論經典:TCP協議的3次握手與4次揮手過程詳解》、《跟着動畫來學TCP三次握手和四次揮手》,這裏不再贅述。

當所有建立連接的報文都能夠正常收發之後,此時套接字就已經進入可收發狀態了,此時可以認爲用一根管理把兩個套接字連接了起來。當然,實際上並不存在這個管子。建立連接之後,協議棧的連接操作就結束了,也就是說 connect 已經執行完畢,控制流程被交回給應用程序。

另外:如果你對Socket代碼更熟悉的話,可以先讀讀這篇《手把手教你寫基於TCP的Socket長連接》。

5、Socket 是如何收發數據的

當控制流程上節中的連接過程回到應用程序之後,接下來就會直接進入數據收發階段。

數據收發操作是從應用程序調用 write 將要發送的數據交給協議棧開始的,協議棧收到數據之後執行發送操作。

協議棧不會關心應用程序傳輸過來的是什麼數據,因爲這些數據最終都會轉換爲二進制序列,協議棧在收到數據之後並不會馬上把數據發送出去,而是會將數據放在發送緩衝區,再等待應用程序發送下一條數據。

爲什麼收到數據包不會直接發送出去,而是放在緩衝區中呢?

因爲只要一旦收到數據就會發送,就有可能發送大量的小數據包,導致網絡效率下降(所以協議棧需要將數據積攢到一定數量才能將其發送出去)。

至於協議棧會向緩衝區放多少數據,這個不同版本和種類的操作系統有不同的說法。

不過,所有的操作系統都會遵循下面這幾個標準:

1)第一個判斷要素:是每個網絡包能夠容納的數據長度,判斷的標準是 MTU,它表示的是一個網絡包的最大長度。最大長度包含頭部,所以如果單論數據區的話,就會用 MTU - 包頭長度,由此的出來的最大數據長度被稱爲 MSS。

 

 

2)另一個判斷標準:是時間,當應用程序產生的數據比較少,協議棧向緩衝區放置數據效率不高時,如果每次都等到 MSS 再發送的話,可能因爲等待時間太長造成延遲。在這種情況下,即使數據長度沒有到達 MSS,也應該把數據發送出去。

但協議棧並沒有告訴我們怎樣平衡這兩個因素,如果數據長度優先,那麼效率有可能比較低;如果時間優先,那又會降低網絡的效率。

經過了一段時間。。。。。。

假設我們使用的是長度有限法則:此時緩衝區已滿,協議棧要發送數據了,協議棧剛要把數據發送出去,卻發現無法一次性傳輸這麼大數據量(相對的)的數據,那怎麼辦呢?

在這種情況下,發送緩衝區中的數據就會超過 MSS 的長度,發送緩衝區中的數據會以 MSS 大小爲一個數據包進行拆分,拆分出來的每塊數據都會加上 TCP,IP,以太網頭部,然後被放進單獨的網絡包中。

到現在,網絡包已經準備好發往服務器了,但是數據發送操作還沒有結束,因爲服務器還未確認是否已經收到網絡包。因此在客戶端發送數據包之後,還需要服務器進行確認。

TCP 模塊在拆分數據時,會計算出網絡包偏移量,這個偏移量就是相對於數據從頭開始計算的第幾個字節,並將算好的字節數寫在 TCP 頭部,TCP 模塊還會生成一個網絡包的序號(SYN),這個序號是唯一的,這個序號就是用來讓服務器進行確認的。

服務器會對客戶端發送過來的數據包進行確認,確認無誤之後,服務器會生成一個序號和確認號(ACK)並一起發送給客戶端,客戶端確認之後再發送確認號給服務器。

我們來看一下實際的工作過程:

首先:客戶端在連接時需要計算出序號初始值,並將這個值發送給服務器。

接下來:服務器通過這個初始值計算出確認號並返回給客戶端(初始值在通信過程中有可能會丟棄,因此當服務器收到初始值後需要返回確認號用於確認)。

同時:服務器也需要計算出從服務器到客戶端方向的序號初始值,並將這個值發送給客戶端。然後,客戶端也需要根據服務器發來的初始值計算出確認號發送給服務器。

至此:連接建立完成,接下來就可以進入數據收發階段了。

數據收發階段中,通信雙方可以同時發送請求和響應,雙方也可以同時對請求進行確認。

請求 - 確認機制非常強大:通過這一機制,我們可以確認接收方有沒有收到某個包,如果沒有收到則重新發送,這樣一來,但凡網絡中出現的任何錯誤,我們都可以即使發現並補救。

上面的文字不夠生動,動畫可以更好的理解請求 - 確認機制:

網卡、集線器、路由器(見《史上最通俗的集線器、交換機、路由器功能原理入門)都沒有錯誤補救機制,一旦檢測到錯誤就會直接丟棄數據包,應用程序也沒有這種機制,起作用的只是 TCP/IP 模塊。

由於網絡環境複雜多變,所以數據包會存在丟失情況,因此發送序號和確認號也存在一定規則,TCP 會通過窗口管理確認號,我們這篇文章不再贅述,大家可以閱讀《通俗易懂-深入理解TCP協議(下):RTT、滑動窗口、擁塞處理》來尋找答案。

PS:另一篇《我們在讀寫Socket時,究竟在讀寫什麼?》中用動畫詳細說明了這個過程,有興趣可以讀一讀。

6、Socket 是如何斷開連接的

當通信雙方不再需要收發數據時,需要斷開連接。不同的應用程序斷開連接的時機不同。

以 Web 爲例:瀏覽器向 Web 服務器發送請求消息,Web 服務器再返回響應消息,這時收發數據就全部結束了,服務器可能會首先發起斷開響應,當然客戶端也有可能會首先發起(誰先斷開連接是應用程序做出的判斷),與協議棧無關。

無論哪一方發起斷開連接的請求,都會調用 Socket 庫的 close 程序。

我們以服務器斷開連接爲例:服務器發起斷開連接請求,協議棧會生成斷開連接的 TCP 頭部,其實就是設置 FIN 位,然後委託 IP 模塊向客戶端發送數據,與此同時,服務器的Socket會記錄下斷開連接的相關信息。

收到服務器發來 FIN 請求後:客戶端協議棧會將Socket標記爲斷開連接狀態,然後,客戶端會向服務器返回一個確認號,這是斷開連接的第一步,在這一步之後,應用程序還會調用 read 來讀取數據。等到服務器數據發送完成後,協議棧會通知客戶端應用程序數據已經接收完畢。

只要收到服務器返回的所有數據,客戶端就會調用 close 程序來結束收發操作,這時客戶端會生成一個 FIN 發送給服務器,一段時間後服務器返回 ACK 號。至此,客戶端和服務器的通信就結束了。

上面的文字不夠生動,動畫可以更好的說明這個過程:

PS:斷開連接的詳細理論知識,可以閱讀《理論經典:TCP協議的3次握手與4次揮手過程詳解》、《跟着動畫來學TCP三次握手和四次揮手》,這裏不再贅述。

7、Socket的刪除

上述通信過程完成後,用來通信的Socket就不再會使用了,此時我們就可以刪除這個Socket了。

不過,這時候Socket不會馬上刪除,而是等過一段時間再刪除。

等待這段時間是爲了防止誤操作,最常見的誤操作就是客戶端返回的確認號丟失,至於等待多長時間,和數據包重傳的方式有關,這裏我們就深入展開討論了。

關於Socket操作的全過程,如果從系統的角度來看,可能會更深入一些,建議可以深入閱讀張彥飛的《深入操作系統,從內核理解網絡包的接收過程(Linux篇)》一文。

8、參考資料

[1] TCP/IP詳解 - 第17章·TCP:傳輸控制協議

[2] TCP/IP詳解 - 第18章·TCP連接的建立與終止

[3] TCP/IP詳解 - 第21章·TCP的超時與重傳

[4] 快速理解網絡通信協議(上篇)

[5] 快速理解網絡通信協議(下篇)

[6] 面視必備,史上最通俗計算機網絡分層詳解

[7] 假如你來設計網絡,會怎麼做?

[8] 假如你來設計TCP協議,會怎麼做?

[10] 淺析TCP協議中的疑難雜症(下篇)

[11] 關閉TCP連接時爲什麼會TIME_WAIT、CLOSE_WAIT

[12] 從底層入手,深度分析TCP連接耗時的祕密

學習交流:

- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK 

本文已同步發佈於: http://www.52im.net/thread-3821-1-1.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章