文章目錄
- 三次握手
- 四次揮手(以最小的成本協商達成連接配接關閉的認識)
-
- 了解TIME_WAIT
- 了解close_wait
三次握手
TCP伺服器裡面
發起建立連接配接一般都是client
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL5YjMzI2YzgzN2YGZ4MTZiRjZiRzNxkTZiFWMhdTNxgzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
- server裡面存在有大量的連接配接,該如何管理呢?
先描述在組織
有描述連接配接的結構體,裡面填充的就是描述連接配接的各自屬性,後以各種資料結構連接配接起來
雙方維護連接配接是有成本的(時間+空間)
- 為什麼是3次握手(不是說三次握手一定成功,隻是以較大機率建立連接配接的過程)
我們并不擔心第一次第二次丢了:因為第一次,第二次都有應答,第三次沒有應答,就有可能有丢失的風險
兩點:保證對方好着沒,網絡好着沒
- 确認雙方主機是否健康
- 驗證全雙工,三次握手,是我們能看到雙方都能看到收發的最小次數!
對用戶端來說:發送資料syn驗證了自己能發送資料,收到syn+ack驗證了自己能收到資料,同時發送成功
對服務端來說:用戶端發送syn驗證了自己能收資料,發送syn+ACK,得到用戶端回應ack說明了自己能發送資料,沒有第三次握手無法證明服務端有發送的能力
- 一次不行的原因:每次發送syn,服務端都要建立一個連接配接結構來管理,如果發送海量的syn,很容易受到攻擊
- 兩次不行的原因:第二次握手的時候,服務端認為建立成功了,可能這個封包用戶端沒收到,丢棄了,用戶端發送大量的syn,伺服器端還是會維護大量的健康連接配接,消耗維護資源(SYN洪水)
- 三次握手建立成功,雙方是等量的消耗資源,可以杜絕純小白,個人的攻擊
- 對于client來說三次握手怎麼樣算完成
因為3次握手沒有響應,隻要client把ack發送出去,client就認為它握手完成
一般而言雙方的握手成功是有時間差的:server段認為收到才算成功
- 第三次ack丢失
client認為連接配接已經完成,server認為連接配接沒有完成
client就會理所當然的給server段發送資料,server(認為連結都沒建立好就發送資料)收到了這個封包,這個封包不是建立連接配接的封包,就會傳回一個RST(證明,剛才建立的連接配接失敗,client就會關閉連接配接)
- 三次握手是雙方的作業系統自動完成的,使用者層完全不參與
client->connect ->發起三次握手(作業系統自動完成)
server->accept,握手成功
四次揮手(以最小的成本協商達成連接配接關閉的認識)
- 斷開連接配接是雙方的事情,雙方随時都有可能發生
- 分别發送FIN,雙方分别應答,就是四次握手,一方發送fin就是把自己的發送緩沖區給關閉了
- 沒有人以斷開連接配接來發起攻擊,因為這個是讓伺服器更輕松
- 斷開連接配接的本質:雙方達成連接配接都應該斷開的共識,就是一個通知對方的機制
了解TIME_WAIT
主動斷開連接配接的一方,就要進入TIME_WAIT狀态
- TIME_WAIT狀态上,認為自己的連接配接已經釋放了,因為它已近收發了四次封包,即使它已近結束了,但是它還不能釋放自己的結構,還要維護一段時間,等待自己的ack發送成功
- 不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狀态
一旦TIME_WAIT狀态,服務是無法立即重新開機的
MSL(max segment life):封包最大傳輸時間,一個封包在網絡裡面最大的存活時間
為什麼要有TIME_WAIT?
TIME_WAIT一般是等待2msl的時間長度
- 盡量保證曆史發送的網絡資料在網絡中消散,因為如果直接斷開的話,還有一些雙方發送的資料,并沒有被讀取
- 2msl剛好保證了資料是一來一回的,曆史上的資料更好就卡在了出口路由器上,
- 盡量的保證最後一個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;
}
啟示
- 一個fd被用完,千萬不要忘記釋放!
- fd是有限制的,會造成fd洩露,fd被占用變得越來越少