1. 三次握手
三次握手(Three-way Handshake)其實(shí)就是指建立一個(gè)TCP連接時(shí)莫瞬,需要客戶端和服務(wù)器總共發(fā)送3個(gè)包夏哭。進(jìn)行三次握手的主要作用就是為了確認(rèn)雙方的接收能力和發(fā)送能力是否正常卒煞、指定自己的初始化序列號(hào)為后面的可靠性傳送做準(zhǔn)備狸驳。實(shí)質(zhì)上其實(shí)就是連接服務(wù)器指定端口藕咏,建立TCP連接再层,并同步連接雙方的序列號(hào)和確認(rèn)號(hào)贸铜,交換TCP窗口大小
信息。
剛開(kāi)始客戶端處于 Closed 的狀態(tài)聂受,服務(wù)端處于 Listen 狀態(tài)蒿秦。
進(jìn)行三次握手:
-
第一次握手:客戶端給服務(wù)端發(fā)一個(gè) SYN 報(bào)文,并指明客戶端的初始化序列號(hào) ISN蛋济。此時(shí)客戶端處于
SYN_SENT
狀態(tài)棍鳖。首部的同步位SYN=1,初始序號(hào)seq=x碗旅,SYN=1的報(bào)文段不能攜帶數(shù)據(jù)渡处,但要消耗掉一個(gè)序號(hào)。
-
第二次握手:服務(wù)器收到客戶端的 SYN 報(bào)文之后祟辟,會(huì)以自己的 SYN 報(bào)文作為應(yīng)答医瘫,并且也是指定了自己的初始化序列號(hào) ISN(s)。同時(shí)會(huì)把客戶端的 ISN + 1 作為ACK 的值旧困,表示自己已經(jīng)收到了客戶端的 SYN醇份,此時(shí)服務(wù)器處于
SYN_RCVD
的狀態(tài)稼锅。在確認(rèn)報(bào)文段中SYN=1,ACK=1僚纷,確認(rèn)號(hào)ack=x+1缰贝,初始序號(hào)seq=y。
-
第三次握手:客戶端收到 SYN 報(bào)文之后畔濒,會(huì)發(fā)送一個(gè) ACK 報(bào)文,當(dāng)然锣咒,也是一樣把服務(wù)器的 ISN + 1 作為 ACK 的值侵状,表示已經(jīng)收到了服務(wù)端的 SYN 報(bào)文,此時(shí)客戶端處于
ESTABLISHED
狀態(tài)毅整。服務(wù)器收到 ACK 報(bào)文之后趣兄,也處于ESTABLISHED
狀態(tài),此時(shí)悼嫉,雙方已建立起了連接艇潭。確認(rèn)報(bào)文段ACK=1,確認(rèn)號(hào)ack=y+1戏蔑,序號(hào)seq=x+1(初始為seq=x蹋凝,第二個(gè)報(bào)文段所以要+1),ACK報(bào)文段可以攜帶數(shù)據(jù)总棵,不攜帶數(shù)據(jù)則不消耗序號(hào)鳍寂。
發(fā)送第一個(gè)SYN的一端將執(zhí)行主動(dòng)打開(kāi)(active open),接收這個(gè)SYN并發(fā)回下一個(gè)SYN的另一端執(zhí)行被動(dòng)打開(kāi)(passive open)情龄。
在socket編程中迄汛,客戶端執(zhí)行connect()時(shí),將觸發(fā)三次握手骤视。
1.1 為什么需要三次握手鞍爱,兩次不行嗎?
弄清這個(gè)問(wèn)題专酗,我們需要先弄明白三次握手的目的是什么睹逃,能不能只用兩次握手來(lái)達(dá)到同樣的目的。
- 第一次握手:客戶端發(fā)送網(wǎng)絡(luò)包祷肯,服務(wù)端收到了唯卖。
這樣服務(wù)端就能得出結(jié)論:客戶端的發(fā)送能力、服務(wù)端的接收能力是正常的躬柬。 - 第二次握手:服務(wù)端發(fā)包拜轨,客戶端收到了。
這樣客戶端就能得出結(jié)論:服務(wù)端的接收允青、發(fā)送能力橄碾,客戶端的接收卵沉、發(fā)送能力是正常的。不過(guò)此時(shí)服務(wù)器并不能確認(rèn)客戶端的接收能力是否正常法牲。 - 第三次握手:客戶端發(fā)包史汗,服務(wù)端收到了。
這樣服務(wù)端就能得出結(jié)論:客戶端的接收拒垃、發(fā)送能力正常停撞,服務(wù)器自己的發(fā)送、接收能力也正常悼瓮。
因此戈毒,需要三次握手才能確認(rèn)雙方的接收與發(fā)送能力是否正常。
試想如果是用兩次握手横堡,則會(huì)出現(xiàn)下面這種情況:
如客戶端發(fā)出連接請(qǐng)求埋市,但因連接請(qǐng)求報(bào)文丟失而未收到確認(rèn),于是客戶端再重傳一次連接請(qǐng)求命贴。后來(lái)收到了確認(rèn)道宅,建立了連接。數(shù)據(jù)傳輸完畢后胸蛛,就釋放了連接污茵,客戶端共發(fā)出了兩個(gè)連接請(qǐng)求報(bào)文段,其中第一個(gè)丟失葬项,第二個(gè)到達(dá)了服務(wù)端省咨,但是第一個(gè)丟失的報(bào)文段只是在某些網(wǎng)絡(luò)結(jié)點(diǎn)長(zhǎng)時(shí)間滯留了,延誤到連接釋放以后的某個(gè)時(shí)間才到達(dá)服務(wù)端玷室,此時(shí)服務(wù)端誤認(rèn)為客戶端又發(fā)出一次新的連接請(qǐng)求零蓉,于是就向客戶端發(fā)出確認(rèn)報(bào)文段,同意建立連接穷缤,不采用三次握手敌蜂,只要服務(wù)端發(fā)出確認(rèn),就建立新的連接了津肛,此時(shí)客戶端忽略服務(wù)端發(fā)來(lái)的確認(rèn)章喉,也不發(fā)送數(shù)據(jù),則服務(wù)端一致等待客戶端發(fā)送數(shù)據(jù)身坐,浪費(fèi)資源秸脱。
1.2 什么是半連接隊(duì)列?
服務(wù)器第一次收到客戶端的 SYN 之后部蛇,就會(huì)處于 SYN_RCVD 狀態(tài)摊唇,此時(shí)雙方還沒(méi)有完全建立其連接,服務(wù)器會(huì)把此種狀態(tài)下請(qǐng)求連接放在一個(gè)隊(duì)列里涯鲁,我們把這種隊(duì)列稱之為半連接隊(duì)列巷查。
當(dāng)然還有一個(gè)全連接隊(duì)列有序,就是已經(jīng)完成三次握手,建立起連接的就會(huì)放在全連接隊(duì)列中岛请。如果隊(duì)列滿了就有可能會(huì)出現(xiàn)丟包現(xiàn)象旭寿。
這里在補(bǔ)充一點(diǎn)關(guān)于SYN-ACK 重傳次數(shù)的問(wèn)題:
服務(wù)器發(fā)送完SYN-ACK包,如果未收到客戶確認(rèn)包崇败,服務(wù)器進(jìn)行首次重傳盅称,等待一段時(shí)間仍未收到客戶確認(rèn)包,進(jìn)行第二次重傳后室。如果重傳次數(shù)超過(guò)系統(tǒng)規(guī)定的最大重傳次數(shù)缩膝,系統(tǒng)將該連接信息從半連接隊(duì)列中刪除。
注意咧擂,每次重傳等待的時(shí)間不一定相同,一般會(huì)是指數(shù)增長(zhǎng)檀蹋,例如間隔時(shí)間為 1s松申,2s,4s俯逾,8s…
1.3 ISN(Initial Sequence Number)是固定的嗎贸桶?
當(dāng)一端為建立連接而發(fā)送它的SYN時(shí),它為連接選擇一個(gè)初始序號(hào)桌肴。ISN隨時(shí)間而變化皇筛,因此每個(gè)連接都將具有不同的ISN。ISN可以看作是一個(gè)32比特的計(jì)數(shù)器坠七,每4ms加1 水醋。這樣選擇序號(hào)的目的在于防止在網(wǎng)絡(luò)中被延遲的分組在以后又被傳送,而導(dǎo)致某個(gè)連接的一方對(duì)它做錯(cuò)誤的解釋彪置。
三次握手的其中一個(gè)重要功能是客戶端和服務(wù)端交換 ISN(Initial Sequence Number)拄踪,以便讓對(duì)方知道接下來(lái)接收數(shù)據(jù)的時(shí)候如何按序列號(hào)組裝數(shù)據(jù)。如果 ISN 是固定的拳魁,攻擊者很容易猜出后續(xù)的確認(rèn)號(hào)惶桐,因此 ISN 是動(dòng)態(tài)生成的。
1.4 三次握手過(guò)程中可以攜帶數(shù)據(jù)嗎潘懊?
其實(shí)第三次握手的時(shí)候姚糊,是可以攜帶數(shù)據(jù)的。但是授舟,第一次救恨、第二次握手不可以攜帶數(shù)據(jù)
為什么這樣呢?大家可以想一個(gè)問(wèn)題,假如第一次握手可以攜帶數(shù)據(jù)的話释树,如果有人要惡意攻擊服務(wù)器忿薇,那他每次都在第一次握手中的 SYN 報(bào)文中放入大量的數(shù)據(jù)裙椭。因?yàn)楣粽吒揪筒焕矸?wù)器的接收、發(fā)送能力是否正常署浩,然后瘋狂著重復(fù)發(fā) SYN 報(bào)文的話揉燃,這會(huì)讓服務(wù)器花費(fèi)很多時(shí)間、內(nèi)存空間來(lái)接收這些報(bào)文筋栋。
也就是說(shuō)炊汤,第一次握手不可以放數(shù)據(jù),其中一個(gè)簡(jiǎn)單的原因就是會(huì)讓服務(wù)器更加容易受到攻擊了弊攘。而對(duì)于第三次的話抢腐,此時(shí)客戶端已經(jīng)處于 ESTABLISHED 狀態(tài)。對(duì)于客戶端來(lái)說(shuō)襟交,他已經(jīng)建立起連接了迈倍,并且也已經(jīng)知道服務(wù)器的接收、發(fā)送能力是正常的了捣域,所以能攜帶數(shù)據(jù)也沒(méi)啥毛病啼染。
1.5 SYN攻擊是什么?
服務(wù)器端的資源分配是在二次握手時(shí)分配的焕梅,而客戶端的資源是在完成三次握手時(shí)分配的迹鹅,所以服務(wù)器容易受到SYN洪泛攻擊。SYN攻擊就是Client在短時(shí)間內(nèi)偽造大量不存在的IP地址贞言,并向Server不斷地發(fā)送SYN包斜棚,Server則回復(fù)確認(rèn)包,并等待Client確認(rèn)该窗,由于源地址不存在弟蚀,因此Server需要不斷重發(fā)直至超時(shí),這些偽造的SYN包將長(zhǎng)時(shí)間占用未連接隊(duì)列酗失,導(dǎo)致正常的SYN請(qǐng)求因?yàn)殛?duì)列滿而被丟棄粗梭,從而引起網(wǎng)絡(luò)擁塞甚至系統(tǒng)癱瘓。SYN 攻擊是一種典型的 DoS/DDoS 攻擊级零。
檢測(cè) SYN 攻擊非常的方便断医,當(dāng)你在服務(wù)器上看到大量的半連接狀態(tài)時(shí),特別是源IP地址是隨機(jī)的奏纪,基本上可以斷定這是一次SYN攻擊鉴嗤。在 Linux/Unix 上可以使用系統(tǒng)自帶的 netstats 命令來(lái)檢測(cè) SYN 攻擊。
netstat -n -p TCP | grep SYN_RECV
常見(jiàn)的防御 SYN 攻擊的方法有如下幾種:
- 縮短超時(shí)(SYN Timeout)時(shí)間
- 增加最大半連接數(shù)
- 過(guò)濾網(wǎng)關(guān)防護(hù)
- SYN cookies技術(shù)
2. 四次揮手
建立一個(gè)連接需要三次握手序调,而終止一個(gè)連接要經(jīng)過(guò)四次揮手(也有將四次揮手叫做四次握手的)醉锅。這由TCP的半關(guān)閉(half-close)造成的。所謂的半關(guān)閉发绢,其實(shí)就是TCP提供了連接的一端在結(jié)束它的發(fā)送后還能接收來(lái)自另一端數(shù)據(jù)的能力硬耍。
TCP 連接的拆除需要發(fā)送四個(gè)包垄琐,因此稱為四次揮手(Four-way handshake),客戶端或服務(wù)端均可主動(dòng)發(fā)起揮手動(dòng)作经柴。
剛開(kāi)始雙方都處于ESTABLISHED
狀態(tài)狸窘,假如是客戶端先發(fā)起關(guān)閉請(qǐng)求。四次揮手的過(guò)程如下:
- 第一次揮手:客戶端發(fā)送一個(gè) FIN 報(bào)文坯认,報(bào)文中會(huì)指定一個(gè)序列號(hào)翻擒。此時(shí)客戶端處于
FIN_WAIT1
狀態(tài)。
即發(fā)出連接釋放報(bào)文段(FIN=1牛哺,序號(hào)seq=u)陋气,并停止再發(fā)送數(shù)據(jù),主動(dòng)關(guān)閉TCP連接引润,進(jìn)入FIN_WAIT1(終止等待1)狀態(tài)巩趁,等待服務(wù)端的確認(rèn)。 - 第二次揮手:服務(wù)端收到 FIN 之后淳附,會(huì)發(fā)送 ACK 報(bào)文议慰,且把客戶端的序列號(hào)值 +1 作為 ACK 報(bào)文的序列號(hào)值,表明已經(jīng)收到客戶端的報(bào)文了燃观,此時(shí)服務(wù)端處于
CLOSE_WAIT
狀態(tài)褒脯。
即服務(wù)端收到連接釋放報(bào)文段后即發(fā)出確認(rèn)報(bào)文段(ACK=1便瑟,確認(rèn)號(hào)ack=u+1缆毁,序號(hào)seq=v),服務(wù)端進(jìn)入CLOSE_WAIT(關(guān)閉等待)狀態(tài)到涂,此時(shí)的TCP處于半關(guān)閉狀態(tài)脊框,客戶端到服務(wù)端的連接釋放〖模客戶端收到服務(wù)端的確認(rèn)后浇雹,進(jìn)入FIN_WAIT2(終止等待2)狀態(tài),等待服務(wù)端發(fā)出的連接釋放報(bào)文段屿讽。 - 第三次揮手:如果服務(wù)端也想斷開(kāi)連接了昭灵,和客戶端的第一次揮手一樣,發(fā)給 FIN 報(bào)文伐谈,且指定一個(gè)序列號(hào)烂完。此時(shí)服務(wù)端處于
LAST_ACK
的狀態(tài)。
即服務(wù)端沒(méi)有要向客戶端發(fā)出的數(shù)據(jù)诵棵,服務(wù)端發(fā)出連接釋放報(bào)文段(FIN=1抠蚣,ACK=1,序號(hào)seq=w履澳,確認(rèn)號(hào)ack=u+1)嘶窄,服務(wù)端進(jìn)入LAST_ACK(最后確認(rèn))狀態(tài)怀跛,等待客戶端的確認(rèn)。 - 第四次揮手:客戶端收到 FIN 之后柄冲,一樣發(fā)送一個(gè) ACK 報(bào)文作為應(yīng)答吻谋,且把服務(wù)端的序列號(hào)值 +1 作為自己 ACK 報(bào)文的序列號(hào)值,此時(shí)客戶端處于
TIME_WAIT
狀態(tài)羊初。需要過(guò)一陣子以確保服務(wù)端收到自己的 ACK 報(bào)文之后才會(huì)進(jìn)入 CLOSED 狀態(tài)滨溉,服務(wù)端收到 ACK 報(bào)文之后,就處于關(guān)閉連接了长赞,處于CLOSED
狀態(tài)晦攒。
即客戶端收到服務(wù)端的連接釋放報(bào)文段后,對(duì)此發(fā)出確認(rèn)報(bào)文段(ACK=1得哆,seq=u+1脯颜,ack=w+1),客戶端進(jìn)入TIME_WAIT(時(shí)間等待)狀態(tài)贩据。此時(shí)TCP未釋放掉栋操,需要經(jīng)過(guò)時(shí)間等待計(jì)時(shí)器設(shè)置的時(shí)間2MSL后,客戶端才進(jìn)入CLOSED狀態(tài)饱亮。
收到一個(gè)FIN只意味著在這一方向上沒(méi)有數(shù)據(jù)流動(dòng)矾芙。客戶端執(zhí)行主動(dòng)關(guān)閉并進(jìn)入TIME_WAIT是正常的,服務(wù)端通常執(zhí)行被動(dòng)關(guān)閉近上,不會(huì)進(jìn)入TIME_WAIT狀態(tài)剔宪。
在socket編程中,任何一方執(zhí)行close()操作即可產(chǎn)生揮手操作壹无。
]
2.1 揮手為什么需要四次葱绒?
因?yàn)楫?dāng)服務(wù)端收到客戶端的SYN連接請(qǐng)求報(bào)文后,可以直接發(fā)送SYN+ACK報(bào)文斗锭。其中ACK報(bào)文是用來(lái)應(yīng)答的地淀,SYN報(bào)文是用來(lái)同步的。但是關(guān)閉連接時(shí)岖是,當(dāng)服務(wù)端收到FIN報(bào)文時(shí)帮毁,很可能并不會(huì)立即關(guān)閉SOCKET,所以只能先回復(fù)一個(gè)ACK報(bào)文豺撑,告訴客戶端烈疚,“你發(fā)的FIN報(bào)文我收到了”。只有等到我服務(wù)端所有的報(bào)文都發(fā)送完了前硫,我才能發(fā)送FIN報(bào)文胞得,因此不能一起發(fā)送。故需要四次揮手。
2.2 2MSL等待狀態(tài)
TIME_WAIT狀態(tài)也成為2MSL等待狀態(tài)阶剑。每個(gè)具體TCP實(shí)現(xiàn)必須選擇一個(gè)報(bào)文段最大生存時(shí)間MSL(Maximum Segment Lifetime)跃巡,它是任何報(bào)文段被丟棄前在網(wǎng)絡(luò)內(nèi)的最長(zhǎng)時(shí)間。這個(gè)時(shí)間是有限的牧愁,因?yàn)門(mén)CP報(bào)文段以IP數(shù)據(jù)報(bào)在網(wǎng)絡(luò)內(nèi)傳輸素邪,而IP數(shù)據(jù)報(bào)則有限制其生存時(shí)間的TTL字段。
對(duì)一個(gè)具體實(shí)現(xiàn)所給定的MSL值猪半,處理的原則是:當(dāng)TCP執(zhí)行一個(gè)主動(dòng)關(guān)閉兔朦,并發(fā)回最后一個(gè)ACK,該連接必須在TIME_WAIT狀態(tài)停留的時(shí)間為2倍的MSL磨确。這樣可讓TCP再次發(fā)送最后的ACK以防這個(gè)ACK丟失(另一端超時(shí)并重發(fā)最后的FIN)沽甥。
這種2MSL等待的另一個(gè)結(jié)果是這個(gè)TCP連接在2MSL等待期間,定義這個(gè)連接的插口(客戶的IP地址和端口號(hào)乏奥,服務(wù)器的IP地址和端口號(hào))不能再被使用摆舟。這個(gè)連接只能在2MSL結(jié)束后才能再被使用。
2.3 四次揮手釋放連接時(shí)邓了,等待2MSL的意義?
MSL是Maximum Segment Lifetime的英文縮寫(xiě)恨诱,可譯為“最長(zhǎng)報(bào)文段壽命”,它是任何報(bào)文在網(wǎng)絡(luò)上存在的最長(zhǎng)時(shí)間骗炉,超過(guò)這個(gè)時(shí)間報(bào)文將被丟棄照宝。
為了保證客戶端發(fā)送的最后一個(gè)ACK報(bào)文段能夠到達(dá)服務(wù)器。因?yàn)檫@個(gè)ACK有可能丟失句葵,從而導(dǎo)致處在LAST-ACK狀態(tài)的服務(wù)器收不到對(duì)FIN-ACK的確認(rèn)報(bào)文厕鹃。服務(wù)器會(huì)超時(shí)重傳這個(gè)FIN-ACK,接著客戶端再重傳一次確認(rèn)笼呆,重新啟動(dòng)時(shí)間等待計(jì)時(shí)器
熊响。最后客戶端和服務(wù)器都能正常的關(guān)閉旨别。假設(shè)客戶端不等待2MSL诗赌,而是在發(fā)送完ACK之后直接釋放關(guān)閉,一但這個(gè)ACK丟失的話秸弛,服務(wù)器就無(wú)法正常的進(jìn)入關(guān)閉連接狀態(tài)铭若。
兩個(gè)理由:
-
保證客戶端發(fā)送的最后一個(gè)ACK報(bào)文段能夠到達(dá)服務(wù)端。
這個(gè)ACK報(bào)文段有可能丟失递览,使得處于LAST-ACK狀態(tài)的B收不到對(duì)已發(fā)送的FIN+ACK報(bào)文段的確認(rèn)叼屠,服務(wù)端超時(shí)重傳FIN+ACK報(bào)文段,而客戶端能在2MSL時(shí)間內(nèi)收到這個(gè)重傳的FIN+ACK報(bào)文段绞铃,接著客戶端重傳一次確認(rèn)镜雨,重新啟動(dòng)2MSL計(jì)時(shí)器,最后客戶端和服務(wù)端都進(jìn)入到CLOSED狀態(tài)儿捧,若客戶端在TIME-WAIT狀態(tài)不等待一段時(shí)間荚坞,而是發(fā)送完ACK報(bào)文段后立即釋放連接挑宠,則無(wú)法收到服務(wù)端重傳的FIN+ACK報(bào)文段,所以不會(huì)再發(fā)送一次確認(rèn)報(bào)文段颓影,則服務(wù)端無(wú)法正常進(jìn)入到CLOSED狀態(tài)各淀。
-
防止“已失效的連接請(qǐng)求報(bào)文段”出現(xiàn)在本連接中。
客戶端在發(fā)送完最后一個(gè)ACK報(bào)文段后诡挂,再經(jīng)過(guò)2MSL碎浇,就可以使本連接持續(xù)的時(shí)間內(nèi)所產(chǎn)生的所有報(bào)文段都從網(wǎng)絡(luò)中消失,使下一個(gè)新的連接中不會(huì)出現(xiàn)這種舊的連接請(qǐng)求報(bào)文段璃俗。
2.4 為什么TIME_WAIT狀態(tài)需要經(jīng)過(guò)2MSL才能返回到CLOSE狀態(tài)奴璃?
理論上,四個(gè)報(bào)文都發(fā)送完畢城豁,就可以直接進(jìn)入CLOSE狀態(tài)了溺健,但是可能網(wǎng)絡(luò)是不可靠的,有可能最后一個(gè)ACK丟失钮蛛。所以TIME_WAIT狀態(tài)就是用來(lái)重發(fā)可能丟失的ACK報(bào)文鞭缭。
3. 總結(jié)
《TCP/IP詳解 卷1:協(xié)議》有一張TCP狀態(tài)變遷圖,很具有代表性魏颓,有助于大家理解三次握手和四次揮手的狀態(tài)變化岭辣。如下圖所示,粗的實(shí)線箭頭表示正常的客戶端狀態(tài)變遷甸饱,粗的虛線箭頭表示正常的服務(wù)器狀態(tài)變遷沦童。