上篇文章:
網絡編程—TCP/IP模型(UDP協議與自定義協議)https://blog.csdn.net/sniper_fandc/article/details/146923934?fromshare=blogdetail&sharetype=blogdetail&sharerId=146923934&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link
目錄
1 TCP協議特點與格式
2 確認應答機制(保證可靠傳輸)
3 超時重傳機制(保證可靠傳輸)
4 連接管理機制(保證可靠傳輸)
5 滑動窗口機制(保證效率)
6 流量控制機制(保證可靠傳輸)
7 擁塞控制機制(保證可靠傳輸)
8 延遲應答機制(保證效率)
9 捎帶應答機制(保證效率)
10 粘包問題
11 TCP連接異常情況
12 常見問題
1 TCP協議特點與格式
????????TCP協議的特點是有連接、可靠傳輸(核心)、面向字節流、有接收緩沖區和發送緩沖區、全雙工。
????????注意:面向字節流和面向數據報是從應用層角度來談,而無論UDP還是TCP發送的數據都是一份一份的報文,只是接收的方式不同。UDP一次必須從接受緩沖區接收一份完整的數據報,而TCP采用字節數組,接收的數據長度以字節數組長度為準,因此才講TCP是面向字節流的。
????????TCP協議格式如下:
????????32位序號:TCP協議會將數據按報文段段進行編號,編號內容是本報文段第一個字節的序號。
????????32位確認序號:希望接收的下一個報文段的第一個字節的編號。
????????4位首部長度:單位4字節,4位比特位最大表示1111=15,即TCP首部長度最大60字節(15*4=60),除了20字節固定首部長度,余下40字節是選項的最大長度。
????????保留6位:預留空間,目前無用處,全為0 。
????????6個標志位(每個標志1比特,0表示不是該類型,1表示是該類型):URG緊急指針是否有效、ACK確認號是否有效、PSH提示接收端應用程序立刻從TCP緩沖區把數據讀走、RST對方要求重新建立連接(攜帶RST標識的稱為復位報文段)、SYN請求建立連接(攜帶SYN標識的稱為同步報文段)、FIN通知對方本端要關閉(攜帶FIN標識的為結束報文段)。
????????16位窗口大小:表示發送該TCP報文的接收窗口還可以接收多少字節的數據量,用于TCP的流量控制(在ACK報文生效,16位不一定只能表示64kb,實際上選項中有窗口擴大因子,實際表示的窗口大小是64kb*窗口擴大因子)。
????????16位校驗和:發送端填充,CRC校驗。接收端校驗不通過,則認為數據有問題。此處的校驗和不光包含TCP首部,也包含TCP數據部分。
????????16位緊急指針:標識哪部分數據是緊急數據,即使接收方窗口大小為0,也可以發送緊急數據,因為緊急數據無須緩存。
2 確認應答機制(保證可靠傳輸)
????????可靠傳輸并不是指安全傳輸(這需要加密機制的保障),而是指數據發送方可以知道傳輸的數據是否被接收方收到。
????????因此TCP為了確保可靠傳輸,為數據進行編號,即32位序號seq,而接收方如果收到TCP報文段,就會發送確認報文ack,確認號ack是希望下一次收到的編號。
????????如上圖,假設報文段長度1000,第一個報文段的序號seq=1,則該報文段的最后一個字節的序號是1000,因此ack是下一次希望收到的序號1001(1000+1)。而第二個報文段的第一個字節的序號seq=1001,因此如果接收方順利收到,就應該發送ack=2001。
????????通過一問一答的方式,發送方如果收到接收方的確認報文,就知道數據已經順利被收到了。
3 超時重傳機制(保證可靠傳輸)
????????因為網絡環境的復雜,所有類型的報文都可能會丟失,因此如果確認應答機制出現差錯,比如發送的TCP報文段丟失,那么接收端就不會收到報文段也就無法發送ack,于是超時重傳機制便可以保證確認應答機制的可靠。
????????當TCP報文段丟失,發送端隔上特定時間段沒有收到ack,就會進行超時重傳。
????????當ack報文丟失時,發送端也會進行超時重傳,但是此時接收端就會收到重復的數據,由于數據都進行了編號,因此接收端就會丟棄先前重復的數據。
????????注意:超時重傳的時間段如何確定?如果第一次超時重傳后,又發生了丟包,一般網絡丟包的概率很小,連續多次丟包的概率更小,概率更小的時間都能發生,說明此時網絡已經非常擁堵了,頻繁的超時重傳無意義,于是第二次超時重傳的間隔時間段一定要比第一次更長。Linux中(BSD Unix和Windows也是如此),超時以500ms為一個單位進行控制,每次判定超時重發的超時時間都是500ms的整數倍。如果重發一次之后,仍然得不到應答,等待2*500ms后再進行重傳。如果仍然得不到應答,等待 4*500ms 進行重傳。依次類推,以指數形式遞增。累計到一定的重傳次數,TCP認為網絡或者對端主機出現異常,強制關閉連接。
4 連接管理機制(保證可靠傳輸)
????????TCP協議是通過“三報文握手四報文揮手”來管理連接的。三報文握手是建立連接的過程,主動發起建立連接請求的是客戶端,被動建立連接的是服務端,服務端持續運行處于監聽建立連接申請的狀態。
????????1.建立連接:客戶端發送SYN報文,隨之進入SYN_SENT狀態;服務端接收到SYN后,發送ACK;同時服務端發送SYN,進入SYN_RCVD狀態;客戶端接收到SYN后,發送ACK,并進入ESTABLISHED狀態;服務端接收到ACK后也進入ESTABLISHED狀態。至此連接建立完成。
????????由于封裝分用操作開銷大,因此可以把服務端發送ACK和發送SYN合并成一個TCP報文段,因此建立連接的過程有3個報文段的交互,故稱為三次握手建立連接。
????????注意:為什么不能是兩次握手和四次握手?1.可以是四次握手,但是沒必要,見上述合并報文的含義。2.不可以是兩次握手,防止已經失效的SYN報文到達服務端,從而維護已經失效的連接(當客戶端發送的SYN因為網絡問題滯留在某個節點,而客戶端觸發超時重傳,又發送SYN報文建立了新的連接后,滯留失效的SYN到達服務端導致服務端無法識別是否要繼續建立連接),實際上第一次第二次握手驗證了客戶端的數據發送能力和接收能力沒有問題,第二次第三次握手驗證了服務端數據發送能力和接收能力沒有問題。
????????2.數據交互:數據交互會重復多次請求和響應。而數據交互期間起始數據的序號不一定是從1開始,而是雙方進行約定,因此三次握手還有一個重要的作用就是協商重要參數配置。
????????3.斷開連接:客戶端希望斷開連接,發送FIN報文,進入FIN_WAIT_1狀態;服務端接收到FIN報文,發送ACK報文,進入CLOSE_WAIT狀態;客戶端接收ACK報文,進入FIN_WAIT_2狀態;經過一段時間后,服務端發送FIN報文,進入LAST_ACK狀態;客戶端接收到FIN報文,發送ACK報文,經過2MSL時間后進入CLOSED狀態;服務端接收到ACK報文,進入CLOSED狀態。至此斷開連接。
????????注意1:這里必須是四次揮手,第二次和第三次不能合并!接收端接收到報文后發送ACK是內核行為,即立即就發送;而發送FIN是用戶代碼行為,只有顯式調用了close()方法才會發送FIN報文。通常服務端發送ACK后(第二次揮手),此時可能還有數據未處理完(緩沖區還有數據未讀取),因此服務端會隔一段時間待數據處理結束后再調用close()進行第三次揮手(在try-catch-finally語句的finally中調用close())。
????????注意2:為什么客戶端必須等2MSL再關閉連接?MSL是報文最大生存時間,即報文從一端到另一端的最長時間,超過這個時間就會被丟棄。1.當客戶端發送的ACK丟失時,此時服務端將進行超時重傳,2MSL保證客戶端一定會等到重傳的FIN并進行處理(想象極端情況,ACK在即將到達服務端丟了,再加上超時重傳的FIN的最大時間剛好是2MSL),客戶端等到重傳的FIN會重啟計時器,重新計時2MSL。2.2MSL保證了TCP連接中所有的報文都會消失,不會出現下次TCP連接中出現上一層的舊報文段,導致數據錯誤。
5 滑動窗口機制(保證效率)
????????采用一問一答的確認應答機制,雖然可靠性得到保證,但是這種方式效率很低,滑動窗口機制就是來提高效率的。
????????具體來講,窗口大小是指能一次性不等ACK就發送的數據量的最大值。比如采用窗口大小為4000,一次性就可以發送4個TCP報文段。
????????當收到窗口內seq=1的ACK(下次傳輸1001)時,窗口向后移動一個報文段長度,發送下一個報文段即4001-5000,以此類推。但是滑動窗口機制下,如果出現超時重傳,如何做?
????????情況1:ACK丟失,假設ack=1001丟失,那么后續發送端收到ack=2001,也就知道1-1000的報文段已經被接收端收到,因此后續ack也會確認數據的收到,故ACK丟失不影響可靠性。
????????情況2:TCP報文段丟失,假設seq=1的報文段丟失(1-1000的數據),窗口內的所有數據都會發送,當接收端收到seq=1001-4000的三個報文段時,把這些報文段放在接收端緩沖區,發送ack=1表示希望收到1-1000的數據。發送端連續收到3個重復確認號的報文段,就知道數據1-1000已經丟失了,觸發重傳(由于這種重傳不是計時器超時了,因此不是超時重傳,而是快重傳,表示快速重發丟失的報文段)。
????????注意1:快重傳時只重傳丟失的報文段,其他報文段已經被存儲在接收端的緩沖區中,故不需要重新發送。
????????注意2:當TCP報文段丟失時,如果窗口中的數據全部發送完,再未收到丟失報文段的ACK時,窗口處于凍結狀態,即使收到其他數據的ACK(只是確認號都是一樣),窗口也不會向后移動。
6 流量控制機制(保證可靠傳輸)
????????網絡傳輸的速度不止由發送端的發送速度決定,也取決于接收端的數據處理速度。如果發送端發送過快,接收端數據緩沖區很快就慢了,再來的數據就會被丟失,因此導致丟包(丟包后發送端超時重傳,引起一系列的連鎖反應)的不可靠現象。
????????因此,流量控制機制會根據接收端的數據處理能力來動態調整發送端的窗口大小。具體而言,在TCP協議格式中有窗口大小字段,只有ACK報文段才會生效,填寫接收端剩余空間的大小。
????????發送端收到ACK后根據窗口大小字段調整自己的窗口,如果窗口大小是0,此時接收端緩沖區已經滿了,因此發送端窗口大小為0不再發送數據,超過超時重傳的時間時,發送端發送窗口探測報文段(沒有數據載荷),接收端接收窗口探測報文段自動觸發ACK發送,一旦接收端緩沖區有空間了會主動發送ACK報文段(窗口更新通知),進而下一步發送數據。
7 擁塞控制機制(保證可靠傳輸)
????????除了發送端發送速率和接收端處理速率,網絡傳輸的可靠性還由傳輸過程的網絡節點轉發保證。當中間節點比較擁堵時,如果發送端發送大量數據就很容易導致丟包,因此需要衡量中間節點的網絡狀況就需要進行“實驗”:
????????實驗是指發送方窗口大小通過試探的方式得到合適的大小,比如剛開始以小窗口發送數據,此時能順利完成數據交互,就可以擴大窗口大小。當出現網絡擁塞(丟包)時,就減小窗口大小,從而實現根據網絡擁塞狀況動態調整窗口大小。
????????具體而言有如下策略:
????????1.慢開始:初始選擇小窗口,以指數速率增加窗口大小(慢開始的含義僅指窗口初始值很小,不代表窗口增加的速率)。
????????2.擁塞避免:為了不讓窗口值擴大速率很快,設置閾值ssthresh,一旦窗口超過ssthresh值,窗口大小增加速率變成線性增加(加法增加,依次增加1個單位)。
????????3.乘法減小:當出現網絡擁塞時,窗口大小恢復初始值,重新慢開始。同時ssthresh值減少為原窗口值的一半(出現網絡擁塞,說明短期內不宜發送過多的數據,因此控制窗口增加速率不要那么快)。
????????注意:這里的窗口和流量控制的窗口取哪個?擁塞控制機制的窗口稱為擁塞窗口cwnd,流量控制的窗口稱為流量窗口。發送方選擇的窗口大小=min(擁塞窗口,流量窗口)。擁塞窗口大于流量窗口,說明此時接收端數據處理壓力大,因此應該減少數據的發送,故選擇窗口為流量窗口;流量窗口大于擁塞窗口,說明此時網絡狀況較差,發送數據可能會產生丟包,因此選擇擁塞窗口。
8 延遲應答機制(保證效率)
????????接收端實際上可能處理緩沖區的速度比較快,因此如果接收端在收到報文段后立即發送ACK,此時窗口大小可能比較小。而延遲發送ACK,由于接收端處理了部分數據,此時窗口大小就可以增加。增加窗口大小意味著網絡傳輸的吞吐量增大,于是效率就得到提高(保證流量控制機制不會抑制傳輸速率太狠)。
????????對于延遲應答,不是所有的報文段都可以延遲發送ACK,否則可能導致發送端的超時重傳。如果超過最大時間限制就應該及時應答,如果連續收到N個報文段,也應該及時應答(此時應答意味著同時確認了多個報文段)。
9 捎帶應答機制(保證效率)
????????在延遲應答機制的基礎上,如果延遲的時間剛好接收端也有數據需要發送(實際上客戶端和服務器分別發送請求和響應都包含數據載荷),那么發送的包含數據的普通報文段也可以捎帶ACK一起發送。
????????注意:在這個基礎上,四報文揮手斷開連接在特殊情況下,四報文揮手可能變成三報文,如果第二次揮手(服務端發送ACK)延遲應答前,而第三次揮手(服務器端發送FIN)立即發送,此時第二次和第三次揮手就會合并為FIN+ACK,但是我們仍然成為四報文揮手。
10 粘包問題
????????粘包問題粘的是應用層的數據包,由于TCP協議只有首部長度,沒有數據長度字段,而TCP協議面向字節流,接收端緩沖區中如果存在多個報文段,在應用層看來是一大串字節,不知道報文段的起始和結束,看起來好像所有的數據粘在一起了,這就是粘包問題。
????????解決方案就是明確數據的邊界:1.約定數據長度2.使用分隔符。在應用層使用TCP的Socket編碼時,使用println()就是使用分隔符傳輸數據的方式,否則就會導致next()讀取數據區分不了邊界從而阻塞等待。
11 TCP連接異常情況
????????1.主機關機:殺死進程,釋放進程PCB,釋放文件描述符(相當于調用close()),因此會正常觸發FIN報文段的發送,即四次揮手流程。當四次揮手還未執行結束就關機了,此時服務端會超時重傳若干次FIN,都沒有反應就自動關閉連接。
????????2.程序崩潰:這種情況也是進程終止,即同主機關機(連接管理由內核負責,即使沒有進程也可以進行)。
????????3.機器掉電:如果接收方掉電,發送方嘗試發送數據,發現沒有ACK,連續多次超時重傳也沒有響應,就嘗試重新建立連接,重新建立連接不成功就會主動放棄。如果發送方掉點,接收方長時間沒有接收到數據,就會定期給發送方發送“心跳包”(發送方發送ping報文,接收方回復pong報文,具有周期性和檢測對方是否存活的作用)。
????????4.網線斷開:同機器掉電。
12 常見問題
????????1.UDP本身是無連接,不可靠,面向數據報的協議,如果要基于傳輸層UDP協議,來實現一個可靠傳輸,應該如何設計?
????????2. UDP大小是受限的,如果要基于傳輸層UDP協議,傳輸超過64K的數據,應該如何設計?
????????問題1和問題2答案類似,都是采用TCP的思路來解決UDP的問題,比如設計類似TCP可靠傳輸機制的UDP協議,引入序列號、確認應答、超時重傳等等。
????????3.什么時候使用TCP,什么時候使用UDP?
????????TCP用于可靠傳輸的情況,應用于文件傳輸,重要狀態更新等場景。
????????UDP用于對高速傳輸和實時性要求較高的通信領域,例如,早期的QQ,視頻傳輸等。另外UDP可以用于廣播。
下篇文章:
網絡編程—TCP/IP模型(IP協議)https://blog.csdn.net/sniper_fandc/article/details/147025311?fromshare=blogdetail&sharetype=blogdetail&sharerId=147025311&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link