傳輸層協(xié)議:TCP和UDP
https://www.bilibili.com/video/BV1fv4y1Z7yS
https://www.bilibili.com/video/BV1Ez4y1r7JF
傳輸層:TCP和UDP
使用IPv4和IPv6的應用程序
平時我們使用的tcpdump
、ping
、traceroute
屬于TCP/IP協(xié)議族,雖然叫TCP/IP協(xié)議族,但是這個協(xié)議族還涉及到許多其他成員。下圖是其概貌。

tcpdump直接同數據鏈路層通信,使用BPF或DLPI的接口不用套接口或XTI。
tcpdump以外的9個應用程序通常是套接口或XTI。
traceroute程序使用兩種套接口:IP接口和ICMP套接口。
ICMP,網際控制消息協(xié)議。處理路由器和主機之間的錯誤和控制消息,ping程序使用ICMP。
ping和ICMP
下圖是ping百度主頁的結果:
ICMP沒有端口
說ping
一個端口,是不妥的。如果有人說ping
一下80端口通不通,其實指的是發(fā)送一個TCP請求探測一下80端口能不能回包。
真正的ping
使用的是ICMP,沒有端口一說。
無連接、不可靠的UDP
無連接
UDP客戶與服務器之間不必存在長期的關系。
一個UDP客戶可以通過同一個UDP套接口發(fā)送數據報給不同的服務器。
一個UDP服務器可以通過同一個UDP套接口從不同的客戶收數據報
不可靠
UDP本身不提供確認、序列號RTT估算、超時及重傳機制。
如果一個UDP數據報在網上被復制,那所有的拷貝都可能送到接收方的主機。
如果一個UDP客戶發(fā)送不同的數據報到同一個目的地,它們的順序可能被打亂。

使用UDP的服務
實時音視頻聊天、一些在線游戲等時間敏感的應用,適用于UDP。這些場景下,使用者可以忍受一定程度的數據丟包,但是不能容忍過多的延遲。
面向連接、可靠的TCP
面向連接
正如第一章的時間日期程序——“接受客戶連接,發(fā)送應答”步驟所說:
TCP連接使用三路握手(three-way handshake)來建立,當握手完畢時,accept函數返回,其返回值是一個稱為已連接(connected descriptor)的新描述字(connfd)。此描述字用于與新客戶的通信。accept為每個連接到服務器的客戶返回一個新的已連接描述字。
TCP連接使用三路握手(three-way handshake)來建立,當握手完畢時,accept函數返回,其返回值是一個稱為已連接(connected descriptor)的新描述字(connfd)。此描述字用于與新客戶的通信。accept為每個連接到服務器的客戶返回一個新的已連接描述字。
確認
TCP發(fā)送數據,有超時及重傳機制,數次重傳失敗后,TCP才放棄。這一點使得TCP比UDP可能消耗更多的時間。
序號
TCP根據數據分節(jié)的序列號,進行排序,去重,將完整的數據傳遞給應用進程。
流量控制
TCP有接收緩沖區(qū),緩沖區(qū)滿后,必須等到應用進程從緩沖區(qū)讀取數據后才能繼續(xù)接收新的數據。
UDP發(fā)送數據不管接收方的緩沖區(qū)是不是能裝下。
全雙工
最后,TCP的連接是全雙工的,意味著接收方也可以在下一時刻成為發(fā)送方,這一前提下,追蹤每個方向上數據流的狀態(tài)信息(序列號、通告窗口)大小,就顯得尤為重要了。
TCP連接的建立和終止
建立:三路握手
建立一個TCP連接的步驟:
被動打開 服務器通過調用
socket
、bind
、listen
,準備好接受外來的連接。主動打開 客戶端調用
connect
,發(fā)送一個SYN分節(jié),告知服務器建立連接后數據的初始序列號。服務器確認 服務器必須確認客戶端發(fā)來的SYN分節(jié),ACK=SYN+1,并且自己也發(fā)一個SYN分節(jié)給客戶端。
客戶端確認 客戶端必須確認服務器的SYN。

TCP選項
下面是第一個分節(jié)的抓包,包含了一些選項
MSS選項。TCP發(fā)送的SYN中帶上這個選項,通知對方它的最大分節(jié)大小,即它能接受的每個TCP分節(jié)中的最大數據量。
窗口規(guī)模選項。TCP雙方能夠通知對方的最大窗口大小是65535,因為TCP Header里,這個字段只占16位(2^16-1)。
時間戳選項。在高速連接中,一些由于暫時的路由的原因造成的迷途分組,在路由穩(wěn)定后,正常到達目的地,TS選項可以防止上述過程可能造成的數據損壞。
終止:四次揮手
終止一個TCP連接的步驟:
主動關閉 某個應用進程首先調用
close
,我們稱這一端執(zhí)行主動關閉,它發(fā)出第一個FIN分節(jié)。被動關閉
接收到FIN的端執(zhí)行被動關閉,確認對這個FIN的接收,這次接收意味著應用進程在相應連接上再也接收不到額外數據(它的接收會作為文件結束符發(fā)給被動方應用進程)。被動方發(fā)起關閉 一段時間后,收到文件結束符的應用進程也調用
close
關閉它的套接口,向主動方發(fā)送一個FIN。主動方確認 接收到FIN分節(jié)后,主動關閉的一方也要確認這個分節(jié)。

上圖演示了客戶端發(fā)起的主動關閉,實際上無論是客戶端還是服務器都可以執(zhí)行主動關閉。譬如HTTP(超文本傳送協(xié)議)就是服務器執(zhí)行主動關閉。
TCP狀態(tài)轉換圖
為每一個連接定義11種狀態(tài)。
TCP規(guī)則決定狀態(tài)的轉換條件,這種轉換基于當前狀態(tài)及在該狀態(tài)下所接收的分節(jié)。例如: 應用進程在
CLOSED
狀態(tài)下執(zhí)行一個主動打開:

在SYN_SENT
情況下收到附帶ACK的SYN:

應用進程調用close
主動關閉:

應用進程在ESTABLISHED
狀態(tài)下接收到FIN:


數據捎帶
在TCP的建立連接的三次握手和終止連接的四次揮手之間,是數據分節(jié)的傳輸。此時服務器對客戶請求的確認是伴隨著服務器的應答發(fā)送的。這稱為捎帶,通常在服務器處理請求并產生應答的時間少于200ms時發(fā)生。如果服務器耗用更長的時間,如1s,就會先確認,再應答。
TCP數據傳輸

TCP還是UDP
上圖這樣的單一分節(jié)的請求和接收,使用TCP時,包括連接建立和連接終止的7個分節(jié),以及最后一次客戶對服務器數據的應答,有8個分節(jié)額外需要消耗。如果使用UDP,只有2個分組需要交換。
許多應用程序還是在使用UDP,因為它們需交換的數據量很小,也避免TCP連接建立和終止連接的額外開銷。
TIME_WAIT狀態(tài)
執(zhí)行主動關閉的那端會在轉換成CLOSED
之前進入這個狀態(tài)。
停留在該狀態(tài)的持續(xù)時間是最長分節(jié)生命期
MSL
的兩倍,俗稱2MSL
。MSL
的值選擇在30s~ 2min之間,這意味著TIME_WAIT
狀態(tài)的延遲在1min~4min之間。MSL
是IP數據報能在互聯(lián)網中生存的最長時間。
存在TIME_WAIT狀態(tài)的兩個理由
實現(xiàn)終止TCP全雙工連接的可靠性
假設最后一個主動關閉方發(fā)給被動方的ACK
丟失,被動方就會重發(fā)最終的FIN
,因此主動一方必須維護狀態(tài)信息,以允許它重發(fā)對應的ACK
。為了實現(xiàn)全雙工關閉(兩個方向數據流都徹底關閉),TCP必須正確處理這四個分節(jié)中任何一個分節(jié)的丟失情況。允許老的重復分節(jié)在網絡中消逝
由于一個分節(jié)最多存在MSL
,那么TIME_WAIT
等待2個MSL
之后,便可以保證老的重復分節(jié)已經在網絡中徹底丟棄。這樣下一次在相同的IP地址和端口建立的連接,必然不會接收到老的請求分節(jié)了。
TCP并發(fā)服務器
并發(fā)服務器中,主服務器通過循環(huán)派生子進程來處理每個新的連接。

需要C/C++ Linux服務器架構師學習資料加群973961276獲?。ㄙY料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費分享