(4.3)TCP狀態轉換,TIME_WAIT詳解,SO_REUSEADDR

一:TCP狀態轉換

  //同一個IP(INADDR_ANY),同一個端口SERV_PORT,只能被成功的bind()一次,若再次bind()就會失敗,並且顯示:Address already in use

   //就好像一個班級裏不能有兩個人叫張三;

  //結論:相同IP地址的相同端口,只能被bind一次;第二次bind會失敗;

 

  //介紹命令netstat:顯示網絡相關信息

  //-a:顯示所有選項

  //-n:能顯示成數字的內容全部顯示成數字

  //-p:顯示段落這對應程序名

  //netstat -anp | grep -E 'State|9000'

 

  //我們用兩個客戶端連接到服務器,服務器給每個客戶端發送一串字符"I sent sth to client!\n",並關閉客戶端;

  //我們用netstat觀察,原來那個監聽端口 一直在監聽【listen】,但是當來了兩個連接之後【連接到服務器的9000端口】,

   //雖然這兩個連接被close掉了,但是產生了兩條TIME_WAIT狀態的信息【因爲你有兩個客戶端連入進來】

 

  //只要客戶端 連接到服務器,並且 服務器把客戶端關閉,那麼服務器端就會產生一條針對9000監聽端口的 狀態爲 TIME_WAIT 的連接;

   //只要用netstat看到 TIME_WAIT狀態的連接,那麼此時, 你殺掉服務器程序再重新啓動,就會啓動失敗,bind()函數返回失敗:

   //bind返回的值爲-1,錯誤碼爲:98,錯誤信息爲:Address already in use

  //TIME_WAIT:涉及到TCP狀態轉換這個話題了;

  //《Unix網絡編程 第三版 卷1》有第二章第六節,2.6.4小節,裏邊就有一個TCP狀態轉換圖;

   //第二章第七節,專門介紹了 TIME_WAIT狀態;

 

  //TCP狀態轉換圖【11種狀態】 是 針對“一個TCP連接【一個socket連接】”來說的;

  //客戶端: CLOSED ->SYN_SENT->ESTABLISHED【連接建立,可以進行數據收發】

  //服務端: CLOSED ->LISTEN->【客戶端來握手】SYN_RCVD->ESTABLISHED【連接建立,可以進行數據收發】

  //誰主動close連接,誰就會給對方發送一個FIN標誌置位的一個數據包給對方;【服務器端發送FIN包給客戶端】

  //服務器主動關閉連接:ESTABLISHED->FIN_WAIT1->FIN_WAIT2->TIME_WAIT

  //客戶端被動關閉:ESTABLISHED->CLOSE_WAIT->LAST_ACK

 二:TIME_WAIT狀態

  //具有TIME_WAIT狀態的TCP連接,就好像一種殘留的信息一樣;當這種狀態存在的時候,服務器程序退出並重新執行會失敗,會提示:

   //bind返回的值爲-1,錯誤碼爲:98,錯誤信息爲:Address already in use

  //所以,TIME_WAIT狀態是一個讓人不喜歡的狀態;

  //連接處於TIME_WAIT狀態是有時間限制的(1-4分鐘之間) = 2 MSL【最長數據包生命週期】;

  //引入TIME_WAIT狀態【並且處於這種狀態的時間爲1-4分鐘】 的原因:

  //(1)可靠的實現TCP全雙工的終止

   //如果服務器最後發送的ACK【應答】包因爲某種原因丟失了,那麼客戶端一定 會重新發送FIN,這樣

   //因爲服務器端有TIME_WAIT的存在,服務器會重新發送ACK包給客戶端,但是如果沒有TIME_WAIT這個狀態,那麼

   //無論客戶端收到ACK包,服務器都已經關閉連接了,此時客戶端重新發送FIN,服務器給回的就不是ACK包,

   //而是RST【連接復位】包,從而使客戶端沒有完成正常的4次揮手,不友好,而且有可能造成數據包丟失;

   //也就是說,TIME_WAIT有助於可靠的實現TCP全雙工連接的終止;

 

    //(二.一)RST標誌

      //對於每一個TCP連接,操作系統是要開闢出來一個收緩衝區,和一個發送緩衝區 來處理數據的收和發;

      //當我們close一個TCP連接時,如果我們這個發送緩衝區有數據,那麼操作系統會很優雅的把發送緩衝區裏的數據發送完畢,然後再發fin包表示連接關閉;

      //FIN【四次揮手】,是個優雅的關閉標誌,表示正常的TCP連接關閉;

 

      //反觀RST標誌:出現這個標誌的包一般都表示 異常關閉;如果發生了異常,一般都會導致丟失一些數據包;

      //如果將來用setsockopt(SO_LINGER)選項要是開啓;發送的就是RST包,此時發送緩衝區的數據會被丟棄;

      //RST是異常關閉,是粗暴關閉,不是正常的四次揮手關閉,所以如果你這麼關閉tcp連接,那麼主動關閉一方也不會進入TIME_WAIT;

 

  //(2)允許老的重複的TCP數據包在網絡中消逝;

 三:SO_REUSEADDR選項

  //setsockopt(SO_REUSEADDR)用在服務器端,socket()創建之後,bind()之前

  //SO_REUSEADDR的能力:

  //(1)SO_REUSEADDR允許啓動一個監聽服務器並捆綁其端口,即使以前建立的將端口用作他們的本地端口的連接仍舊存在;

   //【即便TIME_WAIT狀態存在,服務器bind()也能成功】

  //(2)允許同一個端口上啓動同一個服務器的多個實例,只要每個實例捆綁一個不同的本地IP地址即可;

  //(3)SO_REUSEADDR允許單個進程捆綁同一個端口到多個套接字,只要每次捆綁指定不同的本地IP地址即可;

  //(4)SO_REUSEADDR允許完全重複的綁定:當一個IP地址和端口已經綁定到某個套接字上時,如果傳輸協議支持,

   //同樣的IP地址和端口還可以綁定到另一個套接字上;一般來說本特性僅支持UDP套接字[TCP不行];

  //***************

  //所有TCP服務器都應該指定本套接字選項,以防止當套接字處於TIME_WAIT時bind()失敗的情形出現;

  //試驗程序nginx5_3_2_server.c

  //(3.1)兩個進程,綁定同一個IP和端口:bind()失敗[一個班級不能有兩個人叫張三]

  //(3.2)TIME_WAIT狀態時的bind綁定:bind()成功

 

  //SO_REUSEADDR:主要解決TIME_WAIT狀態導致bind()失敗的問題;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章