天天看點

TCP連接配接管理(2)三次握手四次揮手(以最小的成本協商達成連接配接關閉的認識)

文章目錄

  • 三次握手
  • 四次揮手(以最小的成本協商達成連接配接關閉的認識)
    • 了解TIME_WAIT
    • 了解close_wait

三次握手

TCP伺服器裡面

發起建立連接配接一般都是client

TCP連接配接管理(2)三次握手四次揮手(以最小的成本協商達成連接配接關閉的認識)
  1. server裡面存在有大量的連接配接,該如何管理呢?

先描述在組織

有描述連接配接的結構體,裡面填充的就是描述連接配接的各自屬性,後以各種資料結構連接配接起來

雙方維護連接配接是有成本的(時間+空間)

  1. 為什麼是3次握手(不是說三次握手一定成功,隻是以較大機率建立連接配接的過程)
  • 我們并不擔心第一次第二次丢了:因為第一次,第二次都有應答,第三次沒有應答,就有可能有丢失的風險

    兩點:保證對方好着沒,網絡好着沒

  • 确認雙方主機是否健康
  • 驗證全雙工,三次握手,是我們能看到雙方都能看到收發的最小次數!

對用戶端來說:發送資料syn驗證了自己能發送資料,收到syn+ack驗證了自己能收到資料,同時發送成功

對服務端來說:用戶端發送syn驗證了自己能收資料,發送syn+ACK,得到用戶端回應ack說明了自己能發送資料,沒有第三次握手無法證明服務端有發送的能力

  • 一次不行的原因:每次發送syn,服務端都要建立一個連接配接結構來管理,如果發送海量的syn,很容易受到攻擊
  • 兩次不行的原因:第二次握手的時候,服務端認為建立成功了,可能這個封包用戶端沒收到,丢棄了,用戶端發送大量的syn,伺服器端還是會維護大量的健康連接配接,消耗維護資源(SYN洪水)
  • 三次握手建立成功,雙方是等量的消耗資源,可以杜絕純小白,個人的攻擊
  1. 對于client來說三次握手怎麼樣算完成

因為3次握手沒有響應,隻要client把ack發送出去,client就認為它握手完成

一般而言雙方的握手成功是有時間差的:server段認為收到才算成功

  1. 第三次ack丢失

client認為連接配接已經完成,server認為連接配接沒有完成

client就會理所當然的給server段發送資料,server(認為連結都沒建立好就發送資料)收到了這個封包,這個封包不是建立連接配接的封包,就會傳回一個RST(證明,剛才建立的連接配接失敗,client就會關閉連接配接)

  1. 三次握手是雙方的作業系統自動完成的,使用者層完全不參與

client->connect ->發起三次握手(作業系統自動完成)

server->accept,握手成功

四次揮手(以最小的成本協商達成連接配接關閉的認識)

  1. 斷開連接配接是雙方的事情,雙方随時都有可能發生
  2. 分别發送FIN,雙方分别應答,就是四次握手,一方發送fin就是把自己的發送緩沖區給關閉了
  3. 沒有人以斷開連接配接來發起攻擊,因為這個是讓伺服器更輕松
  4. 斷開連接配接的本質:雙方達成連接配接都應該斷開的共識,就是一個通知對方的機制
TCP連接配接管理(2)三次握手四次揮手(以最小的成本協商達成連接配接關閉的認識)

了解TIME_WAIT

主動斷開連接配接的一方,就要進入TIME_WAIT狀态

  1. TIME_WAIT狀态上,認為自己的連接配接已經釋放了,因為它已近收發了四次封包,即使它已近結束了,但是它還不能釋放自己的結構,還要維護一段時間,等待自己的ack發送成功
  2. 不accept它也會建立連接配接
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        cout << "port" << endl;
        exit(1);
    }
    uint16_t port = atoi(argv[1]);
    int sockfd = Sock::Socket();
    Sock::Bind(sockfd, port);
    Sock::Listen(sockfd);
    while (1)
    {
        int newsock = Sock::Accept(sockfd);
        cout<<newsock<<endl;
        //這裡我們讓伺服器先斷開

    }
    return 0;
}
           

連接配接建立成功這裡estabilsh,我們把伺服器斷開,伺服器就進入了time_wait狀态

TCP連接配接管理(2)三次握手四次揮手(以最小的成本協商達成連接配接關閉的認識)
TCP連接配接管理(2)三次握手四次揮手(以最小的成本協商達成連接配接關閉的認識)

一旦TIME_WAIT狀态,服務是無法立即重新開機的

MSL(max segment life):封包最大傳輸時間,一個封包在網絡裡面最大的存活時間

TCP連接配接管理(2)三次握手四次揮手(以最小的成本協商達成連接配接關閉的認識)

為什麼要有TIME_WAIT?

TIME_WAIT一般是等待2msl的時間長度
  1. 盡量保證曆史發送的網絡資料在網絡中消散,因為如果直接斷開的話,還有一些雙方發送的資料,并沒有被讀取
  2. 2msl剛好保證了資料是一來一回的,曆史上的資料更好就卡在了出口路由器上,
  3. 盡量的保證最後一個ACK被對方收到,沒有消息的話,就認為已近ack發送成功,如果中途收到fin,說明我發送ack失敗

** 為什麼會bind error**

我們斷開連接配接之後,在連接配接,因為之前的伺服器一方處于time_wait方面,這個連接配接沒有斷開,端口依舊被占用着,

因為一個端口号隻能被一個程序綁定

無法立即重新開機有什麼危害

1s都是很大的資料量,伺服器的端口和ip都是不能改變的,客戶不認識,連不上,是以必須使用位址複用的選項

了解close_wait

調用一個close就是2次揮手

如果用戶端調用close,而伺服器不調用close,那麼伺服器就會一直處于close_wait狀态,用戶端就會處于fin_wait2狀态

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        cout << "port" << endl;
        exit(1);
    }
    uint16_t port = atoi(argv[1]);
    int sockfd = Sock::Socket();
    Sock::Setoptsocket(sockfd);
    Sock::Bind(sockfd, port);
    Sock::Listen(sockfd);
    while (1)
    {
        int newsock = Sock::Accept(sockfd);
        cout<<newsock<<endl;
        //這裡我們不調用closefd,伺服器就會處于close_wait狀态

    }
    return 0;
}
           
TCP連接配接管理(2)三次握手四次揮手(以最小的成本協商達成連接配接關閉的認識)

啟示

  1. 一個fd被用完,千萬不要忘記釋放!
  2. fd是有限制的,會造成fd洩露,fd被占用變得越來越少