TCP是面向連接的、可靠的、基于字節流的傳輸層通信協議:
面向連接:一定是一對一才能連接,不能像UDP協議可以一個主機同時向多個主機發送消息,即一對多是無法做到的
可靠的:無論的網絡鏈路中出現了怎樣的鏈路變化,TCP 都可以保證一個報文一定能夠到達接收端
字節流:消息是沒有邊界的,所以無論消息有多大都可以進行傳輸。并且消息是有序的,當前一個消息沒有收到的時候,即使它先收到了后面的字節已經收到,那么也不能扔給應用層去處理,同時對重復的報文會自動丟棄
TCP頭部格式
序列號:在建立連接時由計算機生成的隨機數作為其初始值,通過 SYN 包傳給接收端主機,每發送一次數據,就「累加」一次該「數據字節數」的大小。用來解決網絡包亂序問題
確認應答號:指下一次「期望」收到的數據的序列號,發送端收到這個確認應答以后可以認為在這個序號以前的數據都已經被正常接收。用來解決不丟包的問題
控制位:ACK:該位為 1 時,「確認應答」的字段變為有效,TCP 規定除了最初建立連接時的 SYN 包之外該位必須設置為 1RST:該位為 1 時,表示 TCP 連接中出現異常必須強制斷開連接SYN:該位為 1 時,表示希望建立連接,并在其「序列號」的字段進行序列號初始值的設定FIN:該位為 1 時,表示今后不會再有數據發送,希望斷開連接。當通信結束希望斷開連接時,通信雙方的主機之間就可以相互交換 FIN 位置為 1 的 TCP 段
網絡模型
談一談你對 TCP/IP 四層模型,OSI 七層模型的理解?
OSI參考模型
OSI(Open System Interconnect),即開放式系統互聯。一般都叫OSI參考模型,是ISO(國際標準化組織)組織在1985年研究的網絡互連模型。ISO為了更好的使網絡應用更為普及,推出了OSI參考模型。其含義就是推薦所有公司使用這個規范來控制網絡。這樣所有公司都有相同的規范,就能互聯了。
TCP/IP五層模型
TCP/IP五層協議和OSI的七層協議對應關系如下:
TCP狀態
CLOSED:表示初始狀態
LISTEN:表示服務器端的某個SOCKET處于監聽狀態,可以接受連接了
SYN_RCVD:表示接收到了SYN報文
SYN_SENT:表示客戶端已發送SYN報文
ESTABLISHED:表示連接已經建立了
TIME_WAIT:表示收到了對方的FIN報文,并發送出了ACK報文,就等2MSL后即可回到CLOSED可用狀態了
CLOSING:表示你發送FIN報文后,并沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。如果雙方幾乎在同時close一個SOCKET的話,那么就出現了雙方同時發送FIN報 文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET連接
CLOSE_WAIT:表示在等待關閉
如何在 Linux 系統中查看 TCP 狀態?
TCP 的連接狀態查看,在 Linux 可以通過 netstat -napt 命令查看:
TIME_WAIT
① 為什么需要 TIME_WAIT 狀態?
主動發起關閉連接的一方,才會有 TIME-WAIT 狀態。需要 TIME-WAIT 狀態,主要是兩個原因:
防止具有相同「四元組」的「舊」數據包被收到
保證「被動關閉連接」的一方能被正確的關閉,即保證最后的 ACK 能讓被動關閉方接收,從而幫助其正常關閉
② TIME_WAIT 過多有什么危害?
如果服務器有處于 TIME-WAIT 狀態的 TCP,則說明是由服務器方主動發起的斷開請求。過多的 TIME-WAIT 狀態主要的危害有兩種:
第一是內存資源占用
第二是對端口資源的占用,一個 TCP 連接至少消耗一個本地端口
第二個危害是會造成嚴重的后果的,要知道,端口資源也是有限的,一般可以開啟的端口為 32768~61000,也可以通過如下參數設置指定
net.ipv4.ip_local_port_range
如果發起連接一方的 TIME_WAIT 狀態過多,占滿了所有端口資源,則會導致無法創建新連接。
客戶端受端口資源限制:
客戶端TIME_WAIT過多,就會導致端口資源被占用,因為端口就65536個,被占滿就會導致無法創建新的連接
服務端受系統資源限制:
由于一個四元組表示 TCP 連接,理論上服務端可以建立很多連接,服務端確實只監聽一個端口 但是會把連接扔給處理線程,所以理論上監聽的端口可以繼續監聽。但是線程池處理不了那么多一直不斷的連接了。所以當服務端出現大量 TIME_WAIT 時,系統資源被占滿時,會導致處理不過來新的連接
③ 如何優化 TIME_WAIT?
這里給出優化 TIME-WAIT 的幾個方式,都是有利有弊:
打開 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 選項
net.ipv4.tcp_max_tw_buckets
程序中使用SO_LINGER,應用強制使用RST關閉
④ 為什么 TIME_WAIT 等待的時間是 2MSL?
MSL 是 Maximum Segment Lifetime,報文最大生存時間,它是任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。因為 TCP 報文基于是 IP 協議的,而 IP 頭中有一個 TTL 字段,是 IP 數據報可以經過的最大路由數,每經過一個處理他的路由器此值就減 1,當此值為 0 則數據報將被丟棄,同時發送 ICMP 報文通知源主機。
MSL 與 TTL 的區別:MSL 的單位是時間,而 TTL 是經過路由跳數。所以 MSL 應該要大于等于 TTL 消耗為 0 的時間,以確保報文已被自然消亡。
TIME_WAIT 等待 2 倍的 MSL,比較合理的解釋是:網絡中可能存在來自發送方的數據包,當這些發送方的數據包被接收方處理后又會向對方發送響應,所以一來一回需要等待 2 倍的時間。
比如如果被動關閉方沒有收到斷開連接的最后的 ACK 報文,就會觸發超時重發 Fin 報文,另一方接收到 FIN 后,會重發 ACK 給被動關閉方, 一來一去正好 2 個 MSL。
2MSL 的時間是從客戶端接收到 FIN 后發送 ACK 開始計時的。如果在 TIME-WAIT 時間內,因為客戶端的 ACK 沒有傳輸到服務端,客戶端又接收到了服務端重發的 FIN 報文,那么 2MSL 時間將重新計時。
在 Linux 系統里 2MSL 默認是 60 秒,那么一個 MSL 也就是 30 秒。Linux 系統停留在 TIME_WAIT 的時間為固定的 60 秒。
其定義在 Linux 內核代碼里的名稱為 TCP_TIMEWAIT_LEN:
#define TCP_TIMEWAIT_LEN (60HZ) / how long to wait to destroy TIME-WAIT state, about 60 seconds */
如果要修改TIME_WAIT的時間長度,只能修改Linux內核代碼里TCP_TIMEWAIT_LEN的值,并重新編譯Linux內核。
連接過程
TCP三次握手
開始客戶端和服務器都處于CLOSED狀態,然后服務端開始監聽某個端口,進入LISTEN狀態:
第一次握手(SYN=1, seq=x),發送完畢后,客戶端進入 SYN_SENT 狀態
第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1), 發送完畢后,服務器端進入 SYN_RCVD 狀態
第三次握手(ACK=1,ACKnum=y+1),發送完畢后,客戶端進入 ESTABLISHED 狀態,當服務器端接收到這個包時,也進入 ESTABLISHED 狀態,TCP 握手,即可以開始數據傳輸
假設一開始客戶端和服務端都處于CLOSED的狀態。然后先是服務端主動監聽某個端口,處于LISTEN狀態
【第一個報文】:客戶端會隨機初始化序號(client_isn),將此序號置于 TCP 首部的「序號」字段中,同時把 SYN 標志位置為 1 ,表示 SYN 報文。接著把第一個 SYN 報文發送給服務端,表示向服務端發起連接,該報文不包含應用層數據,之后客戶端處于 SYN-SENT 狀態
【第二個報文】:服務端收到客戶端的 SYN 報文后,首先服務端也隨機初始化自己的序號(server_isn),將此序號填入 TCP 首部的「序號」字段中,其次把 TCP 首部的「確認應答號」字段填入 client_isn + 1, 接著把 SYN 和 ACK 標志位置為 1。最后把該報文發給客戶端,該報文也不包含應用層數據,之后服務端處于 SYN-RCVD 狀態
【第三個報文】:客戶端收到服務端報文后,還要向服務端回應最后一個應答報文,首先該應答報文 TCP 首部 ACK 標志位置為 1 ,其次「確認應答號」字段填入 server_isn + 1 ,最后把報文發送給服務端,這次報文可以攜帶客戶到服務器的數據,之后客戶端處于 ESTABLISHED 狀態
服務器收到客戶端的應答報文后,也進入 ESTABLISHED 狀態
第三次握手是否可以攜帶數據?
第三次握手是可以攜帶數據的,前兩次握手是不可以攜帶數據的。一旦完成三次握手,雙方都處于 ESTABLISHED 狀態,此時連接就已建立完成,客戶端和服務端就可以相互發送數據了。假設第三次握手的報文的seq是x+1:
如果有攜帶數據:下次客戶端發送的報文,seq=服務器發回的ACK號
如果沒有攜帶數據:第三次握手的報文不消耗seq,下次客戶端發送的報文,seq序列號為x+1
① 服務端SYN-RECV流程
② 客戶端SYN-SEND流程
場景1:sk->sk_write_pending != 0
這個值默認是0的,那什么情況會導致不為0呢?答案是協議棧發送數據的函數遇到socket狀態不是ESTABLISHED的時候,會對這個變量做++操作,并等待一小會時間嘗試發送數據。
場景2:icsk->icsk_accept_queue.rskq_defer_accept != 0
客戶端先bind到一個端口和IP,然后setsockopt(TCP_DEFER_ACCEPT),然后connect服務器,這個時候就會出現rskq_defer_accept=1的情況,這時候內核會設置定時器等待數據一起在回復ACK包。
場景3:icsk->icsk_ack.pingpong != 0
pingpong這個屬性實際上也是一個套接字選項,用來表明當前鏈接是否為交互數據流,如其值為1,則表明為交互數據流,會使用延遲確認機制。
為什么是三次握手?不是兩次、四次?
TCP建立連接時,通過三次握手能防止歷史連接的建立,能減少雙方不必要的資源開銷,能幫助雙方同步初始化序列號。序列號能夠保證數據包不重復、不丟棄和按序傳輸。不使用「兩次握手」和「四次握手」的原因:
兩次握手:無法防止歷史連接的建立,會造成雙方資源的浪費,也無法可靠的同步雙方序列號
四次握手:三次握手就已經理論上最少可靠連接建立,所以不需要使用更多的通信次數
接下來以三個方面分析三次握手的原因:
三次握手才可以阻止重復歷史連接的初始化(主要原因)
三次握手才可以同步雙方的初始序列號
三次握手才可以避免資源浪費
原因一:避免歷史連接
客戶端連續發送多次 SYN 建立連接的報文,在網絡擁堵情況下:
一個「舊 SYN 報文」比「最新的 SYN 」 報文早到達了服務端
那么此時服務端就會回一個 SYN + ACK 報文給客戶端
客戶端收到后可以根據自身的上下文,判斷這是一個歷史連接(序列號過期或超時),那么客戶端就會發送 RST 報文給服務端,表示中止這一次連接
如果是兩次握手連接,就不能判斷當前連接是否是歷史連接,三次握手則可以在客戶端(發送方)準備發送第三次報文時,客戶端因有足夠的上下文來判斷當前連接是否是歷史連接:
如果是歷史連接(序列號過期或超時),則第三次握手發送的報文是 RST 報文,以此中止歷史連接
如果不是歷史連接,則第三次發送的報文是 ACK 報文,通信雙方就會成功建立連接
所以,TCP 使用三次握手建立連接的最主要原因是防止歷史連接初始化了連接。
原因二:同步雙方初始序列號
TCP 協議的通信雙方, 都必須維護一個「序列號」, 序列號是可靠傳輸的一個關鍵因素,它的作用:
接收方可以去除重復的數據
接收方可以根據數據包的序列號按序接收
可以標識發送出去的數據包中, 哪些是已經被對方收到的
可見,序列號在 TCP 連接中占據著非常重要的作用,所以當客戶端發送攜帶「初始序列號」的 SYN 報文的時候,需要服務端回一個 ACK 應答報文,表示客戶端的 SYN 報文已被服務端成功接收,那當服務端發送「初始序列號」給客戶端的時候,依然也要得到客戶端的應答回應,這樣一來一回,才能確保雙方的初始序列號能被可靠的同步。
四次握手其實也能夠可靠的同步雙方的初始化序號,但由于第二步和第三步可以優化成一步,所以就成了「三次握手」。而兩次握手只保證了一方的初始序列號能被對方成功接收,沒辦法保證雙方的初始序列號都能被確認接收。
原因三:避免資源浪費
如果只有「兩次握手」,當客戶端的 SYN 請求連接在網絡中阻塞,客戶端沒有接收到 ACK 報文,就會重新發送 SYN ,由于沒有第三次握手,服務器不清楚客戶端是否收到了自己發送的建立連接的 ACK 確認信號,所以每收到一個 SYN 就只能先主動建立一個連接,這會造成什么情況呢?如果客戶端的 SYN 阻塞了,重復發送多次 SYN 報文,那么服務器在收到請求后就會建立多個冗余的無效鏈接,造成不必要的資源浪費。
即兩次握手會造成消息滯留情況下,服務器重復接受無用的連接請求 SYN 報文,而造成重復分配資源。
TCP四次揮手
第一次揮手:FIN=1,seq=u,發送完畢后客戶端進入FIN_WAIT_1 狀態
第二次揮手:ACK=1,seq =v,ack=u+1,發送完畢后服務器端進入CLOSE_WAIT 狀態,客戶端接收到后進入 FIN_WAIT_2 狀態
第三次揮手:FIN=1,ACK=1,seq=w,ack=u+1,發送完畢后服務器端進入LAST_ACK狀態,客戶端接收到后進入 TIME_WAIT狀態
第四次揮手:ACK=1,seq=u+1,ack=w+1,客戶端接收到來自服務器端的關閉請求,發送一個確認包,并進入 TIME_WAIT狀態,等待了某個固定時間(兩個最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,沒有收到服務器端的 ACK ,認為服務器端已經正常關閉連接,于是自己也關閉連接,進入 CLOSED 狀態。服務器端接收到這個確認包之后,關閉連接,進入 CLOSED 狀態
四次揮手過程:
客戶端打算關閉連接,此時會發送一個 TCP 首部 FIN 標志位被置為 1 的報文,也即 FIN 報文,之后客戶端進入 FIN_WAIT_1 狀態
服務端收到該報文后,就向客戶端發送 ACK 應答報文,接著服務端進入 CLOSED_WAIT 狀態
客戶端收到服務端的 ACK 應答報文后,之后進入 FIN_WAIT_2 狀態
等待服務端處理完數據后,也向客戶端發送 FIN 報文,之后服務端進入 LAST_ACK 狀態
客戶端收到服務端的 FIN 報文后,回一個 ACK 應答報文,之后進入 TIME_WAIT 狀態
服務器收到了 ACK 應答報文后,就進入了 CLOSE 狀態,至此服務端已經完成連接的關閉
客戶端在經過 2MSL 一段時間后,自動進入 CLOSE 狀態,至此客戶端也完成連接的關閉
為什么揮手需要四次?
再來回顧下四次揮手雙方發 FIN 包的過程,就能理解為什么需要四次了。
關閉連接時,客戶端向服務端發送 FIN 時,僅僅表示客戶端不再發送數據了但是還能接收數據。
服務器收到客戶端的 FIN 報文時,先回一個 ACK 應答報文,而服務端可能還有數據需要處理和發送,等服務端不再發送數據時,才發送 FIN 報文給客戶端來表示同意現在關閉連接。
從上面過程可知,服務端通常需要等待完成數據的發送和處理,所以服務端的 ACK 和 FIN 一般都會分開發送,從而比三次握手導致多了一次。
TCP優化
正確有效的使用TCP參數可以提高 TCP 性能。以下將從三個角度來闡述提升 TCP 的策略,分別是:
TCP三次握手優化
TCP四次揮手優化
TCP數據傳輸優化
常見問題
TCP和UDP
TCP和UDP的區別?
連接TCP 是面向連接的傳輸層協議,傳輸數據前先要建立連接UDP 是不需要連接,即刻傳輸數據
服務對象TCP 是一對一的兩點服務,即一條連接只有兩個端點UDP 支持一對一、一對多、多對多的交互通信
可靠性TCP 是可靠交付數據的,數據可以無差錯、不丟失、不重復、按需到達UDP 是盡最大努力交付,不保證可靠交付數據
擁塞控制、流量控制TCP 有擁塞控制和流量控制機制,保證數據傳輸的安全性UDP 則沒有,即使網絡非常擁堵了,也不會影響 UDP 的發送速率
首部開銷TCP 首部長度較長,會有一定的開銷,首部在沒有使用「選項」字段時是 20 個字節,如果使用了「選項」字段則會變長的UDP 首部只有 8 個字節,并且是固定不變的,開銷較小
傳輸方式TCP 是流式傳輸,沒有邊界,但保證順序和可靠UDP 是一個包一個包的發送,是有邊界的,但可能會丟包和亂序
分片不同TCP 的數據大小如果大于 MSS 大小,則會在傳輸層進行分片,目標主機收到后,也同樣在傳輸層組裝 TCP 數據包,如果中途丟失了一個分片,只需要傳輸丟失的這個分片UDP 的數據大小如果大于 MTU 大小,則會在 IP 層進行分片,目標主機收到后,在 IP 層組裝完數據,接著再傳給傳輸層,但是如果中途丟了一個分片,則就需要重傳所有的數據包,這樣傳輸效率非常差,所以通常 UDP 的報文應該小于 MTU
ISN
① 為什么客戶端和服務端的初始序列號 ISN 是不相同的?
如果一個已經失效的連接被重用了,但是該舊連接的歷史報文還殘留在網絡中,如果序列號相同,那么就無法分辨出該報文是不是歷史報文,如果歷史報文被新的連接接收了,則會產生數據錯亂。所以,每次建立連接前重新初始化一個序列號主要是為了通信雙方能夠根據序號將不屬于本連接的報文段丟棄。另一方面是為了安全性,防止黑客偽造的相同序列號的 TCP 報文被對方接收。
② 初始序列號 ISN 是如何隨機產生的?
起始 ISN 是基于時鐘的,每 4 毫秒 + 1,轉一圈要 4.55 個小時。RFC1948 中提出了一個較好的初始化序列號 ISN 隨機生成算法。
ISN = M + F (localhost, localport, remotehost, remoteport)
M 是一個計時器,這個計時器每隔 4 毫秒加 1
F 是一個 Hash 算法,根據源 IP、目的 IP、源端口、目的端口生成一個隨機數值。要保證 Hash 算法不能被外部輕易推算得出,用 MD5 算法是一個比較好的選擇
UDP
總結
TCP 向上層提供面向連接的可靠服務 ,UDP 向上層提供無連接不可靠服務
UDP 沒有 TCP 傳輸可靠,但是可以在實時性要求搞的地方有所作為
對數據準確性要求高,速度可以相對較慢的,可以選用TCP
TCP數據可靠性
一句話:通過校驗和、序列號、確認應答、超時重傳、連接管理、流量控制、擁塞控制等機制來保證可靠性。
(1)校驗和
在數據傳輸過程中,將發送的數據段都當做一個16位的整數,將這些整數加起來,并且前面的進位不能丟棄,補在最后,然后取反,得到校驗和。
發送方:在發送數據之前計算校驗和,并進行校驗和的填充。接收方:收到數據后,對數據以同樣的方式進行計算,求出校驗和,與發送方進行比較。
(2)序列號
TCP 傳輸時將每個字節的數據都進行了編號,這就是序列號。序列號的作用不僅僅是應答作用,有了序列號能夠將接收到的數據根據序列號進行排序,并且去掉重復的數據。
(3)確認應答
TCP 傳輸過程中,每次接收方接收到數據后,都會對傳輸方進行確認應答,也就是發送 ACK 報文,這個 ACK 報文中帶有對應的確認序列號,告訴發送方,接收了哪些數據,下一次數據從哪里傳。
(4)超時重傳
在進行 TCP 傳輸時,由于存在確認應答與序列號機制,也就是說發送方發送一部分數據后,都會等待接收方發送的 ACK 報文,并解析 ACK 報文,判斷數據是否傳輸成功。如果發送方發送完數據后,遲遲都沒有接收到接收方傳來的 ACK 報文,那么就對剛剛發送的數據進行重發。
(5)連接管理
就是指三次握手、四次揮手的過程。
(6)流量控制
如果發送方的發送速度太快,會導致接收方的接收緩沖區填充滿了,這時候繼續傳輸數據,就會造成大量丟包,進而引起丟包重傳等等一系列問題。TCP 支持根據接收端的處理能力來決定發送端的發送速度,這就是流量控制機制。
具體實現方式:接收端將自己的接收緩沖區大小放入 TCP 首部的『窗口大小』字段中,通過 ACK 通知發送端。
(7)擁塞控制
TCP 傳輸過程中一開始就發送大量數據,如果當時網絡非常擁堵,可能會造成擁堵加劇。所以 TCP 引入了慢啟動機制,在開始發送數據的時候,先發少量的數據探探路。
TCP協議如何提高傳輸效率
一句話:TCP 協議提高效率的方式有滑動窗口、快重傳、延遲應答、捎帶應答等。
(1)滑動窗口
如果每一個發送的數據段,都要收到 ACK 應答之后再發送下一個數據段,這樣的話我們效率很低,大部分時間都用在了等待 ACK 應答上了。
為了提高效率我們可以一次發送多條數據,這樣就能使等待時間大大減少,從而提高性能。窗口大小指的是無需等待確認應答而可以繼續發送數據的最大值。
(2)快重傳
快重傳也叫高速重發控制。
那么如果出現了丟包,需要進行重傳。一般分為兩種情況:
情況一:數據包已經抵達,ACK被丟了。這種情況下,部分ACK丟了并不影響,因為可以通過后續的ACK進行確認;
情況二:數據包直接丟了。發送端會連續收到多個相同的 ACK 確認,發送端立即將對應丟失的數據重傳。
(3)延遲應答
如果接收數據的主機立刻返回ACK應答,這時候返回的窗口大小可能比較小。
假設接收端緩沖區為1M,一次收到了512K的數據;如果立刻應答,返回的窗口就是512K;
但實際上可能處理端處理速度很快,10ms之內就把512K的數據從緩存區消費掉了;
在這種情況下,接收端處理還遠沒有達到自己的極限,即使窗口再放大一些,也能處理過來;
如果接收端稍微等一會在應答,比如等待200ms再應答,那么這個時候返回的窗口大小就是1M;
窗口越大,網絡吞吐量就越大,傳輸效率就越高;我們的目標是在保證網絡不擁塞的情況下盡量提高傳輸效率。
(4)捎帶應答
在延遲應答的基礎上,很多情況下,客戶端服務器在應用層也是一發一收的。這時候常常采用捎帶應答的方式來提高效率,而ACK響應常常伴隨著數據報文共同傳輸。如:三次握手。
TCP如何處理擁塞
網絡擁塞現象是指到達通信網絡中某一部分的分組數量過多,使得該部分網絡來不及處理,以致引起這部分乃至整個網絡性能下降的現象,嚴重時甚至會導致網絡通信業務陷入停頓,即出現死鎖現象。擁塞控制是處理網絡擁塞現象的一種機制。
擁塞控制的四個階段:
慢啟動
擁塞避免
快速重傳
快速恢復
Socket
基于TCP協議的客戶端和服務器工作:
服務端和客戶端初始化 socket,得到文件描述符
服務端調用 bind,將綁定在 IP 地址和端口
服務端調用 listen,進行監聽
服務端調用 accept,等待客戶端連接
客戶端調用 connect,向服務器端的地址和端口發起連接請求
服務端 accept 返回用于傳輸的 socket 的文件描述符
客戶端調用 write 寫入數據;服務端調用 read 讀取數據
客戶端斷開連接時,會調用 close,那么服務端 read 讀取數據的時候,就會讀取到了 EOF,待處理完數據后,服務端調用 close,表示連接關閉
listen 時候參數 backlog 的意義?
Linux內核中會維護兩個隊列:
未完成連接隊列(SYN 隊列):接收到一個 SYN 建立連接請求,處于 SYN_RCVD 狀態;
已完成連接隊列(Accpet 隊列):已完成 TCP 三次握手過程,處于 ESTABLISHED 狀態;
SYN 隊列 與 Accpet 隊列
int listen (int socketfd, int backlog)
參數一 socketfd 為 socketfd 文件描述符
參數二 backlog,這參數在歷史內環版本有一定的變化
在早期Linux內核backlog是SYN隊列大小,也就是未完成的隊列大小。在Linux內核2.2之后,backlog變成accept隊列,也就是已完成連接建立的隊列長度,所以現在通常認為backlog是accept隊列。但是上限值是內核參數somaxconn的大小,也就說accpet隊列長度=min(backlog, somaxconn)。
accept 發送在三次握手的哪一步?
我們先看看客戶端連接服務端時,發送了什么?
客戶端的協議棧向服務器端發送了 SYN 包,并告訴服務器端當前發送序列號 client_isn,客戶端進入 SYNC_SENT 狀態
服務器端的協議棧收到這個包之后,和客戶端進行 ACK 應答,應答的值為 client_isn+1,表示對 SYN 包 client_isn 的確認,同時服務器也發送一個 SYN 包,告訴客戶端當前我的發送序列號為 server_isn,服務器端進入 SYNC_RCVD 狀態
客戶端協議棧收到 ACK 之后,使得應用程序從 connect 調用返回,表示客戶端到服務器端的單向連接建立成功,客戶端的狀態為 ESTABLISHED,同時客戶端協議棧也會對服務器端的 SYN 包進行應答,應答數據為 server_isn+1
應答包到達服務器端后,服務器端協議棧使得 accept 阻塞調用返回,這個時候服務器端到客戶端的單向連接也建立成功,服務器端也進入 ESTABLISHED 狀態
從上面的描述過程,我們可以得知客戶端 connect 成功返回是在第二次握手,服務端 accept 成功返回是在三次握手成功之后。
客戶端調用 close 了,連接是斷開的流程是什么?
我們看看客戶端主動調用了 close,會發生什么?
客戶端調用 close,表明客戶端沒有數據需要發送了,則此時會向服務端發送FIN報文,進入FIN_WAIT_1狀態
服務端接收到了 FIN 報文,TCP協議棧會為 FIN 包插入一個文件結束符 EOF 到接收緩沖區中,應用程序可以通過 read 調用來感知這個 FIN 包。這個 EOF 會被放在已排隊等候的其他已接收的數據之后,這就意味著服務端需要處理這種異常情況,因為EOF表示在該連接上再無額外數據到達。此時服務端進入 CLOSE_WAIT 狀態
接著,當處理完數據后,自然就會讀到 EOF,于是也調用 close 關閉它的套接字,這會使得會發出一個 FIN 包,之后處于 LAST_ACK 狀態
客戶端接收到服務端的 FIN 包,并發送 ACK 確認包給服務端,此時客戶端將進入 TIME_WAIT 狀態
服務端收到 ACK 確認包后,就進入了最后的 CLOSE 狀態
客戶端進過 2MSL 時間之后,也進入 CLOSE 狀態
審核編輯 :李倩
-
通信協議
+關注
關注
28文章
899瀏覽量
40350 -
網絡
+關注
關注
14文章
7586瀏覽量
89007 -
TCP
+關注
關注
8文章
1374瀏覽量
79159 -
模型
+關注
關注
1文章
3279瀏覽量
48970
原文標題:Socket
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論