摘要:我們知道通過三次握手建立可靠連接,通過四次揮手斷開連接,連接是比較昂貴的資源。從上分析,安全可靠的斷開連接至少需要四次,再多一次的意義不大。連接的異常斷開以上都是在理想的情況下發(fā)生的,理想狀態(tài)下,一個連接可以被長期保持。
我們知道TCP通過三次握手建立可靠連接,通過四次揮手斷開連接,TCP連接是比較昂貴的資源。為什么TCP需要通過三次握手才能建立可靠的連接?兩次不行么?斷開連接為什么需要四次?TCP連接昂貴在哪里?三次握手
客戶端:“喂,聽得到嗎?”
服務(wù)端:“我能聽到,你能聽到我嗎?”
客戶端:“恩,能聽到?!?/pre>為什么需要三次握手,對客戶端而言,再收到服務(wù)端的ACK后,能確定我發(fā)的消息服務(wù)端能收到,服務(wù)端發(fā)的消息我也能收到了,那為什么還要第三次握手?這要從服務(wù)端考慮,服務(wù)端在接收到SYN后只能確定自己能收到客戶端發(fā)來的消息,如果沒有第三次握手,服務(wù)端是不確定對方是否能接收到自己這邊發(fā)送的消息的,這種不確定勢必影響到了信道的可靠性。既然三次就已經(jīng)確保了信道的可靠性,如果在加一次肯定就增加了網(wǎng)絡(luò)消耗從而影響了建立連接的效率。
四次揮手客戶端:“不說了,掛了吧。”
服務(wù)端:“OK!”
服務(wù)端:“你要注意身體啊!”
服務(wù)端:“拜拜!”
客戶端:“拜拜!”斷開連接是釋放資源的過程,還是從客戶端和服務(wù)端兩個人的角度去分析揮手過程。
首先建立連接是為了可靠的數(shù)據(jù)交付,現(xiàn)在連接建立已經(jīng)有一段時間了,客戶端說數(shù)據(jù)已經(jīng)發(fā)完了,已經(jīng)沒什么要發(fā)送了,于是告訴操作系統(tǒng),嘿,老兄,我數(shù)據(jù)已經(jīng)發(fā)完了,你可以把我的發(fā)送資源釋放啦,于是操作系統(tǒng)鎖住了發(fā)送資源(比如發(fā)送隊列)準備釋放,并標記了TCP連接狀態(tài)為FIN_WAIT_1,由于數(shù)據(jù)發(fā)送是雙方的事情,客戶端這邊的發(fā)送資源已經(jīng)釋放,客戶端有義務(wù)告知服務(wù)端這邊的數(shù)據(jù)已經(jīng)發(fā)送完畢,所以操作系統(tǒng)會發(fā)送一條FIN消息到服務(wù)端,告知服務(wù)端可以釋放接收資源了,為了保證服務(wù)端確實收到了FIN消息并釋放了接收資源,服務(wù)端也需要返回一條ACK消息給客戶端,如果客戶端沒收到ACK消息,則重試剛剛的FIN消息。客戶端一旦收到ACK消息,則說明服務(wù)端已經(jīng)釋放了接收資源,操作系統(tǒng)將TCP連接狀態(tài)改為FIN_WAIT_2。到這里TCP連接已經(jīng)關(guān)閉一半。
上面的過程只是結(jié)束了客戶端的數(shù)據(jù)發(fā)送,釋放了發(fā)送數(shù)據(jù)需要的資源,但是客戶端依然可以接收從服務(wù)端發(fā)來的數(shù)據(jù),服務(wù)端只是結(jié)束了數(shù)據(jù)接收并釋放相關(guān)資源,依然可以放數(shù)據(jù),因為服務(wù)端處理完接收的數(shù)據(jù)后要反饋結(jié)果給客戶端。等結(jié)果反饋完后,沒有數(shù)據(jù)要處理了,服務(wù)端也要結(jié)束發(fā)送過程,同樣也得告知客戶端讓其釋放接收數(shù)據(jù)所需要的資源。服務(wù)端重復(fù)上面的過程。但不同的是,客戶端接收到FIN消息并返回ACK消息后需要等一段時間,這是由于擔心服務(wù)端沒有收到ACK又重發(fā)了FIN消息。等過了一段時間后并沒有收到重發(fā)的消息,客戶端就會釋放所有資源(這里就不管服務(wù)端到底有沒有收到ACK了,如果一直管下去就是個死循環(huán))。服務(wù)端也是一樣,重試多次以后也就釋放了所有資源(這里不清楚到底是不是釋放了資源,也有可能有其他機制)。
從上分析,安全可靠的斷開連接至少需要四次,再多一次的意義不大。
昂貴的資源上面分析可知,三次握手和四次揮手無疑會造成巨大的網(wǎng)絡(luò)資源和CPU資源的消耗(如果連接沒有被復(fù)用,這就是一種浪費),另外,客戶端和服務(wù)端分別要維護各自的發(fā)送和接收緩存,各自在操作系統(tǒng)里面的變量(比如文件描述符,操作系統(tǒng)維護的一套數(shù)據(jù)結(jié)構(gòu)),在阻塞式的網(wǎng)絡(luò)模型中,服務(wù)端還要開啟線程來處理這條連接。所以說TCP連接是比較昂貴的資源,需要連接池這種技術(shù)來提高它的復(fù)用性。TCP連接的異常斷開以上都是在理想的情況下發(fā)生的,理想狀態(tài)下,一個TCP連接可以被長期保持。但是現(xiàn)實總是很骨感,在保持TCP連接的過程中很可能出現(xiàn)各種意外的情況,比如網(wǎng)絡(luò)故障,客戶端崩潰或者異常重啟,在這種情況下,如果服務(wù)端沒有及時清理這些連接,服務(wù)端將發(fā)生連接泄露,直至服務(wù)端資源耗盡拒絕提供服務(wù)(connection refused exception)。因此在實際應(yīng)用中,服務(wù)器端需要采取相應(yīng)的方法來探測TCP連接是否已經(jīng)斷連。探測的原理就是心跳機制,可以是應(yīng)用層面的心跳,也可以是第三方的心跳,但是絕大部分類Unix系統(tǒng)均在TCP中提供了相應(yīng)的心跳檢測功能(雖然并不是TCP規(guī)范中的一部分)。客戶端程序崩潰或異常退出當客戶端程序因未知原因崩潰或異常退出后,操作系統(tǒng)會給服務(wù)端發(fā)送一條RST消息,阻塞模型下,服務(wù)端內(nèi)核無法主動通知應(yīng)用層出錯,只有應(yīng)用層主動調(diào)用read()或者write()這樣的IO系統(tǒng)調(diào)用時,內(nèi)核才會利用出錯來通知應(yīng)用層對端RST(Linux系統(tǒng)報Connection reset by peer)。非阻塞模型下,服務(wù)端select或者epoll會返回sockfd可讀,應(yīng)用層對其進行讀取時,read()會報錯RST。
哪些情況下,會收到來自對端的RST消息呢。
connect一個不存在的端口,客戶端會收到一條RST,報錯Connection refused;
程序崩潰或異常退出,會向?qū)Χ税l(fā)送。
對端斷電重啟,send數(shù)據(jù)時會收到來自對端的RST。
close(sockfd)時,直接丟棄接收緩沖區(qū)未讀取的數(shù)據(jù),并給對方發(fā)一個RST。這個是由SO_LINGER選項來控制的;
TCP socket在任何狀態(tài)下,只要收到RST包,即可釋放連接資源。
客戶端斷電或網(wǎng)絡(luò)異常如果客戶端斷電或網(wǎng)絡(luò)異常,并且連接通道內(nèi)沒有任何數(shù)據(jù)交互,服務(wù)端是感知不到客戶端掉線的,此時需要借助心跳機制來感知這種狀況,一般的做法是,服務(wù)端往對端發(fā)送一個心跳包并啟動一個超時定時器,如果能正確收到對端的回應(yīng),說明在線,如果超時,可以進行一系列操作,比如重試、關(guān)閉連接等等。
keep alive or heart beart借鑒一下大神的文章Socket感知連接斷開 正常情況很多人都知道TCP并不會去主動檢測連接的丟失,這意味著,如果雙方不產(chǎn)生交互,那么如果網(wǎng)絡(luò)斷了或者有一方機器崩潰,另外一方將永遠不知道連接已經(jīng)不可用了。檢測連接是否丟失的方法大致有兩種:keepalive和heart-beat。
Keepalive是很多的TCP實現(xiàn)提供的一種機制,它允許連接在空閑的時候雙方會發(fā)送一些特殊的數(shù)據(jù)段,并通過響應(yīng)與否來判斷連接是否還存活著(所謂keep~~alive)。我曾經(jīng)寫過一篇關(guān)于keepalive的blog ,但后來我也發(fā)現(xiàn),其實keepalive在實際的應(yīng)用中并不常見。為何如此?這得歸結(jié)于keepalive設(shè)計的初衷。Keepalive適用于清除死亡時間比較長的連接。
比如這樣的場景:一個用戶創(chuàng)建tcp連接訪問了一個web服務(wù)器,當用戶完成他執(zhí)行的操作后,很粗暴的直接撥了網(wǎng)線。這種情況下,這個tcp連接已經(jīng)斷開了,但是web服務(wù)器并不知道,它會依然守護著這個連接。如果web server設(shè)置了keepalive,那么它就能夠在用戶斷開網(wǎng)線的大概幾個小時以后,確認這個連接已經(jīng)中斷,然后丟棄此連接,回收資源。
采用keepalive,它會先要求此連接一定時間沒有活動(一般是幾個小時),然后發(fā)出數(shù)據(jù)段,經(jīng)過多次嘗試后(每次嘗試之間也有時間間隔),如果仍沒有響應(yīng),則判斷連接中斷??上攵?,整個周期需要很長的時間。
所以,如前面的場景那樣,需要一種方法能夠清除和回收那些在系統(tǒng)不知情的情況下死去了很久的連接,keepalive是非常好的選擇。
但是,在大部分情況下,特別是分布式環(huán)境中,我們需要的是一個能夠快速或者實時監(jiān)控連接狀態(tài)的機制,這里,heart-beat才是更加合適的方案。
Heart-beat(心跳),按我的理解,它的原理和keepalive非常類似,都是發(fā)送一個信號給對方,如果多次發(fā)送都沒有響應(yīng)的話,則判斷連接中斷。它們的不同點在于,keepalive是tcp實現(xiàn)中內(nèi)建的機制,是在創(chuàng)建tcp連接時通過設(shè)置參數(shù)啟動keepalive機制;而heart-beat則需要在tcp之上的應(yīng)用層實現(xiàn)。一個簡單的heart-beat實現(xiàn)一般測試連接是否中斷采用的時間間隔都比較短,可以很快的決定連接是否中斷。并且,由于是在應(yīng)用層實現(xiàn),因為可以自行決定當判斷連接中斷后應(yīng)該采取的行為,而keepalive在判斷連接失敗后只會將連接丟棄。
關(guān)于heart-beat,一個非常有趣的問題是,應(yīng)該在傳輸真正數(shù)據(jù)的連接中發(fā)送心跳信號,還是可以專門創(chuàng)建一個發(fā)送“心跳”信號的連接。比如說,A,B兩臺機器之間通過連接m來傳輸數(shù)據(jù),現(xiàn)在為了能夠檢測A,B之間的連接狀態(tài),我們是應(yīng)該在連接m中傳輸心跳信號,還是創(chuàng)建新的連接n來專門傳輸心跳呢?我個人認為兩者皆可。如果擔心的是端到端的連接狀態(tài),那么就直接在該條連接中實現(xiàn)心跳。但很多時候,關(guān)注的是網(wǎng)絡(luò)狀況和兩臺主機間的連接狀態(tài),這種情況下, 創(chuàng)建專門的心跳連接也未嘗不可。客戶端正常關(guān)閉連接:
//發(fā)送FIN消息,說明客戶端已經(jīng)沒有數(shù)據(jù)發(fā)送,服務(wù)端read時會返回-1或者null socket.shutdownOutput(); //默認的SO_LINGER參數(shù),客戶端發(fā)送FIN消息,服務(wù)端read時會返回-1或者null socket.close(); //設(shè)置了立即關(guān)閉,客戶端發(fā)送RST消息,服務(wù)端`read`時會報`connection rest by peer`。 socket.close();非正常情況客戶端程序崩潰或異常退出:服務(wù)端read時會報connection rest by peer。
斷電重啟:服務(wù)端發(fā)送心跳信息時,會收到客戶端的RST消息,調(diào)用read時會報connection rest by peer。
斷電或網(wǎng)絡(luò)中斷:服務(wù)端發(fā)送心跳信息后超時。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/72749.html
摘要:協(xié)議的定義屬于傳輸層協(xié)議,提供了一種端到端的連接。如果兩次校驗和一致說明數(shù)據(jù)是正確的,否則將認為數(shù)據(jù)被破壞,接收端將丟棄該數(shù)據(jù)緊急指針盡在緊急控制位為時有效。結(jié)尾是一個非常復(fù)雜的協(xié)議,這里稍微總結(jié)了一下,隨著學(xué)習時間增加,繼續(xù)補充。 TCP協(xié)議的定義 TCP屬于傳輸層協(xié)議,提供了一種端到端的連接。 是面向連接的、可靠的的進程到進程通信的協(xié)議 TCP提供全雙工服務(wù),即數(shù)據(jù)可在同一時間雙...
摘要:很多人都知道協(xié)議是基于協(xié)議創(chuàng)造出來的采用文本方式傳輸非二進制傳輸?shù)膽?yīng)用層協(xié)議,協(xié)議是傳輸層協(xié)議,主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸,而應(yīng)用層協(xié)議,主要解決如何包裝和規(guī)范數(shù)據(jù)。你也可以自己定義應(yīng)用層協(xié)議,只不過所有配套的東西都要自己重新造輪子。 從問題切入能幫我們更好地理解晦澀難懂的概念。很多人都知道http協(xié)議是基于Tcp協(xié)議創(chuàng)造出來的采用文本方式傳輸(非二進制傳輸)的應(yīng)用層協(xié)議,TPC/I...
閱讀 780·2023-04-25 20:47
閱讀 2546·2019-08-30 15:53
閱讀 955·2019-08-26 14:05
閱讀 901·2019-08-26 11:59
閱讀 1689·2019-08-26 11:43
閱讀 1688·2019-08-26 10:57
閱讀 1366·2019-08-23 18:23
閱讀 2678·2019-08-23 12:57