TCP ------ TCP四次挥手(断开连接)及断开过程
1、正常情況下,調(diào)用close(),產(chǎn)生的其中一個(gè)效果就是發(fā)送FIN,只有雙方都調(diào)用close(),才會(huì)出現(xiàn)正常的四次揮手。
2、如果是服務(wù)器,發(fā)起四次揮手是在關(guān)閉accept()返回的套接字,而不是socket()返回的套接字
3、Initiator=client,Receiver=server 情況:如果是服務(wù)器進(jìn)入CLOSE_WAIT,而不發(fā)送FIN的話(也就是不調(diào)用close()),重新創(chuàng)建服務(wù)器需要等待一段時(shí)間bind才能成功,這個(gè)時(shí)間就是客戶端FIN_WAIT_2的超時(shí)時(shí)間,超時(shí)后客戶端發(fā)送RST給服務(wù)器。所以客戶端close(),服務(wù)器必須也執(zhí)行close()
4、主動(dòng)調(diào)用 close() 一方才會(huì)進(jìn)入 TIME_WAIT
5、調(diào)用shutdown()并不會(huì)啟動(dòng)四次揮手
斷開(kāi)為什么需要四次握手:
TCP協(xié)議是一種面向連接的、可靠的、基于字節(jié)流的運(yùn)輸層通信協(xié)議。TCP是全雙工模式,這就意味著,當(dāng)主機(jī)1發(fā)出FIN報(bào)文段時(shí),只是表示主機(jī)1已經(jīng)沒(méi)有數(shù)據(jù)要發(fā)送了,主機(jī)1告訴主機(jī)2,它的數(shù)據(jù)已經(jīng)全部發(fā)送完畢了;但是,這個(gè)時(shí)候主機(jī)1還是可以接受來(lái)自主機(jī)2的數(shù)據(jù);當(dāng)主機(jī)2返回ACK報(bào)文段時(shí),表示它已經(jīng)知道主機(jī)1沒(méi)有數(shù)據(jù)發(fā)送了,但是主機(jī)2還是可以發(fā)送數(shù)據(jù)到主機(jī)1的;當(dāng)主機(jī)2也發(fā)送了FIN報(bào)文段時(shí),這個(gè)時(shí)候就表示主機(jī)2也沒(méi)有數(shù)據(jù)要發(fā)送了,就會(huì)告訴主機(jī)1,我也沒(méi)有數(shù)據(jù)要發(fā)送了,之后彼此就會(huì)愉快的中斷這次TCP連接。
為什么TIME_WAIT狀態(tài)需要經(jīng)過(guò)2MSL(最大報(bào)文段生存時(shí)間)才能到CLOSE狀態(tài)
注:主動(dòng)關(guān)閉一方會(huì)進(jìn)入TIME_WAIT狀態(tài),以下假設(shè)Client是主動(dòng)方
1、如果Client直接CLOSED了,那么由于IP協(xié)議的不可靠性或者是其它網(wǎng)絡(luò)原因,導(dǎo)致Server沒(méi)有收到Client最后回復(fù)的ACK。那么Server就會(huì)在超時(shí)之后繼續(xù)發(fā)送FIN,此時(shí)由于Client已經(jīng)CLOSED了,就找不到與重發(fā)的FIN對(duì)應(yīng)的連接,最后Server就會(huì)收到RST而不是ACK,Server就會(huì)以為是連接錯(cuò)誤把問(wèn)題報(bào)告給高層。這樣的情況雖然不會(huì)造成數(shù)據(jù)丟失,但是卻導(dǎo)致TCP協(xié)議不符合可靠連接的要求。所以,Client不是直接進(jìn)入CLOSED,而是要保持TIME_WAIT,保證能再次接收 FIN 并發(fā)送 ACK。(猜測(cè): FIN超時(shí)時(shí)間應(yīng)該小于MSL,否則Client等待2MSL,也可能接收不到FIN)
2、如果Client直接CLOSED,然后又再向Server發(fā)起一個(gè)新連接,我們不能保證這個(gè)新連接與剛關(guān)閉的連接的端口號(hào)是不同的。也就是說(shuō)有可能新連接和老連接的端口號(hào)是相同的。一般來(lái)說(shuō)不會(huì)發(fā)生什么問(wèn)題,但是還是有特殊情況出現(xiàn):假設(shè)新連接和已經(jīng)關(guān)閉的老連接端口號(hào)是一樣的,如果前一次連接的某些數(shù)據(jù)仍然滯留在網(wǎng)絡(luò)中,這些延遲數(shù)據(jù)在建立新連接之后才到達(dá)Server,由于新連接和老連接的端口號(hào)是一樣的,又因?yàn)門(mén)CP協(xié)議判斷不同連接的依據(jù)是socket pair,于是,TCP協(xié)議就認(rèn)為那個(gè)延遲的數(shù)據(jù)是屬于新連接的,這樣就和真正的新連接的數(shù)據(jù)包發(fā)生混淆了。所以TCP連接還要在TIME_WAIT狀態(tài)等待2倍MSL,這樣可以保證本次連接的所有數(shù)據(jù)都從網(wǎng)絡(luò)中消失。
服務(wù)端為了解決這個(gè)TIME_WAIT問(wèn)題,可選擇的方式有三種:
Ø 服務(wù)器關(guān)閉的時(shí)候使用RST的方式(這是網(wǎng)友說(shuō)的,我使用ESP8266做服務(wù)器,發(fā)送RST《發(fā)送RST的原因是調(diào)用 close() 時(shí)接收緩沖區(qū)還有未讀完的數(shù)據(jù)》還是會(huì)等待2MSL)
Ø 服務(wù)器close()發(fā)送RST而不是FIN
Ø 在創(chuàng)建socket,bind之前使能SO_REUSEADDR,然后close()(如果客戶端斷網(wǎng)了,假如服務(wù)器端沒(méi)有keepalive(或者心跳包),服務(wù)器端沒(méi)法知道客戶端斷網(wǎng)了,也就沒(méi)法執(zhí)行close()函數(shù),而是傻傻繼續(xù)等待客戶端的數(shù)據(jù))------- 適用于服務(wù)器不用檢測(cè)客戶端是否存在
Ø 如果使用keepalive,客戶端斷開(kāi)網(wǎng)絡(luò)(比如客戶端被拔了網(wǎng)線),服務(wù)器的keepalive機(jī)制可以檢測(cè)到,read()返回失敗,服務(wù)器再次創(chuàng)建時(shí)不需要等待2MSL ------- 適用于服務(wù)器需要檢測(cè)客戶端是否存在
對(duì)于TIME_WAIT的插曲:
當(dāng)建立一個(gè)TCP連接時(shí),服務(wù)器端會(huì)繼續(xù)用原有端口監(jiān)聽(tīng),同時(shí)用這個(gè)端口與客戶端通信。而客戶端默認(rèn)情況下會(huì)使用一個(gè)隨機(jī)端口與服務(wù)器端的監(jiān)聽(tīng)端口通信。有時(shí)候,為了服務(wù)器端的安全性,我們需要對(duì)客戶端進(jìn)行驗(yàn)證,即限定某個(gè)IP某個(gè)特定端口的客戶端。客戶端可以使用bind來(lái)使用特定的端口。對(duì)于服務(wù)器端,當(dāng)設(shè)置了SO_REUSEADDR選項(xiàng)時(shí),它可以在2MSL內(nèi)啟動(dòng)并listen成功。但是對(duì)于客戶端,當(dāng)使用bind并設(shè)置SO_REUSEADDR時(shí),如果在2MSL內(nèi)啟動(dòng),雖然bind會(huì)成功,但是在windows平臺(tái)上connect會(huì)失敗。而在linux上則不存在這個(gè)問(wèn)題。(我的實(shí)驗(yàn)平臺(tái):winxp, ubuntu7.10)
要解決windows平臺(tái)的這個(gè)問(wèn)題,可以設(shè)置SO_LINGER選項(xiàng)。SO_LINGER選項(xiàng)決定調(diào)用close時(shí)TCP的行為。SO_LINGER涉及到linger結(jié)構(gòu)體,如果設(shè)置結(jié)構(gòu)體中l(wèi)_onoff為非0,l_linger為0,那么調(diào)用close時(shí)TCP連接會(huì)立刻斷開(kāi),TCP不會(huì)將發(fā)送緩沖中未發(fā)送的數(shù)據(jù)發(fā)送,而是立即發(fā)送一個(gè)RST報(bào)文給對(duì)方,這個(gè)時(shí)候TCP連接就不會(huì)進(jìn)入TIME_WAIT狀態(tài)。如你所見(jiàn),這樣做雖然解決了問(wèn)題,但是并不安全。通過(guò)以上方式設(shè)置SO_LINGER狀態(tài),等同于設(shè)置SO_DONTLINGER狀態(tài)。
套接字 SO_LINGER 選項(xiàng)可以規(guī)定 close() 的行為:
l_onoff為0,則該選項(xiàng)關(guān)閉,l_linger的值被忽略,等于缺省情況,close立即返回;
l_onoff為非0,l_linger為0,則套接口關(guān)閉TCP連接時(shí),TCP將丟棄保留在套接口發(fā)送緩沖區(qū)中的任何數(shù)據(jù)并發(fā)送一個(gè)RST給對(duì)方,而不是通常的四分組終止序列,這避免了TIME_WAIT狀態(tài);
l_onoff為非0,l_linger為非0,當(dāng)套接口關(guān)閉時(shí)內(nèi)核將拖延一段時(shí)間(由l_linger決定)。如果套接口緩沖區(qū)中仍殘留數(shù)據(jù),進(jìn)程將處于睡眠狀態(tài),直到(a)所有數(shù)據(jù)發(fā)送完且被對(duì)方確認(rèn),之后進(jìn)行正常的終止序列(描述字訪問(wèn)計(jì)數(shù)為0)或(b)延遲時(shí)間到。此種情況下,應(yīng)用程序檢查close的返回值是非常重要的,如果在數(shù)據(jù)發(fā)送完并被確認(rèn)前時(shí)間到,close將返回EWOULDBLOCK錯(cuò)誤且套接口發(fā)送緩沖區(qū)中的任何數(shù)據(jù)都丟失。close的成功返回僅告訴我們發(fā)送的數(shù)據(jù)(和FIN)已由對(duì)方TCP確認(rèn),它并不能告訴我們對(duì)方應(yīng)用進(jìn)程是否已讀了數(shù)據(jù)。如果套接口設(shè)為非阻塞的,它將不等待close完成
總結(jié)
以上是生活随笔為你收集整理的TCP ------ TCP四次挥手(断开连接)及断开过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 剪枝策略
- 下一篇: 《激荡三十年》十、价格改革(下)——物价