1.計算機網路三要素
網路程式設計:使用程式語言實現多臺計算機的通訊
網路程式設計三大要素
- ip地址:網路中每一臺計算機的唯一標識,透過IP地址找到指定的計算機
- 埠:用於標識程序的邏輯地址,透過埠找到指定程序。
- 協議:定義通訊規則,符合協議則可以通訊,不符合不能通訊,一般有TCP協議和UDP協議。
1.1 TCP協議
TCP(Transmission Control Protocol,傳輸控制協議)是一種面向連線的、可靠的、基於位元組流的通訊協議,資料在傳輸前要建立連線,傳輸完畢後還要斷開連線。客戶端在收發資料前要使用connect()函式和伺服器建立連線,建立連線的目的是保證IP地址、埠、物理鏈路等正確無誤,為資料的傳輸開闢通道。
TCP資料包機構
帶陰影的幾個欄位需要重點說明一下:
- 序號:Seq(Sequence Number)序號佔32位,用來標識從計算機A傳送到計算機B的資料包的序號,計算機傳送資料時對此進行標記。
- 確認號:Ack(Acknowledge Number)確認號佔32位,客戶端和伺服器端都可以傳送,Ack = Seq + 1。
- 標誌位:每個標誌位佔用1Bit,共有6個,分別為 URG、ACK、PSH、RST、SYN、FIN,具體含義如下:
URG:緊急指標(urgent pointer)有效。
ACK:確認序號有效。
PSH:接收方應該儘快將這個報文交給應用層。
RST:重置連線。
SYN:建立一個新連線。
FIN:斷開一個連線。
連線的建立(三次握手
使用 connect() 建立連線時,客戶端和伺服器端會相互傳送三個資料包,請看下圖:
客戶端呼叫 socket() 建立套接字後,因為沒有建立連線,所以套接字處於CLOSED狀態;伺服器端呼叫 listen() 函式後,套接字進入LISTEN狀態,開始監聽客戶端請求。
這個時候,客戶端開始發起請求:
- 當客戶端呼叫 connect() 函式後,TCP協議會組建一個資料包,並設定 SYN 標誌位,表示該資料包是用來建立同步連線的。同時生成一個隨機數字 1000,填充“序號(Seq)”欄位,表示該資料包的序號。完成這些工作,開始向伺服器端傳送資料包,客戶端就進入了SYN-SEND狀態。
- 伺服器端收到資料包,檢測到已經設定了 SYN 標誌位,就知道這是客戶端發來的建立連線的“請求包”。伺服器端也會組建一個資料包,並設定 SYN 和 ACK 標誌位,SYN 表示該資料包用來建立連線,ACK 用來確認收到了剛才客戶端傳送的資料包。 伺服器生成一個隨機數 2000,填充“序號(Seq)”欄位。2000 和客戶端資料包沒有關係。伺服器將客戶端資料包序號(1000)加1,得到1001,並用這個數字填充“確認號(Ack)”欄位。伺服器將資料包發出,進入SYN-RECV狀態。
- 客戶端收到資料包,檢測到已經設定了 SYN 和 ACK 標誌位,就知道這是伺服器發來的“確認包”。客戶端會檢測“確認號(Ack)”欄位,看它的值是否為 1000+1,如果是就說明連線建立成功。
接下來,客戶端會繼續組建資料包,並設定 ACK 標誌位,表示客戶端正確接收了伺服器發來的“確認包”。同時,將剛才伺服器發來的資料包序號(2000)加1,得到 2001,並用這個數字來填充“確認號(Ack)”欄位。客戶端將資料包發出,進入ESTABLISED狀態,表示連線已經成功建立。
- 伺服器端收到資料包,檢測到已經設定了 ACK 標誌位,就知道這是客戶端發來的“確認包”。伺服器會檢測“確認號(Ack)”欄位,看它的值是否為 2000+1,如果是就說明連線建立成功,伺服器進入ESTABLISED狀態。至此,客戶端和伺服器都進入了ESTABLISED狀態,連線建立成功,接下來就可以收發資料了。
注意:
三次握手的關鍵是要確認對方收到了自己的資料包,這個目標就是透過“確認號(Ack)”欄位實現的。計算機會記錄下自己傳送的資料包序號 Seq,待收到對方的資料包後,檢測“確認號(Ack)”欄位,看Ack = Seq + 1是否成立,如果成立說明對方正確收到了自己的資料包。
斷開連線(四次揮手)
建立連線非常重要,它是資料正確傳輸的前提;斷開連線同樣重要,它讓計算機釋放不再使用的資源。如果連線不能正常斷開,不僅會造成資料傳輸錯誤,還會導致套接字不能關閉,持續佔用資源,如果併發量高,伺服器壓力堪憂。
.建立連線需要三次握手,斷開連線需要四次握手,可以形象的比喻為下面的對話:
[Shake 1] 套接字A:“任務處理完畢,我希望斷開連線。”
[Shake 2] 套接字B:“哦,是嗎?請稍等,我準備一下。”等待片刻後……
[Shake 3] 套接字B:“我準備好了,可以斷開連線了。”
[Shake 4] 套接字A:“好的,謝謝合作。”
下圖演示了客戶端主動斷開連線的場景:
建立連線後,客戶端和伺服器都處於ESTABLISED狀態。這時,客戶端發起斷開連線的請求:
- 客戶端呼叫 close() 函式後,向伺服器傳送 FIN 資料包,進入FIN_WAIT_1狀態。FIN 是 Finish 的縮寫,表示完成任務需要斷開連線。
- 伺服器收到資料包後,檢測到設定了 FIN 標誌位,知道要斷開連線,於是向客戶端傳送“確認包”,進入CLOSE_WAIT狀態。
注意:伺服器收到請求後並不是立即斷開連線,而是先向客戶端傳送“確認包”,告訴它我知道了,我需要準備一下才能斷開連線。
- 客戶端收到“確認包”後進入FIN_WAIT_2狀態,等待伺服器準備完畢後再次傳送資料包。
- 等待片刻後,伺服器準備完畢,可以斷開連線,於是再主動向客戶端傳送 FIN 包,告訴它我準備好了,斷開連線吧。然後進入LAST_ACK狀態。
- 客戶端收到伺服器的 FIN 包後,再向伺服器傳送 ACK 包,告訴它你斷開連線吧。然後進入TIME_WAIT狀態。
- 伺服器收到客戶端的 ACK 包後,就斷開連線,關閉套接字,進入CLOSED狀態。
注意:關於 TIME_WAIT 狀態的說明
客戶端最後一次傳送 ACK包後進入 TIME_WAIT 狀態,而不是直接進入 CLOSED 狀態關閉連線,這是為什麼呢?
TCP 是面向連線的傳輸方式,必須保證資料能夠正確到達目標機器,不能丟失或出錯,而網路是不穩定的,隨時可能會毀壞資料,所以機器A每次向機器B傳送資料包後,都要求機器B”確認“,回傳ACK包,告訴機器A我收到了,這樣機器A才能知道資料傳送成功了。如果機器B沒有回傳ACK包,機器A會重新傳送,直到機器B回傳ACK包。客戶端最後一次向伺服器回傳ACK包時,有可能會因為網路問題導致伺服器收不到,伺服器會再次傳送 FIN 包,如果這時客戶端完全關閉了連線,那麼伺服器無論如何也收不到ACK包了,所以客戶端需要等待片刻、確認對方收到ACK包後才能進入CLOSED狀態。那麼,要等待多久呢?資料包在網路中是有生存時間的,超過這個時間還未到達目標主機就會被丟棄,並通知源主機。這稱為報文最大生存時間(MSL,Maximum Segment Lifetime)。TIME_WAIT 要等待 2MSL 才會進入 CLOSED 狀態。ACK 包到達伺服器需要 MSL 時間,伺服器重傳 FIN 包也需要 MSL 時間,2MSL 是資料包往返的最大時間,如果 2MSL 後還未收到伺服器重傳的 FIN 包,就說明伺服器已經收到了 ACK 包。
1.2、UDP協議
UDP(User Datagram Protocol, 使用者資料包協議)是一種無連線的傳輸層協議,提供面向事務的簡單不可靠資訊傳送服務,可以保證通訊效率,傳輸延時小。例如影片聊天應用中用的就是UDP協議,這樣可以保證及時丟失少量資料,影片的顯示也不受很大影響。