TCP串連的狀態詳解以及故障排查

來源:互聯網
上載者:User

TCP串連的狀態詳解以及故障排查

我們通過瞭解TCP各個狀態,可以排除和定位網路或系統故障時大有協助。(總結網路上的內容)

1、TCP狀態

LISTENING:偵聽來自遠方的TCP連接埠的串連請求. 首先服務端需要開啟一個socket進行監聽,狀態為LISTEN。 有提供某種服務才會處於LISTENING狀態,TCP狀態變化就是某個連接埠的狀態變化,提供一個服務就開啟一個連接埠,例如:提供www服務預設開的是80連接埠,提供ftp服務預設的連接埠為21,當提供的服務沒有被串連時就處於LISTENING狀態。FTP服務啟動後首先處於偵聽(LISTENING)狀態。處於偵聽LISTENING狀態時,該連接埠是開放的,等待串連,但還沒有被串連。就像你房子的門已經敞開的,但還沒有人進來。
看LISTENING狀態最主要的是看本機開了哪些連接埠,這些連接埠都是哪個程式開的,關閉不必要的連接埠是保證安全的一個非常重要的方面,服務連接埠都對應一個服務(應用程式),停止該服務就關閉了該連接埠,例如要關閉21連接埠只要停止IIS服務中的FTP服務即可。關於這方面的知識請參閱其它文章。
如果你不幸中了服務連接埠的木馬,木馬也開個連接埠處於LISTENING狀態。

SYN-SENT:用戶端SYN_SENT狀態: 再發送串連請求後等待匹配的串連請求:用戶端通過應用程式調用connect進行active open.於是用戶端tcp發送一個SYN以請求建立一個串連.之後狀態置為SYN_SENT. /*The socket is actively attempting to establish a connection. 在發送串連請求後等待匹配的串連請求 */
當請求串連時用戶端首先要發送同步訊號給要訪問的機器,此時狀態為SYN_SENT,如果串連成功了就變為ESTABLISHED,正常情況下SYN_SENT狀態非常短暫。例如要訪問網站http://www.baidu.com,如果是正常串連的話,用TCPView觀察IEXPLORE.EXE(IE)建立的串連會發現很快從SYN_SENT變為ESTABLISHED,表示串連成功。SYN_SENT狀態快的也許看不到。
如果發現有很多SYN_SENT出現,那一般有這麼幾種情況,一是你要訪問的網站不存在或線路不好,二是用掃描軟體掃描一個網段的機器,也會出出現很多SYN_SENT,另外就是可能中了病毒了,例如中了"衝擊波",病毒發作時會掃描其它機器,這樣會有很多SYN_SENT出現。

SYN-RECEIVED:伺服器端狀態SYN_RCVD 再收到和發送一個串連請求後等待對方對串連請求的確認 當伺服器收到用戶端發送的同步訊號時,將標誌位ACK和SYN置1發送給用戶端,此時伺服器端處於SYN_RCVD狀態,如果串連成功了就變為ESTABLISHED,正常情況下SYN_RCVD狀態非常短暫。
如果發現有很多SYN_RCVD狀態,那你的機器有可能被SYN Flood的DoS(拒絕服務的攻擊)攻擊了。 SYN Flood的攻擊原理是: 在進行三向交握時,攻擊軟體向被攻擊的伺服器發送SYN串連請求(握手的第一步),但是這個地址是偽造的,如攻擊軟體隨機偽造了51.133.163.104、65.158.99.152等等地址。伺服器在收到串連請求時將標誌位ACK和SYN置1發送給用戶端(握手的第二步),但是這些用戶端的IP地址都是偽造的,伺服器根本找不到客戶機,也就是說握手的第三步不可能完成。 這種情況下伺服器端一般會重試(再次發送SYN+ACK給用戶端)並等待一段時間後丟棄這個未完成的串連,這段時間的長度我們稱為SYN Timeout,一般來說這個時間是分鐘的數量級(大約為30秒-2分鐘);一個使用者出現異常導致伺服器的一個線程等待1分鐘並不是什麼很大的問題,但如果有一個惡意的攻擊者大量類比這種情況,伺服器端將為了維護一個非常大的半串連列表而消耗非常多的資源----數以萬計的半串連,即使是簡單的儲存並遍曆也會消耗非常多的CPU時間和記憶體,何況還要不斷對這個列表中的IP進行SYN+ACK的重試。此時從正常客戶的角度看來,伺服器失去響應,這種情況我們稱做: 伺服器端受到了SYN Flood攻擊(SYN洪水攻擊

ESTABLISHED:代表一個開啟的串連。 ESTABLISHED狀態是表示兩台機器正在傳輸資料,觀察這個狀態最主要的就是看哪個程式正在處於ESTABLISHED狀態。
伺服器出現很多ESTABLISHED狀態: netstat -nat |grep 9502或者使用lsof -i:9502可以檢測到。 當用戶端未主動close的時候就中斷連線:即用戶端發送的FIN丟失或未發送。

這時候若用戶端斷開的時候發送了FIN包,則服務端將會處於CLOSE_WAIT狀態;

這時候若用戶端斷開的時候未發送FIN包,則服務端處還是顯示ESTABLISHED狀態;

結果用戶端重新串連伺服器。

而新串連上來的用戶端(也就是剛才斷掉的重新連上來了)在服務端肯定是ESTABLISHED; 如果用戶端重複的上演這種情況,那麼服務端將會出現大量的假的ESTABLISHED串連和CLOSE_WAIT串連。

最終結果就是新的其他用戶端無法串連上來,但是利用netstat還是能看到一條串連已經建立,並顯示ESTABLISHED,但始終無法進入程式碼。

FIN-WAIT-1:等待遠程TCP串連插斷要求,或先前的串連插斷要求的確認 主動關閉(active close)端應用程式調用close,於是其TCP發出FIN請求主動關閉串連,之後進入FIN_WAIT1狀態./* The socket is closed, and the connection is shutting down. 等待遠程TCP的串連插斷要求,或先前的串連插斷要求的確認 */

FIN-WAIT-2:從遠程TCP等待串連插斷要求 主動關閉端接到ACK後,就進入了FIN-WAIT-2 ./* Connection is closed, and the socket is waiting for a shutdown from the remote end. 從遠程TCP等待串連插斷要求 */ 這就是著名的半關閉的狀態了,這是在關閉串連時,用戶端和伺服器兩次握手之後的狀態。在這個狀態下,應用程式還有接受資料的能力,但是已經無法發送資料,但是也有一種可能是,用戶端一直處於FIN_WAIT_2狀態,而伺服器則一直處於WAIT_CLOSE狀態,而直到應用程式層來決定關閉這個狀態。
CLOSE-WAIT:等待從本機使用者發來的串連插斷要求 被動關閉(passive close)端TCP接到FIN後,就發出ACK以回應FIN請求(它的接收也作為檔案結束符傳遞給上層應用程式),並進入CLOSE_WAIT. /* The remote end has shut down, waiting for the socket to close. 等待從本機使用者發來的串連插斷要求 */

CLOSING:等待遠程TCP對串連中斷的確認 比較少見./* Both sockets are shut down but we still don't have all our data sent. 等待遠程TCP對串連中斷的確認 */

LAST-ACK:等待原來的發向遠程TCP的串連插斷要求的確認 被動關閉端一段時間後,接收到檔案結束符的應用程式將調用CLOSE關閉串連。這導致它的TCP也發送一個 FIN,等待對方的ACK.就進入了LAST-ACK . /* The remote end has shut down, and the socket is closed. Waiting for acknowledgement. 等待原來發向遠程TCP的串連插斷要求的確認 */

TIME-WAIT:等待足夠的時間以確保遠程TCP接收到串連插斷要求的確認 在主動關閉端接收到FIN後,TCP就發送ACK包,並進入TIME-WAIT狀態。/* The socket is waiting after close to handle packets still in the network.等待足夠的時間以確保遠程TCP接收到串連插斷要求的確認 */ TIME_WAIT等待狀態,這個狀態又叫做2MSL狀態,說的是在TIME_WAIT2發送了最後一個ACK資料報以後,要進入TIME_WAIT狀態,這個狀態是防止最後一次握手的資料報沒有傳送到對方那裡而準備的(注意這不是四次握手,這是第四次握手的保險狀態)。這個狀態在很大程度上保證了雙方都可以正常結束,但是,問題也來了。
由於插口的2MSL狀態(插口是IP和連接埠對的意思,socket),使得應用程式在2MSL時間內是無法再次使用同一個插口的,對於客戶程式還好一些,但是對於服務程式,例如httpd,它總是要使用同一個連接埠來進行服務,而在2MSL時間內,啟動httpd就會出現錯誤(插口被使用)。為了避免這個錯誤,伺服器給出了一個平靜時間的概念,這是說在2MSL時間內,雖然可以重新啟動伺服器,但是這個伺服器還是要平靜的等待2MSL時間的過去才能進行下一次串連。 詳情請看: TIME_WAIT引起Cannot assign requested address報錯

CLOSED:沒有任何串連狀態 被動關閉端在接受到ACK包後,就進入了closed的狀態。串連結束./* The socket is not being used. 沒有任何串連狀態 */

2、TCP狀態遷移路線圖

client/server兩條路線講述TCP狀態遷移路線圖:

這是一個看起來比較複雜的狀態遷移圖,因為它包含了兩個部分---伺服器的狀態遷移和用戶端的狀態遷移,如果從某一個角度出發來看這個圖,就會清晰許多,這裡面的伺服器和用戶端都不是絕對的,發送資料的就是用戶端,接受資料的就是伺服器。 用戶端應用程式的狀態遷移圖 用戶端的狀態可以用如下的流程來表示: CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED 以上流程是在程式正常的情況下應該有的流程,從書中的圖中可以看到,在建立串連時,當用戶端收到SYN報文的ACK以後,用戶端就開啟了資料互動地串連。而結束串連則通常是用戶端主動結束的,用戶端結束應用程式以後,需要經曆FIN_WAIT_1,FIN_WAIT_2等狀態,這些狀態的遷移就是前面提到的結束串連的四次握手。
伺服器的狀態遷移圖 伺服器的狀態可以用如下的流程來表示: CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED 在建立串連的時候,伺服器端是在第三向交握之後才進入資料互動狀態,而關閉串連則是在關閉串連的第二次握手以後(注意不是第四次)。而關閉以後還要等待用戶端給出最後的ACK包才能進入初始的狀態。
其他狀態遷移 還有一些其他的狀態遷移,這些狀態遷移針對伺服器和用戶端兩方面的總結如下 LISTEN->SYN_SENT,對於這個解釋就很簡單了,伺服器有時候也要開啟串連的嘛。 SYN_SENT->SYN收到,伺服器和用戶端在SYN_SENT狀態下如果收到SYN資料報,則都需要發送SYN的ACK資料報並把自己的狀態調整到SYN收到狀態,準備進入ESTABLISHED SYN_SENT->CLOSED,在發送逾時的情況下,會返回到CLOSED狀態。 SYN_收到->LISTEN,如果受到RST包,會返回到LISTEN狀態。 SYN_收到->FIN_WAIT_1,這個遷移是說,可以不用到ESTABLISHED狀態,而可以直接跳轉到FIN_WAIT_1狀態並等待關閉。


怎樣牢牢地將這張圖刻在腦中呢?那麼你就一定要對這張圖的每一個狀態,及轉換的過程有深刻的認識,不能只停留在一知半解之中。下面對這張圖的11種狀態詳細解析一下,以便加強記憶!不過在這之前,先回顧一下TCP建立串連的三向交握過程,以及關閉串連的四次握手過程。 

3、TCP串連建立三向交握

TCP是一個連線導向的協議,所以在串連雙方發送資料之前,都需要首先建立一條串連。 Client串連Server:

當Client端調用socket函數調用時,相當於Client端產生了一個處於Closed狀態的通訊端。
( 1) 第一次握手:Client端又調用connect函數調用,系統為Client隨機分配一個連接埠,連同傳入connect中的參數(Server的IP和連接埠),這就形成了一個串連四元組,用戶端發送一個帶SYN標誌的TCP報文到伺服器。這是三向交握過程中的報文1。connect調用讓Client端的socket處於SYN_SENT狀態,等待伺服器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。

( 2)第二次握手 伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時伺服器進入SYN_RECV狀態;

( 3) 第三向交握:用戶端收到伺服器的SYN+ACK包,向伺服器發送確認包ACK(ack=k+1),此包發送完畢,用戶端和伺服器進入ESTABLISHED狀態,完成三向交握。串連已經可以進行讀寫操作。

一個完整的三向交握也就是: 請求---應答---再次確認。

TCP協議通過三個報文段完成串連的建立,這個過程稱為三向交握(three-way handshake),過程如所示。對應的函數介面:


2)Server
當Server端調用socket函數調用時,相當於Server端產生了一個處於Closed狀態的監聽通訊端
Server端調用bind操作,將監聽通訊端與指定的地址和連接埠關聯,然後又調用listen函數,系統會為其分配未完成隊列和完成隊列,此時的監聽通訊端可以接受Client的串連,監聽通訊端狀態處於LISTEN狀態。
當Server端調用accept操作時,會從完成隊列中取出一個已經完成的client串連,同時在server這段會產生一個會話通訊端,用於和client端通訊端的通訊,這個會話通訊端的狀態是ESTABLISH。

可以看出,當用戶端調用connect時,觸發了串連請求,向伺服器發送了SYN J包,這時connect進入阻塞狀態;伺服器監聽到串連請求,即收到SYN J包,調用accept函數接收請求向用戶端發送SYN K ,ACK J+1,這時accept進入阻塞狀態;用戶端收到伺服器的SYN K ,ACK J+1之後,這時connect返回,並對SYN K進行確認;伺服器收到ACK K+1時,accept返回,至此三向交握完畢,串連建立。
我們可以通過網路抓包的查看具體的流程: 比如我們伺服器開啟9502的連接埠。使用tcpdump來抓包: tcpdump -iany tcp port 9502 然後我們使用telnet 127.0.0.1 9502開串連.: telnet 127.0.0.1 9502 14:12:45.104687 IP localhost.39870 > localhost.9502: Flags [S], seq 2927179378, win 32792, options [mss 16396,sackOK,TS val 255474104 ecr 0,nop,wscale 3], length 0 (1)
14:12:45.104701 IP localhost.9502 > localhost.39870: Flags [S.], seq 1721825043, ack 2927179379, win 32768, options [mss 16396,sackOK,TS val 255474104 ecr 255474104,nop,wscale 3], length 0 (2)
14:12:45.104711 IP localhost.39870 > localhost.9502: Flags [.], ack 1, win 4099, options [nop,nop,TS val 255474104 ecr 255474104], length 0 (3)
14:13:01.415407 IP localhost.39870 > localhost.9502: Flags [P.], seq 1:8, ack 1, win 4099, options [nop,nop,TS val 255478182 ecr 255474104], length 7
14:13:01.415432 IP localhost.9502 > localhost.39870: Flags [.], ack 8, win 4096, options [nop,nop,TS val 255478182 ecr 255478182], length 0
14:13:01.415747 IP localhost.9502 > localhost.39870: Flags [P.], seq 1:19, ack 8, win 4096, options [nop,nop,TS val 255478182 ecr 255478182], length 18
14:13:01.415757 IP localhost.39870 > localhost.9502: Flags [.], ack 19, win 4097, options [nop,nop,TS val 255478182 ecr 255478182], length 0

我們看到 (1)(2)(3)三步是建立tcp: 第一次握手: 14:12:45.104687 IP localhost.39870 > localhost.9502: Flags [S], seq 2927179378 用戶端IP localhost.39870 (用戶端的連接埠一般是自動分配的) 向伺服器localhost.9502 發送syn包(syn=j)到伺服器》 syn的seq= 2927179378
第二次握手:
14:12:45.104701 IP localhost.9502 > localhost.39870: Flags [S.], seq 1721825043, ack 2927179379, 伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包
SYN(ack=j+1)=ack 2927179379 伺服器主機SYN包(syn=seq 1721825043)

第三向交握: 14:12:45.104711 IP localhost.39870 > localhost.9502: Flags [.], ack 1,
用戶端收到伺服器的SYN+ACK包,向伺服器發送確認包ACK(ack=k+1) 用戶端和伺服器進入ESTABLISHED狀態後,可以進行通訊資料互動。此時和accept介面沒有關係,即使沒有accepte,也進行3次握手完成。 串連出現串連不上的問題,一般是網路出現問題或者網卡超負荷或者是串連數已經滿啦。 紫色背景的部分: IP localhost.39870 > localhost.9502: Flags [P.], seq 1:8, ack 1, win 4099, options [nop,nop,TS val 255478182 ecr 255474104], length 7
用戶端向伺服器發送長度為7個位元組的資料,
IP localhost.9502 > localhost.39870: Flags [.], ack 8, win 4096, options [nop,nop,TS val 255478182 ecr 255478182], length 0
伺服器向客戶確認已經收到資料
IP localhost.9502 > localhost.39870: Flags [P.], seq 1:19, ack 8, win 4096, options [nop,nop,TS val 255478182 ecr 255478182], length 18
然後伺服器同時向用戶端寫入資料。
IP localhost.39870 > localhost.9502: Flags [.], ack 19, win 4097, options [nop,nop,TS val 255478182 ecr 255478182], length 0
用戶端向伺服器確認已經收到資料
這個就是tcp可靠的串連,每次通訊都需要對方來確認。

4. TCP串連的終止(四次握手釋放)

   由於TCP串連是全雙工系統的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的資料發送任務後就能發送一個FIN來終止這個方向的串連。收到一個 FIN只意味著這一方向上沒有資料流動,一個TCP串連在收到一個FIN後仍能發送資料。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。

建立一個串連需要三向交握,而終止一個串連要經過四次握手,這是由TCP的半關閉(half-close)造成的,

(1)用戶端A發送一個FIN,用來關閉客戶A到伺服器B的資料傳送(報文段4)。

(2)伺服器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1(報文段5)。和SYN一樣,一個FIN將佔用一個序號。

(3)伺服器B關閉與用戶端A的串連,發送一個FIN給用戶端A(報文段6)。

(4)用戶端A發回ACK報文確認,並將確認序號設定為收到序號加1(報文段7)。

對應函數介面 

調用過程如下:

  • 1) 當client想要關閉它與server之間的串連。client(某個應用進程)首先調用close主動關閉串連,這時TCP發送一個FIN M;client端處於FIN_WAIT1狀態。
  • 2) 當server端接收到FIN M之後,執行被動關閉。對這個FIN進行確認,返回給client ACK。當server端返回給client ACK後,client處於FIN_WAIT2狀態,server處於CLOSE_WAIT狀態。它的接收也作為檔案結束符傳遞給應用進程,因為FIN的接收 意味著應用進程在相應的串連上再也接收不到額外資料;
  • 3) 一段時間之後,當server端檢測到client端的關閉操作(read返回為0)。接收到檔案結束符的server端調用close關閉它的socket。這導致server端的TCP也發送一個FIN N;此時server的狀態為LAST_ACK。
  • 4) 當client收到來自server的FIN後 。 client端的通訊端處於TIME_WAIT狀態,它會向server端再發送一個ack確認,此時server端收到ack確認後,此通訊端處於CLOSED狀態。

    這樣每個方向上都有一個FIN和ACK。

    1.為什麼建立連線協定是三向交握,而關閉串連卻是四次握手呢?

    這是因為服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一個報文裡來發送。但關閉串連時,當收到對方的FIN報文通知時,它僅僅表示對方沒有資料發送給你了;但未必你所有的資料都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些資料給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉串連了,所以它這裡的ACK報文和FIN報文多數情況下都是分開發送的。 

    2.為什麼TIME_WAIT狀態還需要等2MSL後才能返回到CLOSED狀態?

    這是因為雖然雙方都同意關閉串連了,而且握手的4個報文也都協調和發送完畢,按理可以直接回到CLOSED狀態(就好比從SYN_SEND狀態到ESTABLISH狀態那樣):

    一方面是可靠的實現TCP全雙工系統串連的終止,也就是當最後的ACK丟失後,被動關閉端會重發FIN,因此主動關閉端需要維持狀態資訊,以允許它重新發送最終的ACK。

    另一方面,但是因為我們必須要假想網路是不可靠的,你無法保證你最後發送的ACK報文會一定被對方收到,因此對方處於LAST_ACK狀態下的SOCKET可能會因為逾時未收到ACK報文,而重發FIN報文,所以這個TIME_WAIT狀態的作用就是用來重發可能丟失的ACK報文。

    TCP在2MSL等待期間,定義這個串連(4元組)不能再使用,任何遲到的報文都會丟棄。設想如果沒有2MSL的限制,恰好新到的串連正好滿足原先的4元組,這時候串連就可能接收到網路上的延遲報文就可能干擾最建立立的串連。

    5、同時開啟

    兩個應用程式同時執行主動開啟的情況是可能的,雖然發生的可能性較低。每一端都發送一個SYN,並傳遞給對方,且每一端都使用對端所知的連接埠作為本地連接埠。例如:

    主機a中一應用程式使用7777作為本地連接埠,並串連到主機b 8888連接埠做主動開啟。

    主機b中一應用程式使用8888作為本地連接埠,並串連到主機a 7777連接埠做主動開啟。

    tcp協議在遇到這種情況時,只會開啟一條串連。

    這個串連的建立過程需要4次資料交換,而一個典型的串連建立只需要3次交換(即3次握手)

    但多數伯克利版的tcp/ip實現並不支援同時開啟。

    6、同時關閉 

    如果應用程式同時發送FIN,則在發送後會首先進入FIN_WAIT_1狀態。在收到對端的FIN後,回複一個ACK,會進入CLOSING狀態。在收到對端的ACK後,進入TIME_WAIT狀態。這種情況稱為同時關閉。

    同時關閉也需要有4次報文交換,與典型的關閉相同。


    7. TCP通訊中伺服器處理用戶端意外斷開

    引用地址:http://blog.csdn.net/kkkkkxiaofei/article/details/12966407

    如果TCP串連被對方正常關閉,也就是說,對方是正確地調用了closesocket(s)或者shutdown(s)的話,那麼上面的Recv或Send調用就能馬上返回,並且報錯。這是由於close socket(s)或者shutdown(s)有個正常的關閉過程,會告訴對方“TCP串連已經關閉,你不需要再發送或者接受訊息了”。

    但是,如果意外斷開,用戶端(3g的行動裝置)並沒有正常關閉socket。雙方並未按照協議上的四次揮手去中斷連線。

    那麼這時候正在執行Recv或Send操作的一方就會因為沒有任何串連中斷的通知而一直等待下去,也就是會被長時間卡住。

    像這種如果一方已經關閉或異常終止串連,而另一方卻不知道,我們將這樣的TCP串連稱為半開啟的。

    解決意外中斷辦法都是利用保活機制。而保活機制分又可以讓底層實現也可自己實現。

    1、自己編寫心跳包程式

    簡單的說也就是在自己的程式中加入一條線程,定時向對端發送資料包,查看是否有ACK,如果有則串連正常,沒有的話則串連斷開

    2、啟動TCP編程裡的keepAlive機制

    一、雙方擬定心跳(自實現)

    一般由用戶端發送心跳包,服務端並不回應心跳,只是定時輪詢判斷一下與上次的時間間隔是否逾時(逾時時間自己設定)。伺服器並不主動發送是不想增添伺服器的通訊量,減少壓力。

    但這會出現三種情況:

    情況1.

    用戶端由於某種網路延遲等原因很久後才發送心跳(它並沒有斷),這時伺服器若利用自身設定的逾時判斷其已經斷開,而後去關閉socket。若用戶端有重連機制,則用戶端會重新串連。若不確定這種方式是否關閉了原本正常的用戶端,則在ShutDown的時候一定要選擇send,表示關閉發送通道,伺服器還可以接收一下,萬一用戶端正在發送比較重要的資料呢,是不?

    情況2.

    用戶端很久沒傳心跳,確實是自身斷掉了。在其重啟之前,服務端已經判斷出其逾時,並主動close,則四次揮手成功互動。

    情況3.

    用戶端很久沒傳心跳,確實是自身斷掉了。在其重啟之前,服務端的輪詢還未判斷出其逾時,在未主動close的時候該用戶端已經重新串連。

    這時候若用戶端斷開的時候發送了FIN包,則服務端將會處於CLOSE_WAIT狀態;

    這時候若用戶端斷開的時候未發送FIN包,則服務端處還是顯示ESTABLISHED狀態;

    而新串連上來的用戶端(也就是剛才斷掉的重新連上來了)在服務端肯定是ESTABLISHED;這時候就有個問題,若利用輪詢還未檢測出上條舊串連已經逾時(這很正常,timer總有個間隔吧),而在這時,用戶端又重複的上演情況3,那麼服務端將會出現大量的假的ESTABLISHED串連和CLOSE_WAIT串連。

    最終結果就是新的其他用戶端無法串連上來,但是利用netstat還是能看到一條串連已經建立,並顯示ESTABLISHED,但始終無法進入程式碼。個人最初感覺導致這種情況是因為假的ESTABLISHED串連和CLOSE_WAIT串連會佔用較大的系統資源,程式無法再次建立串連(因為每次我發現這個問題的時候我只連了10個左右用戶端卻已經有40多條無效串連)。而最近幾天測試卻發現有一次程式內只串連了2,3個裝置,但是有8條左右的虛串連,此時已經串連不了新用戶端了。這時候我就覺得我想錯了,不可能這幾條串連就佔用了大量串連把,如果說幾十條還有可能。但是能肯定的是,這個問題的產生絕對是裝置在不停的重啟,而伺服器這邊又是簡單的輪詢,並不能及時處理,暫時還未能解決。

    二、利用KeepAlive

    其實keepalive的原理就是TCP內嵌的一個心跳包,

    以伺服器端為例,如果當前server端檢測到超過一定時間(預設是 7,200,000 milliseconds,也就是2個小時)沒有資料轉送,那麼會向client端發送一個keep-alive packet(該keep-alive packet就是ACK和當前TCP序號減一的組合),此時client端應該為以下三種情況之一:

    1. client端仍然存在,網路連接狀況良好。此時client端會返回一個ACK。server端接收到ACK後重設計時器(複位存活定時器),在2小時後再發送探測。如果2小時內串連上有資料轉送,那麼在該時間基礎上向後推延2個小時。

    2. 用戶端異常關閉,或是網路斷開。在這兩種情況下,client端都不會響應。伺服器沒有收到對其發出探測的響應,並且在一定時間(系統預設為1000 ms)後重複發送keep-alive packet,並且重複發送一定次數(2000 XP 2003 系統預設為5次, Vista後的系統預設為10次)。

    3. 用戶端曾經崩潰,但已經重啟。這種情況下,伺服器將會收到對其存活探測的響應,但該響應是一個複位,從而引起伺服器對串連的終止。

    對於應用程式來說,2小時的空閑時間太長。因此,我們需要手工開啟Keepalive功能並設定合理的Keepalive參數。

    全域設定可更改/etc/sysctl.conf,加上:
    net.ipv4.tcp_keepalive_intvl = 20
    net.ipv4.tcp_keepalive_probes = 3
    net.ipv4.tcp_keepalive_time = 60

    在程式中設定如下:

     

     #include <sys/socket.h>  #include <netinet/in.h>  #include <arpa/inet.h>  #include <sys/types.h>  #include <netinet/tcp.h>  int keepAlive = 1; // 開啟keepalive屬性  int keepIdle = 60; // 如該串連在60秒內沒有任何資料往來,則進行探測   int keepInterval = 5; // 探測時發包的時間間隔為5 秒  int keepCount = 3; // 探測嘗試的次數.如果第1次探測包就收到響應了,則後2次的不再發.  setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));  setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));  setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));  setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount)); 

    在程式中表現為,當tcp檢測到對端socket不再可用時(不能發出探測包,或探測包沒有收到ACK的響應包),select會返回socket可讀,並且在recv時返回-1,同時置上errno為ETIMEDOUT. 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.