1. 程式人生 > 實用技巧 >【linux】系統程式設計-7-網路程式設計

【linux】系統程式設計-7-網路程式設計

目錄


前言

原文

10. 網路程式設計

  • 網際網路通訊所要遵守的眾多協議,被統稱為TCP/IP。

10.1 簡要網路知識

  • TCP/IP是一個龐大的協議族,它是眾多網路協議的集合,包括:ARP、IP、ICMP、UDP、TCP、DNS、DHCP、HTTP、FTP、MQTT等等
  • 分層
graph LR A[應用層] --> a[DNS HTTP FTP SMTP MQTT郵件協議] B[傳輸層] --> b[主要是TCP UDP] C[網路層] --> c[主要為IP ICM ARP協議] D[鏈路層] --> d[MAC層] E[物理層] --> e[主要定義物理傳輸介質]
  • 參考圖

10.2 IP協議

  • IP協議(Internet Protocol),又稱之為網際協議,IP協議處於IP層工作,它是整個TCP/IP協議棧的核心協議

10.2.1 IP地址編址

  • 參考圖
類別 第一位元組(二進位制) 第一位元組取值範圍 網路號個數 主機號個數 適用範圍
A類 0XXX XXXX 0-127 125 16777214 大型網路
B類 10XX XXXX 128-191 16368 65534 中型網路
C類 110X XXXX 192-223 209715 254 小型網路
D類 1110 XXXX 224-239 - - 多播
E類 1111 XXXX 240-255 - - 保留

10.2.2 特殊IP地址

10.2.1 首限廣播地址
  • 受限廣播地址用於定義整個網際網路,如果裝置想使IP資料報被整個網路所接收,就傳送這個目的地址全為1的廣播包,但這樣會給整個網際網路帶來災難性的負擔,所以在任何情況下,路由器都會禁止轉發目的地址為255.255.255.255的廣播資料包,因此這樣的資料包僅會出現在本地網路中(區域網),255.255.255.255這個地址指本網段內的所有主機, 相當於“房子裡面的人都聽著”通知所有主機。
  • 其實就是對整個IP生效,即網路號+主機號都全為1,對整個網際網路生效
10.2.2 直接廣播地址
  • 直接廣播地址僅是主機號為1,廣播地址代表本網路內的所有主機。
  • 其實就是對整個IP生效,即主機號都全為1,對同一網路號內的所有主機生效
10.2.3 多播地址
  • 多播地址用在一對多的通訊中,屬於分類編址中的D類地址, D類地址只能用作目的地址,而不能作為主機中的源地址。
10.2.4 迴環地址
  • 127網段的所有地址都稱為環回地址,主要用來測試網路協議是否工作正常的作用。比如在電腦中使用ping 命令去ping 127.1.1.1就可以測試本地TCP/IP協議是否正常。用通俗的話表示,就是“我自己”,不能以127網段中的IP地址作為主機地址,因此A類地址又少了一個可用網路號。
10.2.5 本網路本主機
  • IP地址32bit全為0的地址(0.0.0.0)表示的是本網路本主機,這個IP地址在IP資料報中只能用作源IP地址,這發生在當裝置啟動時但又不知道自己的IP地址情況下。
  • 在使用DHCP分配IP地址的網路環境中,這樣的地址是很常見的,主機為了獲得一個可用的IP地址,就給DHCP伺服器傳送IP資料報,並用這樣的地址(0.0.0.0)作為源地址,目的地址為255.255.255.255(因為主機這時還不知道DHCP伺服器的IP地址),然後DHCP伺服器就會知道這個主機暫時沒有IP地址,那麼就會分配一個IP給這個主機。

10.3 UDP協議

  • UDP 是User Datagram Protocol的簡稱, 中文名是使用者資料報協議
  • 特點
    • 無連線,即通訊時不需要建立連線(傳送資料結束時也沒有連線可以釋放)所以減小了開銷和傳送資料前的時延;
    • 不可靠,最大努力交付,不保證可靠交付,因此主機不需要維護複雜的連線狀態;
    • 面向報文,只在應用層交下來的報文前增加了首部後就向下交付IP層;
    • 無流量控制和無阻塞控制,即使網路中存在阻塞,也不會影響傳送端的傳送頻率;
    • 支援一對一、一對多、多對一、多對多的互動通訊;
    • 首部開銷小,只有8個位元組,它比TCP的20個位元組的首部要短;
    • 速度快,UDP沒有TCP的握手、確認、視窗、重傳、擁塞控制等機制,UDP是一個無狀態的傳輸協議,所以它在傳遞資料時非常快,即使在網路擁塞的時候UDP也不會降低傳送的資料。
  • 應用環境
    • 常用於實時視訊的傳輸,比如直播、網路電話等,因為即使是出現了資料丟失的情況,導致視訊卡幀,是可以容忍的

10.4 TCP協議

  • TCP是提供一種面向連線、可靠的位元組流傳輸服務
  • 與UDP的區別
    • TCP面向連線、資料可靠。
    • UDP運載的資料是以報文的形式,各個報文在網路中互不相干傳輸,到達目標主機的順序是不一樣的,所以需要在應用層進行重灌。
    • TCP採用資料流的形式傳輸,TCP協議會給每個傳輸的位元組進行編號,在傳輸的過程中,傳送方把資料的起始編號與長度放在TCP報文中,在接收方將所有資料按照編號組裝起來,然後返回一個確認,當所有資料接收完成後才將資料遞交到應用層中。
  • TCP的特性
    • 連線機制:TCP是一個面向連線的協議
    • 確認與重傳:一個完整的TCP傳輸必須有資料的互動。傳送方傳送資料後必須等待接收方的結果反饋,並開啟定時器,超時未接收到資料則認為傳送失敗,進行重發操作;接收方接收到資料後必須向傳送方反饋結果。
    • 緩衝機制
      • 傳送方:應用程式的資料大小、型別都是不可預估的。在資料量很小時,TCP會將資料儲存在一個緩衝空間中,等到資料量足夠大的時候進行傳送資料,直至確認接收方正確接收成功才刪除。
      • 接收方:由於網路中傳輸的資料報到達接收方的時間是不一樣的,所以需要存起來進行重灌再交給應用層。
    • 全雙工通訊:TCP是全雙工通訊。雙方都是主機,任意一方都可以斷開連結。
    • 流量控制
      • 流量控制是一個速度匹配服務,即傳送方的傳送速率與接收方應用程式的讀取速率相匹配。
      • TCP通過讓傳送方維護一個稱為接收視窗(receive window)的變數來提供流量控制。
      • 接收視窗(rwnd),接收方會將此視窗值放在 TCP 報文的首部中的視窗欄位,然後傳遞給傳送方,這個視窗的大小是在傳送資料的時候動態調整的。
      • 若傳送方收到的視窗值為0,此時傳送方還是會進行傳送只有一個位元組的報文段。
    • 差錯控制:TCP協議採用校驗和的方式來檢驗資料有效性。同時,接收方也會把接收到的資料報進行整理,重灌。
    • 擁塞控制:在網路中擁塞的情況下調整自身的傳送速度,這種形式對傳送方的控制被稱為擁塞控制(congestion control),採取的措施是限制傳送方的傳送速度。

10.5 埠號的概念

  • TCP連線是兩個不同主機的應用連線,而傳輸層與上層協議是通過埠號進行識別的。
  • 埠號的取值範圍為:0~65535,不同埠號對應上層應用的不同執行緒
  • 一臺主機可能只有一個IP地址,但是可以有多個埠號
  • 通過 IP地址+埠號 來區分主機不同的執行緒。
  • 常見的TCP協議埠號有21、53、80等等
埠號 協議 說明
20/21 FTP 檔案傳輸協議,使得主機間可以共享檔案。
23 Telnet 終端遠端登入,它為使用者提供了在本地計算機上完成遠端主機工作的能力。
25 SMTP 簡單郵件傳輸協議,它幫助每臺計算機在傳送或中轉信件時找到下一個目的地。
69 TFTP 普通檔案傳輸協議。
80 HTTP 超文字傳輸協議,通過使用網頁瀏覽器、網路爬蟲或者其它的工具,客戶端發起一個HTTP請求到伺服器上指定埠(預設埠為80),應答的伺服器上儲存著一些資源,比如HTML檔案和影象,那麼就會返回這些資料到客戶端。
110 POP3 郵局協議版本3,本協議主要用於支援使用客戶端遠端管理在伺服器上的電子郵件。

10.6 TCP報文段

  • TCP報文段由 首部 + 資料域 組成

10.7 TCP建立連線

  • TCP是一個面向連線的協議,連線俗稱握手。
  • 三次握手建立連線
    • 建立連線的過程是由客戶端發起,服務端等待客戶請求
    • 第一步:客戶端向伺服器端傳送一個SYN報文段(只有首部,且SYN被置 1),初始序號(ISN)隨機選擇,假設為A,ACK置 0。
    • 第二步:伺服器端收到SYN報文段,便知道客戶端需要請求握手,從SYN報文段中提取對應的資訊,為該TCP連線分配TCP快取和變數,並向該客戶TCP傳送允許連線的報文段(握手應答報文)。這個報文段只有首部,包含3個重要的資訊:(建立客戶端-->服務端的連線
      1. SYN與ACK標誌位1
      2. 將TCP報文段首部的確認序號欄位設定為A+1(這個A(ISN)是從握手請求報文中得到)。
      3. 伺服器隨機選擇自己的初始序號(ISN,注意此ISN是伺服器端的ISN,假設為B),並將其放置到TCP報文段首部的序號欄位中。
    • 第三步:客戶端接收到伺服器端的握手應答後,會將SYN置 0,ACK置 1,確認序號置為 B+1, 設定視窗值,可以新增資料域的報文段發給伺服器端。同時給該TCP連線分配快取和變數。(建立服務端-->客戶端的連線
  • 參考圖:

10.8 TCP終止連線

  • 建立連線需要三次握手,而終止連線需要四次揮手。
  • 四次揮手終止連線
    • 第一步:客戶端發出FIN報文段(首部FIN被置 1),序號假設為C,ACK被置 1,但是確認序號是無效的。
    • 第二步:當伺服器端收到FIN報文段,它返回一個ACK報文段(終止連線應答),確認序號為C+1。此時斷開客戶端-->伺服器端的方向
    • 第三步:伺服器端發出FIN報文段向客戶端請求終止連線,此時序號為D,ACK被置 1,但是確認序號是無效的。
    • 第四步:當客戶端收到FIN報文段,它返回一個ACK報文段(終止連線應答),確認序號為D+1。此時斷開伺服器端-->客戶端的連線
  • 參考圖

10.9 TCP狀態

  • TCP協議的狀態如下:
    • CLOSED:初始狀態,表示TCP連線是“關閉著的”或“未開啟的”。
    • LISTEN:表示伺服器端的某個SOCKET處於監聽狀態,可以接受客戶端的連線。
    • SYN_RCVD:表示伺服器接收到了來自客戶端請求連線的SYN報文。在正常情況下,這個狀態是伺服器端的SOCKET在建立TCP連線時的三次握手會話過程中的一箇中間狀態,很短暫,基本上用netstat很難看到這種狀態,除非故意寫一個監測程式,將三次TCP握手過程中最後一個ACK報文不予傳送。當TCP連線處於此狀態時,再收到客戶端的ACK報文,它就會進入到ESTABLISHED狀態。
    • SYN_SENT:這個狀態與SYN_RCVD狀態相呼應,當客戶端SOCKET執行connect()進行連線時,它首先發送SYN報文,然後隨即進入到SYN_SENT 狀態,並等待服務端的傳送三次握手中的第2個報文。SYN_SENT狀態表示客戶端已傳送SYN報文。
    • ESTABLISHED:表示TCP連線已經成功建立。
    • FIN_WAIT_1:這個狀態得好好解釋一下,其實FIN_WAIT_1FIN_WAIT_2兩種狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是:FIN_WAIT_1狀態實際上是當SOCKETESTABLISHED狀態時,它想主動關閉連線,向對方傳送了FIN報文,此時該SOCKET進入到FIN_WAIT_1狀態。而當對方迴應ACK報文後,則進入到FIN_WAIT_2狀態。當然在實際的正常情況下,無論對方處於任何種情況下,都應該馬上回應ACK報文,所以FIN_WAIT_1狀態一般是比較難見到的,而FIN_WAIT_2狀態有時仍可以用netstat看到。
    • FIN_WAIT_2:上面已經解釋了這種狀態的由來,實際上FIN_WAIT_2狀態下的SOCKET表示半連線,即有一方呼叫close()主動要求關閉連線。注意:FIN_WAIT_2是沒有超時的(不像TIME_WAIT狀態),這種狀態下如果對方不關閉(不配合完成4次揮手過程),那這個FIN_WAIT_2 狀態將一直保持到系統重啟,越來越多的FIN_WAIT_2狀態會導致核心crash
    • TIME_WAIT:表示收到了對方的FIN報文,併發送出了ACK報文。TIME_WAIT狀態下的TCP連線會等待2*MSL(Max Segment Lifetime,最大分段生存期,指一個TCP報文在Internet上的最長生存時間。每個具體的TCP協議實現都必須選擇一個確定的MSL值,RFC 1122建議是2分鐘,但BSD傳統實現採用了30秒,Linux可以在cat/proc/sys/net/ipv4/tcp_fin_timeout看到本機的這個值),然後即可回到CLOSED可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。(這種情況應該就是四次揮手變成三次揮手的那種情況)
    • CLOSING:這種狀態在實際情況中應該很少見,屬於一種比較罕見的例外狀態。正常情況下,當一方傳送FIN報文後,按理來說是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示一方傳送FIN報文後,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼情況下會出現此種情況呢?那就是當雙方几乎在同時close()一個SOCKET的話,就出現了雙方同時傳送FIN報文的情況,這是就會出現CLOSING狀態,表示雙方都正在關閉SOCKET連線。
    • CLOSE_WAIT:表示正在等待關閉。怎麼理解呢?當對方close()一個SOCKET後傳送FIN報文給自己,你的系統毫無疑問地將會迴應一個ACK報文給對方,此時TCP連線則進入到CLOSE_WAIT狀態。接下來呢,你需要檢查自己是否還有資料要傳送給對方,如果沒有的話,那你也就可以close()這個SOCKET併發送FIN報文給對方,即關閉自己到對方這個方向的連線。有資料的話則看程式的策略,繼續傳送或丟棄。簡單地說,當你處於CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉連線。
    • LAST_ACK:當被動關閉的一方在傳送FIN報文後,等待對方的ACK報文的時候,就處於LAST_ACK狀態。當收到對方的ACK報文後,也就可以進入到CLOSED可用狀態了。

參考

* 野火