日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

win2008 查询 tcp连接失败_TCP详解(转)

發(fā)布時(shí)間:2025/3/15 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 win2008 查询 tcp连接失败_TCP详解(转) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一直以來寫過好些TCP連接的程序,各種情況都處理過,但是對(duì)具體的TCP協(xié)議卻缺少完整性的學(xué)習(xí)。在網(wǎng)上找了一篇覺得特別棒的文章,這里做下搬運(yùn)工,保存下來,方便以后自己翻閱。

通過閱讀發(fā)現(xiàn)一個(gè)有意思的事情,其實(shí)TCP的客戶端是會(huì)把服務(wù)端給弄卡住的,客戶端一直不調(diào)用recv,當(dāng)數(shù)據(jù)滿了后,服務(wù)器就發(fā)不出來數(shù)據(jù)了,會(huì)一直發(fā)送查詢窗口信息,具體看下面的滑動(dòng)窗口那一段,我還做了一份代碼測試了一下,確實(shí)如描述一樣。

鏈接: https://pan.baidu.com/s/1mNyZiTLbmYq6Fnf-r6piqg 提取碼: nnhv

所以這體現(xiàn)了心跳的作用,當(dāng)長時(shí)間沒收到數(shù)據(jù)的時(shí)候,直接斷開。

當(dāng)然也可以用setsockopt 來設(shè)置下發(fā)送超時(shí)時(shí)間。假如send失敗就斷開

不過一般還是用心跳吧,也可以兩者都用

看完此文該就有了比較系統(tǒng)的了解了

以下是很值得欣賞的原文:

原文鏈接:https://blog.csdn.net/sinat_36629696/article/details/80740678

TCP協(xié)議

TCP協(xié)議全稱: 傳輸控制協(xié)議, 顧名思義, 就是要對(duì)數(shù)據(jù)的傳輸進(jìn)行一定的控制.

先來看看它的報(bào)頭

我們來分析分析每部分的含義和作用

源端口號(hào)/目的端口號(hào): 表示數(shù)據(jù)從哪個(gè)進(jìn)程來, 到哪個(gè)進(jìn)程去.

32位序號(hào):

4位首部長度: 表示該tcp報(bào)頭有多少個(gè)4字節(jié)(32個(gè)bit)

6位保留: 顧名思義, 先保留著, 以防萬一

6位標(biāo)志位

URG: 標(biāo)識(shí)緊急指針是否有效

ACK: 標(biāo)識(shí)確認(rèn)序號(hào)是否有效

PSH: 用來提示接收端應(yīng)用程序立刻將數(shù)據(jù)從tcp緩沖區(qū)讀走

RST: 要求重新建立連接. 我們把含有RST標(biāo)識(shí)的報(bào)文稱為復(fù)位報(bào)文段

SYN: 請(qǐng)求建立連接. 我們把含有SYN標(biāo)識(shí)的報(bào)文稱為同步報(bào)文段

FIN: 通知對(duì)端, 本端即將關(guān)閉. 我們把含有FIN標(biāo)識(shí)的報(bào)文稱為結(jié)束報(bào)文段

16位窗口大小:

16位檢驗(yàn)和: 由發(fā)送端填充, 檢驗(yàn)形式有CRC校驗(yàn)等. 如果接收端校驗(yàn)不通過, 則認(rèn)為數(shù)據(jù)有問題. 此處的校驗(yàn)和不光包含TCP首部, 也包含TCP數(shù)據(jù)部分.

16位緊急指針: 用來標(biāo)識(shí)哪部分?jǐn)?shù)據(jù)是緊急數(shù)據(jù).

選項(xiàng)和數(shù)據(jù)暫時(shí)忽略

連接管理機(jī)制

正常情況下, tcp需要經(jīng)過三次握手建立連接, 四次揮手?jǐn)嚅_連接.

那么什么是三次握手? 什么是四次揮手呢?

三次握手

第一次:

客戶端 - - > 服務(wù)器 此時(shí)服務(wù)器知道了客戶端要建立連接了

第二次:

客戶端 < - - 服務(wù)器 此時(shí)客戶端知道服務(wù)器收到連接請(qǐng)求了

第三次:

客戶端 - - > 服務(wù)器 此時(shí)服務(wù)器知道客戶端收到了自己的回應(yīng)

到這里, 就可以認(rèn)為客戶端與服務(wù)器已經(jīng)建立了連接.

再來看個(gè)圖.

剛開始, 客戶端和服務(wù)器都處于 CLOSE 狀態(tài).

此時(shí), 客戶端向服務(wù)器主動(dòng)發(fā)出連接請(qǐng)求, 服務(wù)器被動(dòng)接受連接請(qǐng)求.

1, TCP服務(wù)器進(jìn)程先創(chuàng)建傳輸控制塊TCB, 時(shí)刻準(zhǔn)備接受客戶端進(jìn)程的連接請(qǐng)求, 此時(shí)服務(wù)器就進(jìn)入了 LISTEN(監(jiān)聽)狀態(tài)

2, TCP客戶端進(jìn)程也是先創(chuàng)建傳輸控制塊TCB, 然后向服務(wù)器發(fā)出連接請(qǐng)求報(bào)文,此時(shí)報(bào)文首部中的同步標(biāo)志位SYN=1, 同時(shí)選擇一個(gè)初始序列號(hào) seq = x, 此時(shí),TCP客戶端進(jìn)程進(jìn)入了 SYN-SENT(同步已發(fā)送狀態(tài))狀態(tài)。TCP規(guī)定, SYN報(bào)文段(SYN=1的報(bào)文段)不能攜帶數(shù)據(jù),但需要消耗掉一個(gè)序號(hào)。

3, TCP服務(wù)器收到請(qǐng)求報(bào)文后, 如果同意連接, 則發(fā)出確認(rèn)報(bào)文。確認(rèn)報(bào)文中的 ACK=1, SYN=1, 確認(rèn)序號(hào)是 x+1, 同時(shí)也要為自己初始化一個(gè)序列號(hào) seq = y, 此時(shí), TCP服務(wù)器進(jìn)程進(jìn)入了SYN-RCVD(同步收到)狀態(tài)。這個(gè)報(bào)文也不能攜帶數(shù)據(jù), 但是同樣要消耗一個(gè)序號(hào)。

4, TCP客戶端進(jìn)程收到確認(rèn)后還, 要向服務(wù)器給出確認(rèn)。確認(rèn)報(bào)文的ACK=1,確認(rèn)序號(hào)是 y+1,自己的序列號(hào)是 x+1.

5, 此時(shí),TCP連接建立,客戶端進(jìn)入ESTABLISHED(已建立連接)狀態(tài)。當(dāng)服務(wù)器收到客戶端的確認(rèn)后也進(jìn)入ESTABLISHED狀態(tài),此后雙方就可以開始通信了。

為什么不用兩次?

主要是為了防止已經(jīng)失效的連接請(qǐng)求報(bào)文突然又傳送到了服務(wù)器,從而產(chǎn)生錯(cuò)誤。如果使用的是兩次握手建立連接,假設(shè)有這樣一種場景,客戶端發(fā)送的第一個(gè)請(qǐng)求連接并且沒有丟失,只是因?yàn)樵诰W(wǎng)絡(luò)中滯留的時(shí)間太長了,由于TCP的客戶端遲遲沒有收到確認(rèn)報(bào)文,以為服務(wù)器沒有收到,此時(shí)重新向服務(wù)器發(fā)送這條報(bào)文,此后客戶端和服務(wù)器經(jīng)過兩次握手完成連接,傳輸數(shù)據(jù),然后關(guān)閉連接。此時(shí)之前滯留的那一次請(qǐng)求連接,因?yàn)榫W(wǎng)絡(luò)通暢了, 到達(dá)了服務(wù)器,這個(gè)報(bào)文本該是失效的,但是,兩次握手的機(jī)制將會(huì)讓客戶端和服務(wù)器再次建立連接,這將導(dǎo)致不必要的錯(cuò)誤和資源的費(fèi)。

如果采用的是三次握手,就算是那一次失效的報(bào)文傳送過來了,服務(wù)端接受到了那條失效報(bào)文并且回復(fù)了確認(rèn)報(bào)文,但是客戶端不會(huì)再次發(fā)出確認(rèn)。由于服務(wù)器收不到確認(rèn),就知道客戶端并沒有請(qǐng)求連接。

為什么不用四次?

因?yàn)槿我呀?jīng)可以滿足需要了, 四次就多余了.

再來看看何為四次揮手.

數(shù)據(jù)傳輸完畢后,雙方都可以釋放連接.

此時(shí)客戶端和服務(wù)器都是處于ESTABLISHED狀態(tài),然后客戶端主動(dòng)斷開連接,服務(wù)器被動(dòng)斷開連接.

1, 客戶端進(jìn)程發(fā)出連接釋放報(bào)文,并且停止發(fā)送數(shù)據(jù)。

釋放數(shù)據(jù)報(bào)文首部,FIN=1,其序列號(hào)為seq=u(等于前面已經(jīng)傳送過來的數(shù)據(jù)的最后一個(gè)字節(jié)的序號(hào)加1),此時(shí)客戶端進(jìn)入FIN-WAIT-1(終止等待1)狀態(tài)。 TCP規(guī)定,FIN報(bào)文段即使不攜帶數(shù)據(jù),也要消耗一個(gè)序號(hào)。

2, 服務(wù)器收到連接釋放報(bào)文,發(fā)出確認(rèn)報(bào)文,ACK=1,確認(rèn)序號(hào)為 u+1,并且?guī)献约旱男蛄刑?hào)seq=v,此時(shí)服務(wù)端就進(jìn)入了CLOSE-WAIT(關(guān)閉等待)狀態(tài)。

TCP服務(wù)器通知高層的應(yīng)用進(jìn)程,客戶端向服務(wù)器的方向就釋放了,這時(shí)候處于半關(guān)閉狀態(tài),即客戶端已經(jīng)沒有數(shù)據(jù)要發(fā)送了,但是服務(wù)器若發(fā)送數(shù)據(jù),客戶端依然要接受。這個(gè)狀態(tài)還要持續(xù)一段時(shí)間,也就是整個(gè)CLOSE-WAIT狀態(tài)持續(xù)的時(shí)間。

3, 客戶端收到服務(wù)器的確認(rèn)請(qǐng)求后,此時(shí)客戶端就進(jìn)入FIN-WAIT-2(終止等待2)狀態(tài),等待服務(wù)器發(fā)送連接釋放報(bào)文(在這之前還需要接受服務(wù)器發(fā)送的最終數(shù)據(jù))

4, 服務(wù)器將最后的數(shù)據(jù)發(fā)送完畢后,就向客戶端發(fā)送連接釋放報(bào)文,FIN=1,確認(rèn)序號(hào)為v+1,由于在半關(guān)閉狀態(tài),服務(wù)器很可能又發(fā)送了一些數(shù)據(jù),假定此時(shí)的序列號(hào)為seq=w,此時(shí),服務(wù)器就進(jìn)入了LAST-ACK(最后確認(rèn))狀態(tài),等待客戶端的確認(rèn)。

5, 客戶端收到服務(wù)器的連接釋放報(bào)文后,必須發(fā)出確認(rèn),ACK=1,確認(rèn)序號(hào)為w+1,而自己的序列號(hào)是u+1,此時(shí),客戶端就進(jìn)入了TIME-WAIT(時(shí)間等待)狀態(tài)。注意此時(shí)TCP連接還沒有釋放,必須經(jīng)過2?MSL(最長報(bào)文段壽命)的時(shí)間后,當(dāng)客戶端撤銷相應(yīng)的TCB后,才進(jìn)入CLOSED狀態(tài)。

6, 服務(wù)器只要收到了客戶端發(fā)出的確認(rèn),立即進(jìn)入CLOSED狀態(tài)。同樣,撤銷TCB后,就結(jié)束了這次的TCP連接。可以看到,服務(wù)器結(jié)束TCP連接的時(shí)間要比客戶端早一些。

再來看一張圖.

為什么最后客戶端還要等待 2*MSL的時(shí)間呢?

MSL(Maximum Segment Lifetime),TCP允許不同的實(shí)現(xiàn)可以設(shè)置不同的MSL值。

第一,保證客戶端發(fā)送的最后一個(gè)ACK報(bào)文能夠到達(dá)服務(wù)器,因?yàn)檫@個(gè)ACK報(bào)文可能丟失,站在服務(wù)器的角度看來,我已經(jīng)發(fā)送了FIN+ACK報(bào)文請(qǐng)求斷開了,客戶端還沒有給我回應(yīng),應(yīng)該是我發(fā)送的請(qǐng)求斷開報(bào)文它沒有收到,于是服務(wù)器又會(huì)重新發(fā)送一次,而客戶端就能在這個(gè)2MSL時(shí)間段內(nèi)收到這個(gè)重傳的報(bào)文,接著給出回應(yīng)報(bào)文,并且會(huì)重啟2MSL計(jì)時(shí)器。

第二,防止類似與“三次握手”中提到了的“已經(jīng)失效的連接請(qǐng)求報(bào)文段”出現(xiàn)在本連接中。客戶端發(fā)送完最后一個(gè)確認(rèn)報(bào)文后,在這個(gè)2MSL時(shí)間中,就可以使本連接持續(xù)的時(shí)間內(nèi)所產(chǎn)生的所有報(bào)文段都從網(wǎng)絡(luò)中消失。這樣新的連接中不會(huì)出現(xiàn)舊連接的請(qǐng)求報(bào)文。

為什么建立連接是三次握手,關(guān)閉連接確是四次揮手呢?

建立連接的時(shí)候, 服務(wù)器在LISTEN狀態(tài)下,收到建立連接請(qǐng)求的SYN報(bào)文后,把ACK和SYN放在一個(gè)報(bào)文里發(fā)送給客戶端。 而關(guān)閉連接時(shí),服務(wù)器收到對(duì)方的FIN報(bào)文時(shí),僅僅表示對(duì)方不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù),而自己也未必全部數(shù)據(jù)都發(fā)送給對(duì)方了,所以己方可以立即關(guān)閉,也可以發(fā)送一些數(shù)據(jù)給對(duì)方后,再發(fā)送FIN報(bào)文給對(duì)方來表示同意現(xiàn)在關(guān)閉連接,因此,己方ACK和FIN一般都會(huì)分開發(fā)送,從而導(dǎo)致多了一次。

如果已經(jīng)建立了連接, 但是客戶端突發(fā)故障了怎么辦?

TCP設(shè)有一個(gè)保活計(jì)時(shí)器,顯然,客戶端如果出現(xiàn)故障,服務(wù)器不能一直等下去,白白浪費(fèi)資源。服務(wù)器每收到一次客戶端的請(qǐng)求后都會(huì)重新復(fù)位這個(gè)計(jì)時(shí)器,時(shí)間通常是設(shè)置為2小時(shí),若兩小時(shí)還沒有收到客戶端的任何數(shù)據(jù),服務(wù)器就會(huì)發(fā)送一個(gè)探測報(bào)文段,以后每隔75分鐘發(fā)送一次。若一連發(fā)送10個(gè)探測報(bào)文仍然沒反應(yīng),服務(wù)器就認(rèn)為客戶端出了故障,接著就關(guān)閉連接。

理解TIME_WAIT狀態(tài)

可以做一個(gè)實(shí)驗(yàn), 先運(yùn)行server, 再運(yùn)行client連接server, 然后斷開server, 再立馬運(yùn)行server.

我們會(huì)發(fā)現(xiàn):

綁定的時(shí)候出了問題.

這是因?yàn)?雖然server應(yīng)用程序終止了,但TCP協(xié)議層的連接并沒有完全斷開,因此不能再次監(jiān)聽綁定同樣的server端口.

TCP協(xié)議規(guī)定,主動(dòng)關(guān)閉連接的一方要處于TIME_ WAIT狀態(tài),等待2*MSL(maximum segment lifetime)的時(shí)間后才能回到CLOSED狀態(tài).

我們使用Ctrl-C終止了server, 所以server是主動(dòng)關(guān)閉連接的一方, 在TIME_WAIT期間仍然不能再次監(jiān)聽同樣的server端口

MSL在RFC1122中規(guī)定為兩分鐘,但是各操作系統(tǒng)的實(shí)現(xiàn)不同, 在Centos7上默認(rèn)配置的值是60s;

可以通過 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看MSL的值

解決TIME_WAIT引起的bind失敗問題

在server的TCP連接沒有完全斷開之前不允許重新監(jiān)聽, 某些情況下可能是不合理的.

比如:

服務(wù)器需要處理非常大量的客戶端的連接(每個(gè)連接的生存時(shí)間可能很短, 但是每秒都有大量的客戶端來請(qǐng)求).

這個(gè)時(shí)候如果由服務(wù)器端主動(dòng)關(guān)閉連接(比如某些客戶端不活躍, 就需要被服務(wù)器端主動(dòng)清理掉), 就會(huì)產(chǎn)生大量TIME_WAIT連接.

由于我們的請(qǐng)求量很大, 就可能導(dǎo)致TIME_WAIT的連接數(shù)很多, 導(dǎo)致服務(wù)器的端口不夠用, 無法處理新的連接.

解決方法:

- 使用setsockopt()設(shè)置socket描述符的選項(xiàng)SO_REUSEADDR為1, 表示允許創(chuàng)建端口號(hào)相同但I(xiàn)P地址不同的多個(gè)socket描述符.

用法:

在server代碼的socket()和bind()調(diào)用之間插入如下代碼

int opt = 1;

setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

確認(rèn)應(yīng)答機(jī)制(ACK機(jī)制)

TCP將每個(gè)字節(jié)的數(shù)據(jù)都進(jìn)行了編號(hào), 即為序列號(hào).

每一個(gè)ACK都帶有對(duì)應(yīng)的確認(rèn)序列號(hào), 意思是告訴發(fā)送者, 我已經(jīng)收到了哪些數(shù)據(jù); 下一次你要從哪里開始發(fā).

比如, 客戶端向服務(wù)器發(fā)送了1005字節(jié)的數(shù)據(jù), 服務(wù)器返回給客戶端的確認(rèn)序號(hào)是1003, 那么說明服務(wù)器只收到了1-1002的數(shù)據(jù).

1003, 1004, 1005都沒收到.

此時(shí)客戶端就會(huì)從1003開始重發(fā).

超時(shí)重傳機(jī)制

主機(jī)A發(fā)送數(shù)據(jù)給B之后, 可能因?yàn)榫W(wǎng)絡(luò)擁堵等原因, 數(shù)據(jù)無法到達(dá)主機(jī)B

如果主機(jī)A在一個(gè)特定時(shí)間間隔內(nèi)沒有收到B發(fā)來的確認(rèn)應(yīng)答, 就會(huì)進(jìn)行重發(fā)

但是主機(jī)A沒收到確認(rèn)應(yīng)答也可能是ACK丟失了.

這種情況下, 主機(jī)B會(huì)收到很多重復(fù)數(shù)據(jù).

那么TCP協(xié)議需要識(shí)別出哪些包是重復(fù)的, 并且把重復(fù)的丟棄.

這時(shí)候利用前面提到的序列號(hào), 就可以很容易做到去重.

超時(shí)時(shí)間如何確定?

最理想的情況下, 找到一個(gè)最小的時(shí)間, 保證 “確認(rèn)應(yīng)答一定能在這個(gè)時(shí)間內(nèi)返回”.

但是這個(gè)時(shí)間的長短, 隨著網(wǎng)絡(luò)環(huán)境的不同, 是有差異的.

如果超時(shí)時(shí)間設(shè)的太長, 會(huì)影響整體的重傳效率; 如果超時(shí)時(shí)間設(shè)的太短, 有可能會(huì)頻繁發(fā)送重復(fù)的包.

TCP為了保證任何環(huán)境下都能保持較高性能的通信, 因此會(huì)動(dòng)態(tài)計(jì)算這個(gè)最大超時(shí)時(shí)間.

Linux中(BSD Unix和Windows也是如此), 超時(shí)以500ms為一個(gè)單位進(jìn)行控制, 每次判定超時(shí)重發(fā)的超時(shí)時(shí)間都是500ms的整數(shù)倍.

如果重發(fā)一次之后, 仍然得不到應(yīng)答, 等待 2*500ms 后再進(jìn)行重傳. 如果仍然得不到應(yīng)答, 等待 4*500ms 進(jìn)行重傳.

依次類推, 以指數(shù)形式遞增. 累計(jì)到一定的重傳次數(shù), TCP認(rèn)為網(wǎng)絡(luò)異常或者對(duì)端主機(jī)出現(xiàn)異常, 強(qiáng)制關(guān)閉連接.

滑動(dòng)窗口

剛才我們討論了確認(rèn)應(yīng)答機(jī)制, 對(duì)每一個(gè)發(fā)送的數(shù)據(jù)段, 都要給一個(gè)ACK確認(rèn)應(yīng)答. 收到ACK后再發(fā)送下一個(gè)數(shù)據(jù)段.

這樣做有一個(gè)比較大的缺點(diǎn), 就是性能較差. 尤其是數(shù)據(jù)往返時(shí)間較長的時(shí)候.

那么我們可不可以一次發(fā)送多個(gè)數(shù)據(jù)段呢?

例如這樣:

一個(gè)概念: 窗口

窗口大小指的是無需等待確認(rèn)應(yīng)答就可以繼續(xù)發(fā)送數(shù)據(jù)的最大值.

上圖的窗口大小就是4000個(gè)字節(jié) (四個(gè)段).

發(fā)送前四個(gè)段的時(shí)候, 不需要等待任何ACK, 直接發(fā)送

收到第一個(gè)ACK確認(rèn)應(yīng)答后, 窗口向后移動(dòng), 繼續(xù)發(fā)送第五六七八段的數(shù)據(jù)…

因?yàn)檫@個(gè)窗口不斷向后滑動(dòng), 所以叫做滑動(dòng)窗口.

操作系統(tǒng)內(nèi)核為了維護(hù)這個(gè)滑動(dòng)窗口, 需要開辟發(fā)送緩沖區(qū)來記錄當(dāng)前還有哪些數(shù)據(jù)沒有應(yīng)答

只有ACK確認(rèn)應(yīng)答過的數(shù)據(jù), 才能從緩沖區(qū)刪掉.

如果出現(xiàn)了丟包, 那么該如何進(jìn)行重傳呢?

此時(shí)分兩種情況討論:

1, 數(shù)據(jù)包已經(jīng)收到, 但確認(rèn)應(yīng)答ACK丟了.

這種情況下, 部分ACK丟失并無大礙, 因?yàn)檫€可以通過后續(xù)的ACK來確認(rèn)對(duì)方已經(jīng)收到了哪些數(shù)據(jù)包.

2, 數(shù)據(jù)包丟失

當(dāng)某一段報(bào)文丟失之后, 發(fā)送端會(huì)一直收到 1001 這樣的ACK, 就像是在提醒發(fā)送端 “我想要的是 1001”

如果發(fā)送端主機(jī)連續(xù)三次收到了同樣一個(gè) “1001” 這樣的應(yīng)答, 就會(huì)將對(duì)應(yīng)的數(shù)據(jù) 1001 - 2000 重新發(fā)送

這個(gè)時(shí)候接收端收到了 1001 之后, 再次返回的ACK就是7001了

因?yàn)?001 - 7000接收端其實(shí)之前就已經(jīng)收到了, 被放到了接收端操作系統(tǒng)內(nèi)核的接收緩沖區(qū)中.

這種機(jī)制被稱為 “高速重發(fā)控制” ( 也叫 “快重傳” )

流量控制

接收端處理數(shù)據(jù)的速度是有限的. 如果發(fā)送端發(fā)的太快, 導(dǎo)致接收端的緩沖區(qū)被填滿, 這個(gè)時(shí)候如果發(fā)送端繼續(xù)發(fā)送, 就會(huì)造成丟包, 進(jìn)而引起丟包重傳等一系列連鎖反應(yīng).

因此TCP支持根據(jù)接收端的處理能力, 來決定發(fā)送端的發(fā)送速度.

這個(gè)機(jī)制就叫做 流量控制(Flow Control)

接收端將自己可以接收的緩沖區(qū)大小放入 TCP 首部中的 “窗口大小” 字段,

通過ACK通知發(fā)送端;

窗口大小越大, 說明網(wǎng)絡(luò)的吞吐量越高;

接收端一旦發(fā)現(xiàn)自己的緩沖區(qū)快滿了, 就會(huì)將窗口大小設(shè)置成一個(gè)更小的值通知給發(fā)送端;

發(fā)送端接受到這個(gè)窗口大小的通知之后, 就會(huì)減慢自己的發(fā)送速度;

如果接收端緩沖區(qū)滿了, 就會(huì)將窗口置為0;

這時(shí)發(fā)送方不再發(fā)送數(shù)據(jù), 但是需要定期發(fā)送一個(gè)窗口探測數(shù)據(jù)段, 讓接收端把窗口大小再告訴發(fā)送端.

那么接收端如何把窗口大小告訴發(fā)送端呢?

我們的TCP首部中, 有一個(gè)16位窗口大小字段, 就存放了窗口大小的信息;

16位數(shù)字最大表示65536, 那么TCP窗口最大就是65536字節(jié)么?

實(shí)際上, TCP首部40字節(jié)選項(xiàng)中還包含了一個(gè)窗口擴(kuò)大因子M, 實(shí)際窗口大小是窗口字段的值左移 M 位(左移一位相當(dāng)于乘以2).

擁塞控制

雖然TCP有了滑動(dòng)窗口這個(gè)大殺器, 能夠高效可靠地發(fā)送大量數(shù)據(jù).

但是如果在剛開始就發(fā)送大量的數(shù)據(jù), 仍然可能引發(fā)一些問題.

因?yàn)榫W(wǎng)絡(luò)上有很多計(jì)算機(jī), 可能當(dāng)前的網(wǎng)絡(luò)狀態(tài)已經(jīng)比較擁堵.

在不清楚當(dāng)前網(wǎng)絡(luò)狀態(tài)的情況下, 貿(mào)然發(fā)送大量數(shù)據(jù), 很有可能雪上加霜.

因此, TCP引入 慢啟動(dòng) 機(jī)制, 先發(fā)少量的數(shù)據(jù), 探探路, 摸清當(dāng)前的網(wǎng)絡(luò)擁堵狀態(tài)以后, 再?zèng)Q定按照多大的速度傳輸數(shù)據(jù).

在此引入一個(gè)概念 擁塞窗口

發(fā)送開始的時(shí)候, 定義擁塞窗口大小為1;

每次收到一個(gè)ACK應(yīng)答, 擁塞窗口加1;

每次發(fā)送數(shù)據(jù)包的時(shí)候, 將擁塞窗口和接收端主機(jī)反饋的窗口大小做比較, 取較小的值作為實(shí)際發(fā)送的窗口

像上面這樣的擁塞窗口增長速度, 是指數(shù)級(jí)別的.

“慢啟動(dòng)” 只是指初使時(shí)慢, 但是增長速度非常快.

為了不增長得那么快, 此處引入一個(gè)名詞叫做慢啟動(dòng)的閾值, 當(dāng)擁塞窗口的大小超過這個(gè)閾值的時(shí)候, 不再按照指數(shù)方式增長, 而是按照線性方式增長.

當(dāng)TCP開始啟動(dòng)的時(shí)候, 慢啟動(dòng)閾值等于窗口最大值

在每次超時(shí)重發(fā)的時(shí)候, 慢啟動(dòng)閾值會(huì)變成原來的一半, 同時(shí)擁塞窗口置回1

少量的丟包, 我們僅僅是觸發(fā)超時(shí)重傳;

大量的丟包, 我們就認(rèn)為是網(wǎng)絡(luò)擁塞;

當(dāng)TCP通信開始后, 網(wǎng)絡(luò)吞吐量會(huì)逐漸上升;

隨著網(wǎng)絡(luò)發(fā)生擁堵, 吞吐量會(huì)立刻下降.

擁塞控制, 歸根結(jié)底是TCP協(xié)議想盡可能快的把數(shù)據(jù)傳輸給對(duì)方, 但是又要避免給網(wǎng)絡(luò)造成太大壓力的折中方案.

延遲應(yīng)答

如果接收數(shù)據(jù)的主機(jī)立刻返回ACK應(yīng)答, 這時(shí)候返回的窗口可能比較小.

假設(shè)接收端緩沖區(qū)為1M. 一次收到了500K的數(shù)據(jù);

如果立刻應(yīng)答, 返回的窗口大小就是500K;

但實(shí)際上可能處理端處理的速度很快, 10ms之內(nèi)就把500K數(shù)據(jù)從緩沖區(qū)消費(fèi)掉了; 在這種情況下, 接收端處理還遠(yuǎn)沒有達(dá)到自己的極限, 即使窗口再放大一些, 也能處理過來;

如果接收端稍微等一會(huì)兒再應(yīng)答, 比如等待200ms再應(yīng)答, 那么這個(gè)時(shí)候返回的窗口大小就是1M

窗口越大, 網(wǎng)絡(luò)吞吐量就越大, 傳輸效率就越高.

TCP的目標(biāo)是在保證網(wǎng)絡(luò)不擁堵的情況下盡量提高傳輸效率;

那么所有的數(shù)據(jù)包都可以延遲應(yīng)答么?

肯定也不是

有兩個(gè)限制

數(shù)量限制: 每隔N個(gè)包就應(yīng)答一次

時(shí)間限制: 超過最大延遲時(shí)間就應(yīng)答一次

具體的數(shù)量N和最大延遲時(shí)間, 依操作系統(tǒng)不同也有差異

一般 N 取2, 最大延遲時(shí)間取200ms

捎帶應(yīng)答

在延遲應(yīng)答的基礎(chǔ)上, 我們發(fā)現(xiàn), 很多情況下

客戶端和服務(wù)器在應(yīng)用層也是 “一發(fā)一收” 的

意味著客戶端給服務(wù)器說了 “How are you”

服務(wù)器也會(huì)給客戶端回一個(gè) “Fine, thank you”

那么這個(gè)時(shí)候ACK就可以搭順風(fēng)車, 和服務(wù)器回應(yīng)的 “Fine, thank you” 一起發(fā)送給客戶端

面向字節(jié)流

創(chuàng)建一個(gè)TCP的socket, 同時(shí)在內(nèi)核中創(chuàng)建一個(gè) 發(fā)送緩沖區(qū) 和一個(gè) 接收緩沖區(qū);

調(diào)用write時(shí), 數(shù)據(jù)會(huì)先寫入發(fā)送緩沖區(qū)中;

如果發(fā)送的字節(jié)數(shù)太大, 會(huì)被拆分成多個(gè)TCP的數(shù)據(jù)包發(fā)出;

如果發(fā)送的字節(jié)數(shù)太小, 就會(huì)先在緩沖區(qū)里等待, 等到緩沖區(qū)大小差不多了, 或者到了其他合適的時(shí)機(jī)再發(fā)送出去;

接收數(shù)據(jù)的時(shí)候, 數(shù)據(jù)也是從網(wǎng)卡驅(qū)動(dòng)程序到達(dá)內(nèi)核的接收緩沖區(qū);

然后應(yīng)用程序可以調(diào)用read從接收緩沖區(qū)拿數(shù)據(jù);

另一方面, TCP的一個(gè)連接, 既有發(fā)送緩沖區(qū), 也有接收緩沖區(qū),

那么對(duì)于這一個(gè)連接, 既可以讀數(shù)據(jù), 也可以寫數(shù)據(jù), 這個(gè)概念叫做 全雙工

由于緩沖區(qū)的存在, 所以TCP程序的讀和寫不需要一一匹配

例如:

寫100個(gè)字節(jié)的數(shù)據(jù), 可以調(diào)用一次write寫100個(gè)字節(jié), 也可以調(diào)用100次write, 每次寫一個(gè)字節(jié);

讀100個(gè)字節(jié)數(shù)據(jù)時(shí), 也完全不需要考慮寫的時(shí)候是怎么寫的, 既可以一次read 100個(gè)字節(jié), 也可以一次read一個(gè)字節(jié), 重復(fù)100次;

粘包問題

首先要明確, 粘包問題中的 “包”, 是指應(yīng)用層的數(shù)據(jù)包.

在TCP的協(xié)議頭中, 沒有如同UDP一樣的 “報(bào)文長度” 字段

但是有一個(gè)序號(hào)字段.

站在傳輸層的角度, TCP是一個(gè)一個(gè)報(bào)文傳過來的. 按照序號(hào)排好序放在緩沖區(qū)中.

站在應(yīng)用層的角度, 看到的只是一串連續(xù)的字節(jié)數(shù)據(jù).

那么應(yīng)用程序看到了這一連串的字節(jié)數(shù)據(jù), 就不知道從哪個(gè)部分開始到哪個(gè)部分是一個(gè)完整的應(yīng)用層數(shù)據(jù)包.

此時(shí)數(shù)據(jù)之間就沒有了邊界, 就產(chǎn)生了粘包問題

那么如何避免粘包問題呢?

歸根結(jié)底就是一句話, 明確兩個(gè)包之間的邊界

對(duì)于定長的包

- 保證每次都按固定大小讀取即可

例如上面的Request結(jié)構(gòu), 是固定大小的, 那么就從緩沖區(qū)從頭開始按sizeof(Request)依次讀取即可

對(duì)于變長的包

- 可以在數(shù)據(jù)包的頭部, 約定一個(gè)數(shù)據(jù)包總長度的字段, 從而就知道了包的結(jié)束位置

還可以在包和包之間使用明確的分隔符來作為邊界(應(yīng)用層協(xié)議, 是程序員自己來定的, 只要保證分隔符不和正文沖突即可)

對(duì)于UDP協(xié)議來說, 是否也存在 “粘包問題” 呢?

對(duì)于UDP, 如果還沒有向上層交付數(shù)據(jù), UDP的報(bào)文長度仍然存在.

同時(shí), UDP是一個(gè)一個(gè)把數(shù)據(jù)交付給應(yīng)用層的, 就有很明確的數(shù)據(jù)邊界.

站在應(yīng)用層的角度, 使用UDP的時(shí)候, 要么收到完整的UDP報(bào)文, 要么不收.

不會(huì)出現(xiàn)收到 “半個(gè)” 的情況.

TCP 異常情況

進(jìn)程終止: 進(jìn)程終止會(huì)釋放文件描述符, 仍然可以發(fā)送FIN. 和正常關(guān)閉沒有什么區(qū)別.

機(jī)器重啟: 和進(jìn)程終止的情況相同.

機(jī)器掉電/網(wǎng)線斷開: 接收端認(rèn)為連接還在, 一旦接收端有寫入操作, 接收端發(fā)現(xiàn)連接已經(jīng)不在了, 就會(huì)進(jìn)行 reset. 即使沒有寫入操作, TCP自己也內(nèi)置了一個(gè)保活定時(shí)器, 會(huì)定期詢問對(duì)方是否還在. 如果對(duì)方不在, 也會(huì)把連接釋放.

另外, 應(yīng)用層的某些協(xié)議, 也有一些這樣的檢測機(jī)制.

例如HTTP長連接中, 也會(huì)定期檢測對(duì)方的狀態(tài).

例如QQ, 在QQ斷線之后, 也會(huì)定期嘗試重新連接.

TCP 小結(jié)

為什么TCP這么復(fù)雜?

因?yàn)榧纫WC可靠性, 同時(shí)又要盡可能提高性能.

保證可靠性的機(jī)制

校驗(yàn)和

序列號(hào)(按序到達(dá))

確認(rèn)應(yīng)答

超時(shí)重傳

連接管理

流量控制

擁塞控制

提高性能的機(jī)制

滑動(dòng)窗口

快速重傳

延遲應(yīng)答

捎帶應(yīng)答

定時(shí)器

超時(shí)重傳定時(shí)器

保活定時(shí)器

TIME_WAIT定時(shí)器

基于 TCP 的應(yīng)用層協(xié)議

HTTP

HTTPS

SSH

Telnet

FTP

SMTP

當(dāng)然, 也包括我們自己寫TCP程序時(shí)自定義的應(yīng)用層協(xié)議

TCP 和 UDP 對(duì)比

我們說了TCP是可靠連接, 那么是不是TCP一定就優(yōu)于UDP呢?

TCP和UDP之間的優(yōu)點(diǎn)和缺點(diǎn), 不能簡單絕對(duì)地進(jìn)行比較

TCP用于可靠傳輸?shù)那闆r, 應(yīng)用于文件傳輸, 重要狀態(tài)更新等場景

UDP用于對(duì)高速傳輸和實(shí)時(shí)性要求較高的通信領(lǐng)域

例如, 早期的QQ, 視頻傳輸?shù)? 另外UDP可以用于廣播

歸根結(jié)底, TCP和UDP都是一種工具, 什么時(shí)機(jī)用, 具體怎么用, 還是要根據(jù)具體的需求場景去決定.

————————————————

版權(quán)聲明:本文為CSDN博主「rugu_xxx」的原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/sinat_36629696/article/details/80740678

總結(jié)

以上是生活随笔為你收集整理的win2008 查询 tcp连接失败_TCP详解(转)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。