TCP的狀態轉化過程(11中狀態)以及TIME_WAIT狀態
TCP中的三次握手,四次揮手是我們所熟知的,可是,我們熟悉裡面的各種狀態嗎???
(SYN_SENT, ESTABLISHED, CLOSE_WAIT.............),試問一句,我們瞭解裡面的狀態轉化嗎???
1,大家先看一個簡單的通訊圖(圖片轉載與:UNIX網路程式設計,page:36,圖2-5)
可以很明顯的看到,在通訊雙方,客戶端,服務端的狀態變化過程
有人可能會說:我們上面不是說,有11中狀態嗎??為什麼到啦這裡變成了只有10中
(1,(主動開啟:SYN_SENT) 2,ESTABLISHED 3,(主動關閉:FIN_WAIT_1) 4,FIN_WAIT_2
5,TIME_WAIT 6,SYN_RCVD 7,CLOSE_WAIT(被動關閉) 8,LAST_ACK 9,CLOSED
10,LISTEN)
為什麼不是11個呢???
哈哈,其實還有一種狀態叫做:CLOSING(這個狀態產生的原因比較特殊,後面分析)
接下來我們分析一下,這些狀態的變化過程,,,
主動套介面:用來發起連線 被動套介面:用來接受連線
1,對於伺服器端來說:
當呼叫socket函式建立一個套接字時,狀態是CLOSED
將呼叫connect發起連線的客戶套接字。listen函式把一個未連線的套接字轉化成一個被動套接字,指示核心
應接受指向該套接字的連線請求。結合TCP的狀態轉化圖:
呼叫listen函式導致套接字從:CLOSED狀態轉化為:LISTEN狀態
2,對於客戶端來說:
呼叫socket函式建立一個套介面時,狀態也是CLOSED,同樣的,它也被假設為一個主動套接字,緊接著,調
用connect主動開啟套介面,並且一直阻塞著,等待三次握手的完成,我們把這個狀態稱之為:主動套介面。
當客戶端發起了三次握手的第一次(SYN J,MSS = 536)的時候,套介面的狀態變成了:
SYN_SENT(主動開啟)
3,對於伺服器端而言,呼叫了listen之後,然後狀態就變成了LISTEN狀態,接著呼叫accept函式,使自身一直
保持阻塞的狀態,直到三次握手的第一次來到(來自TCP協議棧的TCP的第一個分節),即接收到(SYN J,
MSS = 536),此刻狀態由:LISTEN轉變為SYN_RCVD
4,對於客戶端來說,剛才傳送了TCP協議棧中TCP三次握手的第一個分節,此刻應該接受來自伺服器傳送過來的
TCP三次握手的第二個分節,這時伺服器傳送過來:(SYN K, ACK J+1, MSS = 1460),此刻,伺服器
的狀態不變,還是SYN_RCVD,然後,客戶端接受伺服器傳送過來的TCP三次握手的第二次分節,此刻狀態
由之前的:SYN_SENT轉變為ESTABLISHED,(客戶端已經建立完成),這時,connect函式返回
5,然後客戶端保持ESTABLISHED狀態,並且發出TCP協議棧中TCP三次握手的第三個分節(ACK K+1)
服務端的狀態由:SYN_RCVD轉變為:ESTABLISHED,從未完成的佇列中取出隊首的第一個連線放在已完成
佇列,這樣accept函式就會返回。
此刻,兩者都建立完成,這個時候可以完成通訊了
6,那麼接下來就是連線終止的四次握手,,,
當雙方都變成ESTABLISHED狀態之後,雙方就可以通訊了,在雙方通訊的過程中,由於狀態都沒有變化,
所以這裡,我們暫且不討論。在通訊的時候呢,雙方都可以主動發起關閉,那麼:我們假定客戶端發起一個
關閉請求(呼叫close函式):會向服務端傳送一個TCP分節(TCP協議棧中四次握手的的第一個分節:
FIN M),然後客戶端的狀態會變成:FIN_WAIT_1(主動關閉),此刻,服務端接收到這個TCP分節後,
並且會對剛才發過來的連線進行確認(ACK M+1),,服務端的狀態會變成 CLOSE_WAIT(被動關
閉),當,客戶端接收到這個確認之後(ACK M+1),客戶端的狀態轉變
為:FIN_WAIT_2 , 只有當服務端的read函式返回為0的時候,服務端才需要,也是才可以發起關閉請求(FIN
N),傳送完成之後,就變成了:
LAST_ACK, 當客戶端接受到了這個關閉請求之後,狀態會變成了:TIME_WAIT(會經過
2MSL(TCP報文端最大生存週期的兩倍時間)之後,轉變為:CLOSED),緊接著客戶端會發送
最後一次確認:(ACK N+1),等到服務端接收到這個確認後,服務端的狀態會變成:CLOSED
關於CLOSING:
該狀態產生的原因是:對於客戶端和服務端而言,兩者同時關閉的情況(這種情況並不多見),如下圖:
、 兩者同時關閉,後狀態同時變成了FIN_WAIT_1,然後當另外一端接收到關閉分節後,狀態同時變成CLOSING,然後都對剛才那個分節進行確認,當對端收到之後,兩者又都變成了TIME_WAIT,
所以說:在關閉的過程中,不一定可以必須要經過FIN_WAIT_2這個狀態。。。。。。。。。。。。
關於TIME_WAIT:
1,我們可以從上面的狀態分析中得知,對於TIME_WAIT狀態而言,是執行主動關閉的那端經歷了這個狀態。
該端點停留在這個狀態的持續時間是最長分節生命期(MAXIMUM SEGMENT LIFETIME, msl)的兩
倍,有時候稱之為:2MSL
任何TCP實現都必須為MSL選擇一個值,RFC1122的建議值是2分鐘,而源自Berkeley的實現傳統上改用
30秒這個值,又因為:資訊的傳送是需要一個來回,著也就說明,TIME_WAIT狀態的持續時間是1分鐘
到4分鐘之間。而MSL是任何IP資料報能夠在因特網中存活的最長時間。我們也知道這個時間是有限的,
因為每個資料報含有一個跳限(hop limit)的8位欄位,它的最大值是255。儘管這是一個跳數限制而不是
真正的時間限制,我們仍然假設:
具有最大跳限(255)的分組在網路中存在的時間不可能超過MSL秒。。。。。
分組在網路中“迷途”通常是路由異路的結果。某個路由器崩潰或某兩個路由器之間的某個鏈路斷開時,路由
協議需要花數秒鐘到數分鐘的時間才能穩定並找出另一條通路。在這段時間內可能發生路由迴圈(
路由器A把分組傳送給路由器B,而B再把它們傳送給A),我們關心的分組可能就此陷入這樣的迴圈。
假設迷途的分組是一個TCP分節,在它迷途期間,傳送端TCP超時重傳該分組,而重傳的分組卻通過某條
候選路徑到達最終目的。然而不久後(自迷途的分組開始其旅程起最多MSL秒以內)路由迴圈修復,早先
迷失在這個迴圈中的分組最終也被送到目的地。TCP必須正確處理這些重複的分組。
TIME_WAIT狀態存在的兩個理由:
1,可靠的實現TCP全雙工連線的終止(更好的完善TCP的可靠性)
2,允許老的重複分節在網路中消逝
關於第一點:假設最終的ACK丟失了來解釋(並不能保證傳輸的可靠行)。伺服器將重新發送它的最終的
那個FIN, 因此客戶必須維護狀態資訊,以允許它重新發送那個ACK。要是客戶不維護狀態資訊,它將
響應以一個RST(另外一種型別的TCP分節),該分節將被伺服器解釋成一個錯誤。如果TCP打算執行所
有必要的工作以徹底終止某個連線上兩個方向的資料流(即全雙工關閉),那麼它必須正確處理連線終止
序列4個分節中任何一個分節丟失的情況。本例子也說明了為什麼執行主動關閉的那一端是處於
TIME_WAIT的那一端;因為可能不得不重傳最終的那個ACK的就是那一端。
關於第二點:我們假設在12.106.32.254的1500埠和206.168.112.219的21埠之間有一個TCP連線。我
們關閉這個連線,過一段時間後在相同的IP地址和埠之間建立另一個連線。後一個連線稱為前一個連線
的化身,因為他們的IP地址和埠號相同。TCP必須防止來自某個連線的老的重複分組在該連線已終止後
再現,從而被誤解成屬於同一個連線的某個新的化身。為做到這一點,TCP將不給處於TIME_WAIT狀態
的連線發起新的化身。既然TIME_WAIT狀態的持續時間是MSL的2倍,這就足矣讓某個方向上的分組最多
存活MSL秒即被丟棄,另一個方向上的應答最多存活MSL秒也被丟棄。通過實施這個規則,我們就能保證
每成功建立一個TCP連線時,來自該連線先前化身的老的重複分組都已在網路中消逝了。。。。
大家可以過來看看!!!
當我們僅僅開啟服務端之後(埠號為5188),我們來看看所處的狀態。
開啟服務端:
呼叫命令檢視所有的網路狀態:netstat
然後,我們通過命令:摘取有關tcp的狀態:netstat -an |grep tcp
緊接著為了刪減出有效的資訊,我們只需要tcp協議,5188這個埠,我們可以這樣做:
netstat -an|grep tcp|grep 5188
嗯嗯,此刻,可以看到,我們這裡的狀態是處於LISTEN,呼叫的accept函式還是在阻塞著,等待著返回。
這時,我們再次開啟客戶端,繼續觀察一下狀態:
然後,我們繼續呼叫之前的命令:
netstat -an|grep tcp|grep 5188
當客戶端一開啟,那麼就完成了TCP的建立,這裡,我們可以看到有兩個是:ESTABLISHED
其中第二行的42555表示的是客戶端所開啟的埠,5188是服務端所開啟的埠,客戶端連向了伺服器端
由於我們上面的測試是在同一臺主機上的,所以會出現上面的三種資訊
而對於其他的狀態而言,只是因為狀態的轉化時間非常短(三次握手,四次揮手完成的特別快),我們不
去探究具體的狀態,,,
1,查詢伺服器程序:
ps -ef | grep echoserv
分析其pid號,知道了我們此刻開啟的是中間的這個服務端(21858,21849)
所以,此刻,我們殺死這個程序:
kill -9 21858
到啦這裡,我們再次檢視一下狀態:
至於為什麼會產生一個FIN_WAIT2, 而不是TIME_WAIT狀態呢,,,,這是因為:我們程式中是這樣處理的,我們
的服務端關閉之後,然後客戶端接收到啦這個分節,並向服務端傳送了當前的分節確認,然後自己阻塞在了從鍵盤獲
取字元的這個位置,並不能執行到函式read處去,也就是說,
read函式壓根就不會返回0,所以客戶端就不會重新向服務端重新發送關閉連線的分節,也就停留在此刻了,同樣的,
服務端接受到啦確認分節,那麼自己的狀態就變成了FIN_WAIT_2,這樣就解釋的通了,哈哈哈
以下是:我們的客戶端處理程式:
void echo_cli(int sock)
{
char sendbuf[1024] = {0};
char recvbuf[1024] = {0};
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
writen(sock, sendbuf, strlen(sendbuf));
int ret = readline(sock, recvbuf, sizeof(recvbuf));
if(ret == -1)
ERR_EXIT("READline");
else if(ret == 0)
{
printf("client close \n");
break;
}
fputs(recvbuf, stdout); //fgets接受到的資料,預設說明是存在換行符的
memset(sendbuf, 0 , sizeof(sendbuf));
memset(recvbuf, 0 , sizeof(recvbuf));
}
close(sock);
}
此刻,如果我們再重新輸入字元,然後就會執行到read函式處,由於對方已經關閉,對端會接收到(四次揮手)的
第一個分節(FIN),然後read返回0,從上面函式可以看出,程式執行break,然後繼續執行close(sock)
而對於客戶端先關閉的情況,,,則是這個樣子的,,,
同理,先開啟服務端,再開啟客戶端,,,
進去之後,直接按:CTRL + C,使客戶端退出,我們檢視一下狀態:
可以知道,出現了TIME_WAIT狀態,,,
同樣的,這裡,我們也需要檢視一下echoserv具體的實現:
void echo_serv(int conn)
{
char recvbuf[1024];
while(1)
{
memset(recvbuf, 0, sizeof(recvbuf));
int ret = readline(conn, recvbuf, 1024);
if(ret == -1)
ERR_EXIT("READLine");
if(ret == 0)
{
printf("client close\n");
break;
}
fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf));
}
}
出現這個狀態也是比較簡單,因為:客戶端結束了之後,服務端開始執行readline(裡面封裝了read),read 返回為0
不會阻塞,緊接著就執行close,會繼續傳送一個fin分節,,所以會出現後面的TIME_WAIT狀態啦,,,
我們的伺服器端會處於TIME_WAIT狀態,這時如果我們繼續開啟伺服器會出現:地址佔用,
如果,我們不使用REUSEADDR的話,如果我們使用這個REUSEADDR,並且設定選項的話,setsockopt的話,那麼我們可以隨時開啟伺服器,不用等待2MSL個時間
關於RST分節,
1,對於RST分節,其實是這個樣子的,我們開啟服務端,客戶端,然後關閉服務端(會向客戶端傳送一個FIN 分節)
,但是這個時候,我們的客戶端是阻塞在fgets函式的,我們從鍵盤給一個字串,讓其滿足fgets函式,執行到write
函式,將剛才的字串輸出給服務端,由於剛才的服務端已經終止了並且傳送了一個FIN,說明不能在傳送
新的段,並且也不能接受對端的資料,由於此時服務端已經終止,所以上面客戶端傳送給服務端的資訊,也就找不
到歸宿這個時候(對方程序不存在了),TCP協議棧就會發送一個RST的tcp分節過去。如果這個時候,我們在呼叫
write() 函式去讀取的話,那麼就會產生SIGPIPE,
程式如下:
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
// writen(sock, sendbuf, strlen(sendbuf));
write(sock , sendbuf, 1); //分兩次傳送,先發送1個,然後在傳送剩餘的
write(sock , sendbuf + 1, strlen(sendbuf) - 1);
int ret = readline(sock, recvbuf, sizeof(recvbuf));
if(ret == -1)
ERR_EXIT("READline");
else if(ret == 0)
{
printf("client close \n");
break;
}
fputs(recvbuf, stdout); //fgets接受到的資料,預設說明是存在換行符的
memset(sendbuf, 0 , sizeof(sendbuf));
memset(recvbuf, 0 , sizeof(recvbuf));
}
可以看到,上面我們呼叫了兩次的write函式,第一次write函式(傳送字元的時候),對面的程序已經不在了,TCP
協議棧會發送一個RST分節,緊接著我們再次呼叫了write函式,此刻就產生了一個SIGPIPE的訊號中斷,直接終止當
前程序,倘使不退出程式的話,那麼read會返回0(readline中封裝著read),所以ret等於0,應該會列印client close
,但是我們的程式並沒有列印。。。。。
(開啟相應的客戶端,服務端)
觀察狀態:
服務端關閉:
觀察狀態:
給客戶端一個字串,滿足fgets函式
程式直接退出了,所以看得出來,並沒有列印client close
所以說,我們上面的分析是合理的。。。。。。
接下來我們修改一下程式:
<span style="color:#000000;">void handle_sigpipe(int sig)
{
printf("recv is a sig = %d\n", sig);
}
int main()
{
signal(SIGPIPE, handle_sigpipe);
int sock;
if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERR_EXIT("socket");</span>
同樣的道理,我們來執行一下程式:
這裡還能輸出:client close,為什麼呢???這是因為產生了sigpipe中斷訊號後,我們對中斷訊號進行了處理了,所以不會退出程式了
同樣的,我們來檢視一下這個:sig = 13
可以看到,這裡的正是sigpipe訊號
上面看啦這麼多,我們貌似好像看到了用kill殺死一個程序和CTRL + C,我們來看看區別!!!
同理,開啟客戶端,服務端
檢視狀態:
呼叫CTRL + C,關閉伺服器
接著我們繼續檢視狀態
如果我們:呼叫kill殺死相應的服務端程序的話!!!
緊接著,我們再來看看狀態:
CTRL+C:傳送SIGINT訊號給前臺程序組中的所有程序。常用於終止正在執行的程式,強制中斷程式的執行
CTRL+Z:傳送SIGTSTP訊號給前臺程序組中的所有程序,常用於掛起一個程序,是將任務中斷,但是此任務並沒有結束,它仍然在程序中他只是維持掛起的狀態,使用者可以使用fg/bg操作繼續前臺或後臺的任務,fg命令重新啟動前臺被中斷的任務,bg命令把被中斷的任務放在後臺執行
可知,如果我們呼叫kill的話,那麼我們還能觀察到對等的狀態,如果我們呼叫CTRL + C的話,那麼我們的整個服務端
程式都被中斷
總之:上面說了這麼多的原因,就是說,一端A呼叫close退出的話,會發送FIN分節給
對端B,但是對於B接收到A的分節之後,並不能保證A端的程序是不是已經消失,,,
因為對方呼叫close,並不意味著對方的程序會消失,,,當然,上面我們是通過kill或
者CTRL + C來確保的,如果這時B端再呼叫write,發現A端不存在,那麼TCP協議棧會
傳送一個RST分節(連線重置的TCP端),對於當前的全雙工管道而言,如果再次調
用write函式的話,那麼就會
產生SIGPIPE訊號中斷。。。。。。。。。。
相關推薦
TCP的狀態轉化過程(11中狀態)以及TIME_WAIT狀態
TCP中的三次握手,四次揮手是我們所熟知的,可是,我們熟悉裡面的各種狀態嗎??? (SYN_SENT, ESTABLISHED, CLOSE_WAIT.............),試問一句,我們瞭解裡面的狀態轉化嗎??? 1,大家先看一個簡單的通訊圖(圖片轉載
Zookeeper常用命令列命令(類unix命令)以及 Stat狀態說明
1 常見命令(類unix命令) 命令基本語法 功能描述 help 顯示所有操作命令 ls path [watch] 使用 ls 命令來檢視當前znode中所包含的內容
HTTP狀態碼大全(轉自wiki)
成對 節點 而是 沒有 redirect port multiple 許可 sta 1xx消息 這一類型的狀態碼,代表請求已被接受,需要繼續處理。這類響應是臨時響應,只包含狀態行和某些可選的響應頭信息,並以空行結束。由於HTTP/1.0協議中沒有定義任何1xx狀態碼,所以除
Nginx輸出基本狀態信息(Ngx_http_stub_module模塊)
web;nginx;linux輸出Nginx基本狀態信息(Ngx_http_stub_status_module)官方文檔:http://nginx.org/en/docs/http/ngx_http_status_module.html官方定義:The ngx_http_stub_status_module
設計模式 十七 狀態模式State(物件行為型)
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
TCP/IP學習筆記(11)-tcp互動資料流,成塊資料流
目前建立在TCP協議上的網路協議特別多,有telnet,ssh,有ftp,有http等等。這些協議又可以根據資料吞吐量來大致分成兩大類:  
HTTP狀態碼一覽表(HTTP Status Code)
英文版: 100:Continue 101:Switching Protocols 102:Processing 200:OK 201:Created 202:Accepted 203:Non-Authoriative Information 204:No Content 205:Reset Content
在KVM中設定TLS證書認證TCP和VNC連線(填坑版)
本文是一篇散文 今天我們就來說一下KVM的安全配置 KVM的安全實現有很多型別 比如用SSH來連線KVM管理器 但是SSH這個方法有個不好的地方就是沒法對我們的VNC加密 或者就是我們這裡的TLS配置 既然涉及到了TLS肯定離不開證書 我們就先從證
狀態模式State(物件行為型)
1.概述 在軟體開發過程中,應用程式可能會根據不同的情況作出不同的處理。最直接的解決方案是將這些所有可能發生的情況全都考慮到。然後使用if... ellse語句來做狀態判斷來進行不同情況的處理。但是對複雜狀態的判斷就顯得“力不從心了”。隨著增加新的狀態或者修改一個狀體(if else(或sw
hihocoder-1048 狀態壓縮·二(狀壓DP)
小Ho的觀察力一向不錯,這不,他很快便發現了M的取值範圍只可能為3、4、5三種情況,但是這一發現並沒有能夠給他減輕多少煩惱。 雖然在過去一段時間的訓練下,小Ho很快就意識到這道題目可能仍然是需要使用動態規劃進行解決,但是他苦思冥想也沒有能夠想到很好的狀態定義方式:“如果我每次列舉一塊蛋糕的放置位置,那麼我就
設計模式 十七 狀態模式State(物件行為型)
1.概述在軟體開發過程中,應用程式可能會根據不同的情況作出不同的處理。最直接的解決方案是將這些所有可能發生的情況全都考慮到。然後使用if... ellse語句來做狀態判斷來進行不同情況的處理。但是對複雜狀態的判斷就顯得“力不從心了”。隨著增加新的狀態或者修改一個狀體(if el
iPhone X (XS XR XSMAX)如何根據狀態列上的圖示 獲取裝置的聯網狀態(不是單個應用的)
在iPhone X 上通過狀態列獲取聯網狀態使用下面的程式碼直接閃退 - (BOOL)networkingStatesFromStatebar { UIApplication *app = [UIApplication sharedApplication];
Vuex 在工程化中的使用以及state狀態屬性的使用筆記
Vuex是什麼? 引入官方提供的解釋,vuex 是專門為 vue.js 設計的一套 狀態管理模式 。什麼是狀態管理模式?說白了就是資料的集中管理。我們在使用 vue.js 時所用到的資料全部抽取出來放在一個state物件下,這樣我們在任何元件內都可以訪
TCP/IP通訊過程(以傳送電子郵件為例)
1.應用程式處理 (1)A使用者啟動郵件應用程式,填寫收件人郵箱和傳送內容,點選“傳送”,開始TCP/IP通訊; (2)應用程式對傳送的內容進行編碼處理,這一過程相當於OSI的表示層功能; (3)由A使用者所使用的郵件軟體決定何時建立通訊連
【POJ2411】Mondriaan's Dream-狀態壓縮DP(插頭DP?)
題目大意:求用1*2的骨牌完美覆蓋h*w的棋盤的方法數。 做法:這道題絕對是經典題啊,久仰大名......關於輪廓線和插頭的思想可以看cdq大大的論文:點這裡。但這題不用搞那麼麻煩,因為骨牌之間相互
TCP/IP學習筆記(11)——廣播和多播
1、引言 廣播和多播僅應用於 UDP,它們對需將報文同時傳往多個接收者的應用來說十分重要。TCP是一個面向連線的協議,它意味著分別運行於兩主機(由 IP地址確定)內的兩程序(由埠號確定)間存在一條連線。 考慮包含多個主機的共享通道網路如乙太網。每個乙太網幀包
TCP狀態圖的理解以及TIME_WAIT狀態的作用
TCP/IP狀態圖 以及 TIME_WAIT作用 在TCP/IP狀態圖中,有很多種的狀態,它們之間有的是可以互相轉換的,也就是說,從一種狀態轉到另一種狀態,但是這種轉換不是隨便傳送的,是要滿足一定的條件。TCP/IP狀態圖看起來更像是自動機。下圖即為TCP/IP狀態。 由上圖可以看出,一共有11種
Linux下apache日誌(按日期存放)分析與狀態檢視方法
一、apache日誌按日期記錄 在apache的配置檔案中找到 ErrorLog logs/error_log CustomLog logs/access_log common Linux系統配
使用Charles3.11.2過程(附加下載Mac)
做iOS苦於沒有UI素材和JSON或XML資料資源等,我們可以嘗試用Charles來進行其他App捉包進行獲取想要的素材。(真心想做出自己理想的app不是抄襲而是藉助來學習) 安裝好javaforosx 之後再安裝Charles-proxy-3.11.2 複製charl
【UE4】【C++】列舉類(對於不同狀態顯示不同的顏色準心)C++設定狀態,藍圖設定顏色(UI顏色繫結)
官方對列舉的用法https://wiki.unrealengine.com/Enums_For_Both_C%2B%2B_and_BP寫在UClass上面、#include下面UENUM() enum class EFiringState :uint8 { //