網(wǎng)絡(luò)協(xié)議補(bǔ)完計(jì)劃--TCP協(xié)議

目錄

  • TCP協(xié)議的基本概念
    • 面向鏈接的服務(wù)
    • 可靠的服務(wù)
    • 序列號(hào)
    • 字節(jié)流傳輸
  • TCP協(xié)議數(shù)據(jù)段的格式
    • TCP偽頭部
  • TCP協(xié)議鏈接的建立和關(guān)閉
    • TCP連接的建立
      • 三次握手
      • 序列號(hào)和確認(rèn)號(hào)
      • 建立連接時(shí)的初始數(shù)據(jù)
    • TCP連接的關(guān)閉
      • 四次揮手
      • 復(fù)位
    • TCP連接的狀態(tài)遷移
      • 三次握手的狀態(tài)遷移
      • 同時(shí)打開的狀態(tài)遷移
      • 四次揮手的狀態(tài)遷移
  • TCP協(xié)議數(shù)據(jù)的傳送與流量控制
    • 字節(jié)流的分段
      • TCP緩沖區(qū)
      • TCP數(shù)據(jù)段的長(zhǎng)度
      • TCP選項(xiàng)(字段)
    • 滑動(dòng)窗口機(jī)制
      • 基本滑動(dòng)窗口機(jī)制
      • TCP數(shù)據(jù)段的標(biāo)識(shí)符-序列號(hào)
      • TCP的滑動(dòng)窗口機(jī)制
      • 可變窗口大小
    • TCP的擁塞機(jī)制
      • 擁塞窗口
      • 成倍減少機(jī)制
      • 慢啟動(dòng)機(jī)制
      • 擁塞避免機(jī)制
    • 緊急數(shù)據(jù)的傳輸
      • 普通緊急數(shù)據(jù)
      • 帶外數(shù)據(jù)
  • TCP的傻窗口癥狀
    • 如何解決傻窗口的問題
  • TCP協(xié)議與UDP協(xié)議的比較
    • 面向連接的傳輸服務(wù)
    • 可靠的傳輸服務(wù)
    • 面向字節(jié)流的傳輸服務(wù)
  • TCP應(yīng)用于UDP應(yīng)用
    • TCP應(yīng)用
      • 大文件傳輸--FTP
      • 遠(yuǎn)程登錄--Telnet
      • 可靠傳輸--http
    • UDP應(yīng)用
      • 實(shí)時(shí)應(yīng)用
      • 多播式應(yīng)用
      • 某些時(shí)候的降級(jí)策略
    • 使用UDP時(shí)應(yīng)該注意
  • QQ關(guān)于TCP以及UDP的一些帖子整理
    • QQ既有UDP也有TCP
    • 早期客戶端與客戶端主要使用UDP直接通訊
    • 有了代理服務(wù)器之后的當(dāng)下
    • 為什么最初選擇UDP
    • QQ的TCP長(zhǎng)連接到底在做什么、為什么不用已經(jīng)建立好的TCP卻還是用UDP發(fā)送消息?
    • 大文件傳輸/視頻下的TCP

前言

參照清華大學(xué)出版社-羅軍周主編的《TCP/IP協(xié)議及網(wǎng)絡(luò)編程技術(shù)》進(jìn)行學(xué)習(xí)。
本篇主要參考第九章:《TCP協(xié)議》


TCP協(xié)議的基本概念

TCP協(xié)議為應(yīng)用軟件提供一種面向鏈接可靠字節(jié)流傳輸服務(wù)的協(xié)議。

引用《網(wǎng)絡(luò)協(xié)議補(bǔ)完計(jì)劃--TCP/IP協(xié)議概述》中的解釋:

亞當(dāng)和夏娃分別生活在兩個(gè)山頭,山頭之間是萬(wàn)丈深淵,亞當(dāng)采集野果需要分享給夏娃,如果他們之間有一條索道(物理連接),野果可以順著索道滑到夏娃那一邊,那就沒有網(wǎng)絡(luò)協(xié)議什么事了。
事實(shí)上山頭之間沒有索道。但是亞當(dāng)何等聰明,于是他想出了一個(gè)方法,假設(shè)亞當(dāng)需要給夏娃10個(gè)野果,否則她會(huì)餓死。

TCP協(xié)議能夠確保從發(fā)送方發(fā)出的數(shù)據(jù)、按照順序完整的交付給接收方。

連接建立
亞當(dāng)對(duì)著夏娃大喊:愛妃,你聽得到嗎?
夏娃回應(yīng):孩他爹,我聽得到!
亞當(dāng)接著喊:那好,我扔果子給你吃,你接到果子就喊一聲,一共十個(gè)。

運(yùn)送貨物
于是亞當(dāng)開始扔第一個(gè),夏娃喊收到了一個(gè)。
亞當(dāng)扔第二個(gè),夏娃喊收到兩個(gè)。
超時(shí)重傳 ( timeout retransmit)
亞當(dāng)扔第三個(gè),可是夏娃遲遲沒有回音,亞當(dāng)意識(shí)到可能果子落到懸崖了,于是重新扔,夏娃喊收到第三個(gè)。
Advertised window size = 0
于是亞當(dāng)連續(xù)扔了第四、五、六個(gè),夏娃急了:孩他爹,慢點(diǎn)扔,臣妾忙不過(guò)來(lái)了…
Advertised window size > 0
于是亞當(dāng)坐下休息,愛妃又開始叫了:繼續(xù)扔吧。
亞當(dāng)開始扔第七個(gè),夏娃喊收到七個(gè)。

關(guān)閉連接
終于亞當(dāng)扔完了,亞當(dāng)喊:愛妃,果子扔完了,寡人去忙別的了。
夏娃回復(fù):好的,我也休息一下,再見。
亞當(dāng):再見
以上的過(guò)程類似TCP連接的過(guò)程,TCP是一個(gè)虛擬連接

——————

  • 面向鏈接的服務(wù)

1. 連接狀態(tài)與各個(gè)端(主機(jī))有關(guān)、與其間的路由器無(wú)關(guān)。
2. 鏈接是點(diǎn)對(duì)點(diǎn)雙向的、雙方都可以發(fā)送或接收。
3. 端口只負(fù)責(zé)接收數(shù)據(jù)、鏈接標(biāo)識(shí)符決定最終流向。
與UDP協(xié)議不同、TCP協(xié)議的端口只負(fù)責(zé)數(shù)據(jù)的接收。
最終交給哪個(gè)應(yīng)用程序處理、取決于鏈接的標(biāo)識(shí)符。
4. TCP協(xié)議允許多個(gè)TCP連接共用一個(gè)端口。
由于上一個(gè)原因、即使使用同一個(gè)端口。我們依舊可以把數(shù)據(jù)送達(dá)正確的接受者受眾
5. 無(wú)法使用廣播或多播
由于TCP鏈接是針對(duì)兩端進(jìn)行的、所以只能二者之間進(jìn)行通信。
——————

  • 可靠的服務(wù)

我們一直強(qiáng)調(diào)IP協(xié)議提供的是不可靠的傳輸服務(wù)、既IP數(shù)據(jù)包可能會(huì)丟失、失序、重復(fù)等等。
TCP協(xié)議作為上層服務(wù)是如何通過(guò)一個(gè)不可靠的傳輸可靠的呢?

通過(guò)超時(shí)從發(fā)機(jī)制來(lái)實(shí)現(xiàn)
  1. 接收方在收到發(fā)送方的數(shù)據(jù)后需要給發(fā)送方回復(fù)一個(gè)確認(rèn)
  2. 發(fā)送方在發(fā)出數(shù)據(jù)后會(huì)等待接收方的確認(rèn)
  3. 在收到確認(rèn)之前不會(huì)發(fā)送下一條數(shù)據(jù)。
  4. 如果等待一定時(shí)間仍沒有收到確認(rèn)就將剛剛的數(shù)據(jù)重發(fā)
  5. 知道收到確認(rèn)或者重發(fā)次數(shù)用完為止。
確認(rèn)--重發(fā)示意圖

——————

序列號(hào)

很多時(shí)候并不是數(shù)據(jù)包丟失、而是由于一些原因?qū)е戮W(wǎng)絡(luò)延時(shí)、這時(shí)候重發(fā)數(shù)據(jù)會(huì)導(dǎo)致接收方收到兩次數(shù)據(jù)、發(fā)送方也會(huì)收到兩次確認(rèn)。
那么如何確認(rèn)發(fā)送的數(shù)據(jù)以及確認(rèn)是同一個(gè)呢?
就要在數(shù)據(jù)包中記錄一個(gè)序列號(hào)的字段。
相同的數(shù)據(jù)會(huì)使用同一個(gè)序列號(hào)、確認(rèn)信息也會(huì)注明是對(duì)哪一個(gè)序列號(hào)的確認(rèn)。上圖中的1、2、3就是不同的序列號(hào)

——————

字節(jié)流傳輸

TCP協(xié)議向上層應(yīng)用程序提供的傳輸服務(wù)是面向字節(jié)流的。
TCP協(xié)議將數(shù)據(jù)看做有序排列的二進(jìn)制位、并按照8位分割成有序的字節(jié)流。

  • 發(fā)送方應(yīng)用層將數(shù)據(jù)以字節(jié)流的順序遞交給下層TCP協(xié)議進(jìn)行傳輸
  • 接收方的TCP協(xié)議會(huì)將數(shù)據(jù)以相同的字節(jié)流順序交付給接收方應(yīng)用程序。
  • TCP雖然將數(shù)據(jù)包一次一次的進(jìn)行傳輸、但并不關(guān)系數(shù)據(jù)的具體結(jié)構(gòu)。
  • 接收方可能在一次收取動(dòng)作中無(wú)法獲得全部數(shù)據(jù)、需要將數(shù)據(jù)包拼接還原。

TCP協(xié)議數(shù)據(jù)段的格式

應(yīng)用層不會(huì)在意字節(jié)流是怎樣進(jìn)行傳輸?shù)摹⒌獻(xiàn)P協(xié)議的傳輸是受限于IP數(shù)據(jù)包大小的(很可能比用戶數(shù)據(jù)總量大但是比TCP數(shù)據(jù)包小)、因此TCP協(xié)議必須將字節(jié)流數(shù)據(jù)進(jìn)行分割并組成IP數(shù)據(jù)包進(jìn)行傳輸、在目標(biāo)主機(jī)的TCP協(xié)議將會(huì)將這些數(shù)據(jù)再組合成數(shù)據(jù)流。
TCP協(xié)議數(shù)據(jù)包有自己的頭部和數(shù)據(jù)區(qū)、一個(gè)TCP數(shù)據(jù)包稱為段。

TCP協(xié)議數(shù)據(jù)段

——————

TCP偽頭部

作用和UDP協(xié)議的偽頭部相同、用于計(jì)算校驗(yàn)和。


TCP協(xié)議鏈接的建立和關(guān)閉

TCP連接的建立

一個(gè)TCP連接由四部分組成。發(fā)送方的IP地址與端口號(hào)、接收方的IP地址與端口號(hào)。
建立一個(gè)鏈接是為了讓發(fā)送方和接收方都做好準(zhǔn)備開始傳輸數(shù)據(jù)。

——————

主動(dòng)打開與被動(dòng)打開

  • 主動(dòng)方
    向系統(tǒng)申請(qǐng)一個(gè)TCP端口、并發(fā)出第一個(gè)消息(連接請(qǐng)求)的一方

  • 被動(dòng)方
    已經(jīng)向系統(tǒng)申請(qǐng)好了一個(gè)TCP端口(監(jiān)聽端口)等待其他主機(jī)連接請(qǐng)求的一方

  • 三次握手


  1. 三次握手的主要目的在于同步連接雙方發(fā)送數(shù)據(jù)的初始序列號(hào)。
    連接中每次數(shù)據(jù)的發(fā)送、都需要附帶序列號(hào)。作用已經(jīng)在前文中提過(guò)
  2. 連接是雙向的
    數(shù)據(jù)的發(fā)送/回復(fù)是雙向的。每個(gè)方向都有自己維護(hù)的序列號(hào)

還需要注意的是三次握手中數(shù)據(jù)包的代碼位

  • SYN - 創(chuàng)建一個(gè)連接
  • ACK - 同意一個(gè)操作

我們來(lái)解釋一下上圖中每步的含義:

  1. 第一次握手: (主動(dòng)方 ----->> 被動(dòng)方)鏈接待建立

主動(dòng)方向被動(dòng)方申請(qǐng)建立連接(SYN)--以發(fā)送數(shù)據(jù)給被動(dòng)方

序列號(hào)seq字段設(shè)置為主動(dòng)方初始序列號(hào) + 代碼位設(shè)置為syn

  1. 第二次握手: (主動(dòng)方 ----->> 被動(dòng)方)鏈接已建立、 (被動(dòng)方 ----->> 主動(dòng)方)鏈接待建立

被動(dòng)方同意建立連接(ACK)--以接收主動(dòng)方數(shù)據(jù)
被動(dòng)方申請(qǐng)建立連接(SYN)--以發(fā)送數(shù)據(jù)給主動(dòng)方

序列號(hào)seq字段設(shè)置為被動(dòng)方初始序列號(hào)
ack設(shè)置成主動(dòng)方初始序列號(hào)+1
代碼位設(shè)置為syn、ack

  1. 第二次握手: (主動(dòng)方 ----->> 被動(dòng)方)鏈接已建立、 (被動(dòng)方 ----->> 主動(dòng)方)鏈接已建立

主動(dòng)方同意建立連接(ACK)--以接收被動(dòng)方數(shù)據(jù)

ack設(shè)置成被動(dòng)方初始序列號(hào)+1

  • 兩個(gè)主動(dòng)打開連接建立的過(guò)程

這種情況發(fā)生在兩端幾乎同時(shí)發(fā)送SYN并且這兩個(gè)SYN在網(wǎng)絡(luò)中交錯(cuò)的情形。這種情況可能發(fā)生,但是非常罕見。

需要注意的是、上圖中紅色部分以及綠色部分(發(fā)送SYN時(shí))、初始序列號(hào)字段必須是相同的、否則接收方就不知道它的初始序列號(hào)是多少了。

  • 序列號(hào)和確認(rèn)號(hào)

TCP會(huì)話的每一端都包含一個(gè)32位(bit)的序列號(hào),該序列號(hào)被用來(lái)跟蹤該端發(fā)送的數(shù)據(jù)量。每一個(gè)包中都包含序列號(hào),在接收端則通過(guò)確認(rèn)號(hào)用來(lái)通知發(fā)送端數(shù)據(jù)成功接收

當(dāng)某個(gè)主機(jī)開啟一個(gè)TCP會(huì)話時(shí),他的初始序列號(hào)是隨機(jī)的,可能是0和4,294,967,295之間的任意值,然而,像Wireshark這種工具,通常顯示的都是相對(duì)序列號(hào)/確認(rèn)號(hào),而不是實(shí)際序列號(hào)/確認(rèn)號(hào),相對(duì)序列號(hào)/確認(rèn)號(hào)是和TCP會(huì)話的初始序列號(hào)相關(guān)聯(lián)的。這是很方便的,因?yàn)楸绕鹫鎸?shí)序列號(hào)/確認(rèn)號(hào),跟蹤更小的相對(duì)序列號(hào)/確認(rèn)號(hào)會(huì)相對(duì)容易一些

關(guān)于為什么需要一個(gè)隨機(jī)的初始序列號(hào)
一部分原因是為了防止重復(fù)
更重要的原因是在安全上為了防止IP欺騙的攻擊
如果確認(rèn)號(hào)固定、那么一旦IP被偽裝、即便攻擊者接收不到響應(yīng)包、但還是可以隨意建立連接并發(fā)送指令(比如注入一些后門指令)。

  • 建立連接時(shí)的初始數(shù)據(jù)

一般建立連接的TCP段中是不帶數(shù)據(jù)的、但不意味著不能攜帶數(shù)據(jù)。
有時(shí)會(huì)我們會(huì)在三次握手中將數(shù)據(jù)區(qū)加入數(shù)據(jù)、接收方將其保存下來(lái)、在建立連接之后就可以迅速遞交給上層應(yīng)用了。

——————

TCP連接的關(guān)閉

  • 四次揮手
  1. TCP連接是雙向的
    可以看做兩個(gè)管道、同一時(shí)刻每一個(gè)管道都可以發(fā)送數(shù)據(jù)
  2. 半關(guān)閉
    只關(guān)閉一個(gè)方向的數(shù)據(jù)傳輸、另一個(gè)方向還是可以繼續(xù)的

同樣需要注意的是三次握手中數(shù)據(jù)包的代碼位:

  • FIN - 終結(jié)一個(gè)連接
  • ACK - 同意一個(gè)操作
  1. 主動(dòng)方申請(qǐng)關(guān)閉
    連接的主動(dòng)方先發(fā)送FIN數(shù)據(jù)段、告訴被動(dòng)方自己的數(shù)據(jù)發(fā)送完了。
  2. 被動(dòng)方確認(rèn)
    連接的被動(dòng)方很可能還沒發(fā)送完數(shù)據(jù)、所以只能用ACK段告訴主動(dòng)方自己知道也同意主動(dòng)方關(guān)閉管道不在發(fā)送數(shù)據(jù)過(guò)來(lái)。
  3. 被動(dòng)方申請(qǐng)關(guān)閉
    連接的被動(dòng)方在確認(rèn)自己的數(shù)據(jù)也發(fā)送完了之后、發(fā)送FIN段給主動(dòng)方關(guān)閉連接。
  4. 主動(dòng)方確認(rèn)
    連接的主動(dòng)方通過(guò)ACK告訴被動(dòng)方、同意被動(dòng)方也關(guān)閉管道。

需要注意的是在四次揮手的過(guò)程中、不需要發(fā)送序列號(hào)了(至少我沒找到??)。

  • 復(fù)位
    涉及到另一個(gè)代碼位:RST - 復(fù)位
    直接將連接關(guān)閉、不需要四次揮手。直接關(guān)閉雙向連接

——————

TCP連接的狀態(tài)遷移

  • 三次握手的狀態(tài)遷移


  • 同時(shí)打開的狀態(tài)遷移


  • 四次揮手的狀態(tài)遷移


  • 計(jì)時(shí)等待狀態(tài)(2MSL)

每個(gè)TCP具體實(shí)現(xiàn)、必須選擇一個(gè)TCP段最大生存時(shí)間MSL
也就是每一個(gè)TCP段被丟棄前在網(wǎng)絡(luò)內(nèi)的最長(zhǎng)時(shí)間。

時(shí)長(zhǎng)通常為2min

當(dāng)一個(gè)TCP連接執(zhí)行主動(dòng)關(guān)閉、并發(fā)回最后一個(gè)ACK(確認(rèn))、該連接必須在計(jì)時(shí)等待狀態(tài)停留2倍的MSL。
這樣可以讓TCP有一次重發(fā)這個(gè)ACK的機(jī)會(huì)。

這種設(shè)計(jì)的另一個(gè)結(jié)果就是這個(gè)TCP連接在2MSL的等待時(shí)間內(nèi)、定義這個(gè)鏈接的端口不能再被使用。


TCP協(xié)議數(shù)據(jù)的傳送與流量控制

主要涉及字節(jié)流的分段、TCP的確認(rèn)重發(fā)機(jī)制、超時(shí)的判斷及緊急數(shù)據(jù)的傳輸?shù)?/p>

字節(jié)流的分段

由于TCP上應(yīng)用層提供的是字節(jié)流的傳輸服務(wù)。為了避免用戶提供少量多次的數(shù)據(jù)導(dǎo)致傳輸效率低下、TCP協(xié)議采取的是緩沖的辦法。

  • TCP緩沖區(qū)

應(yīng)用層提供給TCP協(xié)議的數(shù)據(jù)會(huì)被先放入緩沖區(qū)中、并沒有真正的發(fā)送。只有在合適的時(shí)候或者應(yīng)用程序顯示地要求將數(shù)據(jù)發(fā)送時(shí)、TCP才會(huì)將數(shù)據(jù)組織成合適的數(shù)據(jù)段發(fā)送出去。
至于TCP協(xié)議的緩沖區(qū)有多大、一般要根據(jù)TCP協(xié)議確定的最大段長(zhǎng)度決定。

  • TCP數(shù)據(jù)段的長(zhǎng)度

TCP數(shù)據(jù)段最終將會(huì)封裝IP數(shù)據(jù)包中進(jìn)行發(fā)送。
從理論上講、TCP數(shù)據(jù)段的最大長(zhǎng)度加上IP數(shù)據(jù)包頭部長(zhǎng)度不能超過(guò)65535個(gè)8位字。
但實(shí)際上、TCP數(shù)據(jù)段的長(zhǎng)度要遠(yuǎn)遠(yuǎn)小于這個(gè)值。主要有一下幾個(gè)原因:

  1. IP數(shù)據(jù)包的分片
    對(duì)于不同的物理鏈路限制、IP數(shù)據(jù)包可能會(huì)分片、這在一定程度上會(huì)影響傳輸效率。
  2. IP數(shù)據(jù)分片的丟失與重發(fā)
    而分片就意味著重組、如果缺少一個(gè)分片、那么整個(gè)IP數(shù)據(jù)包都將面臨重發(fā)。
  3. 傳輸兩端主機(jī)系統(tǒng)的計(jì)算能力差別
    大型服務(wù)器的內(nèi)存數(shù)量和普通主機(jī)存在十分大的差異、必須遷就著弱勢(shì)一方。以免弱勢(shì)一方被一個(gè)超大的數(shù)據(jù)包淹沒。

基于以上兩個(gè)原因、TCP數(shù)據(jù)包的大小應(yīng)該被控制在合理的范圍、使其最終能夠封裝在IP數(shù)據(jù)包中通過(guò)一個(gè)數(shù)據(jù)幀進(jìn)行傳輸。

  • TCP選項(xiàng)(字段)

建立連接時(shí)TCP協(xié)議數(shù)據(jù)段中、有一個(gè)TCP選項(xiàng)的字段。

TCP選項(xiàng)格式

其中:

  1. 類型字段: 長(zhǎng)度為8位。 2標(biāo)識(shí)最大段長(zhǎng)
  2. 長(zhǎng)度字段: 長(zhǎng)度為8位。
  3. 選項(xiàng)數(shù)據(jù): 長(zhǎng)度為16位。
  • MSS與MTU

MSS(最大報(bào)文段長(zhǎng)度)我們可以通過(guò)TCP報(bào)文的頭部TCP選項(xiàng)字段得到。
MTU(最大傳輸單元)針對(duì)不同的物理鏈路會(huì)有不同的約束、而且這種下層的約束是上層協(xié)議無(wú)法獲取的。

——————

滑動(dòng)窗口機(jī)制

單純的用每個(gè)數(shù)據(jù)段發(fā)送確認(rèn)最后重傳的機(jī)制、傳輸時(shí)間會(huì)成倍增長(zhǎng)、很不效率。

實(shí)際上TCP協(xié)議使用的確認(rèn)重發(fā)機(jī)制是一種稱為滑動(dòng)窗口的機(jī)制。
他可以讓發(fā)送方連續(xù)發(fā)送多個(gè)數(shù)據(jù)段、然后再等待接收方確認(rèn)

  • 基本滑動(dòng)窗口機(jī)制

TCP協(xié)議發(fā)送的字節(jié)流根據(jù)狀態(tài)可以分為三類:

  1. 已發(fā)送、已確認(rèn)(發(fā)送成功)
    數(shù)據(jù)確定已經(jīng)被接收方獲取

TCP協(xié)議可以完全不用關(guān)心、也不保存

  1. 已發(fā)送、待確認(rèn)(發(fā)送中)
    數(shù)據(jù)可能還在傳輸中/數(shù)據(jù)已經(jīng)被接收但確認(rèn)信息在傳輸中

為數(shù)據(jù)段啟動(dòng)一個(gè)定時(shí)器
定時(shí)器超時(shí)沒有收到確認(rèn)時(shí)、對(duì)該數(shù)據(jù)段執(zhí)行重發(fā)動(dòng)作。

  1. 未發(fā)送
    數(shù)據(jù)還沒有被發(fā)送

可能應(yīng)用程序還沒有提交給TCP協(xié)議。
也可能以字節(jié)流的形式保存在TCP的緩沖區(qū)中

滑動(dòng)窗口機(jī)制-1

如上圖所示、在數(shù)據(jù)不斷發(fā)送的過(guò)程中。藍(lán)色的窗口會(huì)不斷的向右移動(dòng)。知道所有數(shù)據(jù)全部變成綠色(已發(fā)送)。

在滑動(dòng)窗口機(jī)制中、窗口的大小(允許同時(shí)發(fā)送的數(shù)據(jù)量)決定了數(shù)據(jù)傳輸?shù)?code>效率。在極端情況下可能為1。

滑動(dòng)窗口的移動(dòng)
  1. 滑動(dòng)窗口中的數(shù)據(jù)并不是同時(shí)發(fā)送、而是有先后(數(shù)據(jù)的準(zhǔn)備時(shí)間)的。
  2. 當(dāng)收到一個(gè)第一個(gè)確認(rèn)后、將窗口向右移動(dòng)一個(gè)。發(fā)送第五個(gè)數(shù)據(jù)段。

如果窗口大小控制得當(dāng)(第四個(gè)數(shù)據(jù)段發(fā)出剛好收到第一個(gè)確認(rèn))、可以極大程度的利用TCP這種全雙工連接的的特點(diǎn)以同時(shí)進(jìn)行雙向傳輸。

  • TCP數(shù)據(jù)段的標(biāo)識(shí)符-序列號(hào)

防止數(shù)據(jù)包失序、以及在重發(fā)機(jī)制下用來(lái)區(qū)分接收到的數(shù)據(jù)是否是同一個(gè)的特殊號(hào)碼

  1. 序列號(hào)不是數(shù)據(jù)段的編號(hào)
    是按字節(jié)進(jìn)行編號(hào)的。比如一個(gè)數(shù)據(jù)段的序列號(hào)是216且該數(shù)據(jù)段攜帶了100B數(shù)據(jù)。那么下一個(gè)數(shù)據(jù)段的序列號(hào)就是216+100=316。

  2. 確認(rèn)號(hào)字段也不是確認(rèn)段的編號(hào)
    確認(rèn)號(hào)也是按字節(jié)進(jìn)行編號(hào)的。比如一個(gè)他接收到的數(shù)據(jù)段序列號(hào)是216且攜帶了100B數(shù)據(jù)。那么他發(fā)送的確認(rèn)信息中確認(rèn)號(hào)則應(yīng)該為216+100=316。表示他希望接收序列號(hào)為316的數(shù)據(jù)段。
    需要注意的是確認(rèn)號(hào)并不是在每個(gè)數(shù)據(jù)段中都用到、只有在數(shù)據(jù)段中設(shè)置了ACK位、才會(huì)識(shí)別確認(rèn)號(hào)。

  3. 三次握手時(shí)的序列號(hào)以及確認(rèn)號(hào)

    三次握手中序列號(hào)的變化

先介紹其中字段的含義
seq:序列號(hào)
aseq:確認(rèn)號(hào)
dlen:數(shù)據(jù)區(qū)長(zhǎng)度。建立連接時(shí)通常不攜帶數(shù)據(jù)、但是不代表不能攜帶。

上圖更加直觀的印證了

  1. 確認(rèn)號(hào)=序列號(hào)+數(shù)據(jù)區(qū)長(zhǎng)度
    但是在連接的建立階段需要額外+1、不然由于數(shù)據(jù)區(qū)為空。序列號(hào)永遠(yuǎn)是一個(gè)了。
  2. 雙方各自維護(hù)各自的序列號(hào)
    主動(dòng)方序列號(hào)x與被動(dòng)方序列號(hào)y。沒有絲毫的聯(lián)系
  • TCP的滑動(dòng)窗口機(jī)制

之前的例子是以數(shù)據(jù)段為單位進(jìn)行解釋、但實(shí)際工程中的滑動(dòng)窗口、是以字節(jié)作為基本單位工作的。

發(fā)送方窗口
在TCP協(xié)議中、發(fā)送方每個(gè)窗口都用三個(gè)指針表示

發(fā)送方滑動(dòng)窗口指針

  1. 第一個(gè)指針
    指向窗口范圍的第一個(gè)字節(jié)
  2. 第二個(gè)指針
    指向馬上要發(fā)送的字節(jié)
  3. 第三個(gè)字節(jié)
    指向窗口中的最后一個(gè)字節(jié)

接收方窗口
在TCP協(xié)議中、接收方窗口用三個(gè)指針并不能完全表示

接收方滑動(dòng)窗口

  1. 實(shí)線部分(201到1000
    當(dāng)前窗口
  2. 白色部分(201到500以及733到800
    等待接收數(shù)據(jù)的部分
  3. 深色部分(501到732以及801到1000
    已經(jīng)接收到信息的部分

需要注意的是確認(rèn)信息中的確認(rèn)號(hào)、代表對(duì)于接收方已經(jīng)將確認(rèn)號(hào)前的數(shù)據(jù)全部接收完畢。

那么、當(dāng)接收方只接收到501到732的數(shù)據(jù)段卻沒有接收到201到500的數(shù)據(jù)段時(shí)、接收方會(huì)將這段數(shù)據(jù)暫存起來(lái)。
之后、如果接收到一個(gè)序列號(hào)為201、數(shù)據(jù)長(zhǎng)度為300的數(shù)據(jù)段。
將會(huì)發(fā)送確認(rèn)號(hào)為733的數(shù)據(jù)段。
那么接收方的窗口將移動(dòng)到733(當(dāng)然發(fā)送方也是)。

由于TCP鏈接是全雙工的、所以發(fā)送方以及接收方每一端都必須保存兩個(gè)窗口。一個(gè)負(fù)責(zé)發(fā)送、一個(gè)負(fù)責(zé)接收。
這種確認(rèn)方式我們稱之為《累計(jì)確認(rèn)》

關(guān)于累計(jì)確認(rèn)的優(yōu)點(diǎn):

1. 他允許發(fā)送方在重發(fā)時(shí)發(fā)送比之前正常發(fā)送時(shí)還要多一些的數(shù)據(jù)
比如最初發(fā)送了500B的數(shù)據(jù)、在確認(rèn)回來(lái)之前客戶端又交付了300B。這時(shí)他就可以將800B的數(shù)據(jù)一次發(fā)送出去。變相的增加了之前500B到達(dá)的幾率。
2. 確認(rèn)累計(jì)無(wú)需對(duì)丟失的確認(rèn)進(jìn)行重發(fā)
由于接收方發(fā)送的確認(rèn)號(hào)會(huì)直接更改發(fā)送方的窗口位置(參考剛才對(duì)于接收方窗口的解釋)、所以哪怕是確認(rèn)丟失也無(wú)需重發(fā)、只要下一個(gè)確認(rèn)能夠正確到達(dá)就可以。

關(guān)于累計(jì)確認(rèn)的缺點(diǎn):

** 一個(gè)極端的情況**


累計(jì)確認(rèn)的極端情況

如圖所示、后面的三個(gè)數(shù)據(jù)段全部送達(dá)、但是由于第一個(gè)數(shù)據(jù)段丟失。接收方無(wú)法回復(fù)確認(rèn)號(hào)、導(dǎo)致發(fā)送方的窗口第一個(gè)序列號(hào)依舊為201。

這時(shí)發(fā)送方不知道該發(fā)送第一個(gè)數(shù)據(jù)段還是所有數(shù)據(jù)段。如果每次都重發(fā)所有數(shù)據(jù)段、可能會(huì)對(duì)網(wǎng)絡(luò)造成很大的負(fù)擔(dān)。

針對(duì)這個(gè)情況、目前的標(biāo)準(zhǔn)做法是:

  1. 重發(fā)第一個(gè)數(shù)據(jù)段、通過(guò)確認(rèn)號(hào)來(lái)判斷剛才的幾個(gè)數(shù)據(jù)段成功了幾個(gè)。
  2. 如果幾次發(fā)送之后確定接收方剛才的四個(gè)數(shù)據(jù)段一個(gè)都沒有收到、那么則重回單個(gè)數(shù)據(jù)包的簡(jiǎn)單發(fā)送模式。
  • 可變窗口大小

由于緩沖區(qū)的存在、維護(hù)接收窗口是需要資源的。而一些性能較差的主機(jī)系統(tǒng)而言維護(hù)并不能同時(shí)處理那么多的數(shù)據(jù)。
所以、我們要求發(fā)送方的窗口不能大于接收方的窗口(通常要小一些)。

接收方窗口的大小、則在發(fā)送每一個(gè)ACK設(shè)置為1確認(rèn)包時(shí)、通過(guò)窗口字段來(lái)告知發(fā)送方。這樣做、也就允許接收方隨時(shí)改變窗口的大小。

需要注意的是。在縮小窗口時(shí)、新窗口的右邊界不能小于原窗口的右邊界、不過(guò)擴(kuò)大是沒有問題的。
對(duì)于縮小窗口、極限值為0。也就以為這停止傳輸。

借用書上的例子

TCP接受窗口大小變化示意圖
  • 上面的例子中前三行窗口越來(lái)越小
    上層應(yīng)用可能由于很多原因(比如數(shù)據(jù)發(fā)送過(guò)快、接收方來(lái)不及處理需要人工干預(yù)、正在等待處理等等)無(wú)法接收數(shù)據(jù)、導(dǎo)致TCP緩沖區(qū)的數(shù)據(jù)無(wú)法提交給應(yīng)用層
    這無(wú)形中就減小了緩沖區(qū)的大小、使得窗口的右邊界無(wú)法向右移動(dòng)

  • 發(fā)送方窗口也將相應(yīng)變小
    接收方將變小的接受窗口通過(guò)ACK數(shù)據(jù)段發(fā)送給發(fā)送方、發(fā)送方也會(huì)相應(yīng)的減小(如果需要)發(fā)送窗口的大小以配合接收方。

  • 窗口復(fù)原
    最后一行、上層應(yīng)用程序處理完數(shù)據(jù)。TCP可以將緩沖區(qū)中積壓的數(shù)據(jù)傳遞上去、窗口也就可以恢復(fù)原樣了。

綜上所述

TCP協(xié)議使用的是。以《《累計(jì)確認(rèn)》》為基礎(chǔ)、以《《字節(jié)為單位》》《《滑動(dòng)窗口機(jī)制》》。實(shí)現(xiàn)了數(shù)據(jù)的可靠、高效傳輸。還實(shí)現(xiàn)了流量控制(處理?yè)砣?/p>

————————

TCP的擁塞機(jī)制

所謂的擁塞就是只網(wǎng)絡(luò)中的轉(zhuǎn)發(fā)設(shè)備(路由器)因?yàn)檫^(guò)多的數(shù)據(jù)包需要轉(zhuǎn)發(fā)而造成某些數(shù)據(jù)包的延遲或者丟失。
一旦路由器的存儲(chǔ)資源被占滿、后到達(dá)的數(shù)據(jù)包就將被丟棄。
路由器丟棄數(shù)據(jù)包時(shí)、會(huì)向源主機(jī)發(fā)送類型為源端關(guān)閉的ICMP數(shù)據(jù)包(關(guān)于ICMP可以參考《網(wǎng)絡(luò)協(xié)議補(bǔ)完計(jì)劃--ICMP協(xié)議》)。
但需要注意的是ICMP協(xié)議并不能解決問題、因?yàn)閿?shù)據(jù)包一旦超時(shí)或丟失、IP協(xié)議(只負(fù)責(zé)運(yùn)輸數(shù)據(jù))通常的反應(yīng)就是重新發(fā)送、久而久之造成網(wǎng)絡(luò)癱瘓。

因此、需要上層(TCP)協(xié)議對(duì)網(wǎng)絡(luò)狀態(tài)進(jìn)行判斷并且做出正確的反應(yīng)。《《滑動(dòng)窗口機(jī)制》》的另一個(gè)作用就是減輕或避免網(wǎng)絡(luò)擁塞。
而TCP專門負(fù)責(zé)處理網(wǎng)絡(luò)擁塞的機(jī)制稱為《慢啟動(dòng)》《成倍減少》、二者相輔相成。

  • 擁塞窗口

之前我們提到發(fā)送方的窗口不能大于接收方窗口
這里還有另一個(gè)限制、就是擁塞窗口。
實(shí)際的發(fā)送窗口狀態(tài)將會(huì)二者取其小。

  • 成倍減少機(jī)制

當(dāng)擁塞發(fā)生。通過(guò)減小擁塞窗口的大小來(lái)減少發(fā)送的數(shù)據(jù)包數(shù)量。

當(dāng)TCP協(xié)議發(fā)現(xiàn)數(shù)據(jù)包丟失(即將超時(shí)重發(fā))時(shí)、具體的操作是:

  1. 減少窗口
    將擁塞窗口減小一半(但至少為1)。
  2. 增加超時(shí)間隔
    并且將仍在其中的數(shù)據(jù)段重發(fā)時(shí)間增加一倍。

于是、如果數(shù)據(jù)包不斷丟失、TCP的發(fā)送窗口和重發(fā)時(shí)間都會(huì)以指數(shù)級(jí)變化。
最終、發(fā)送的數(shù)據(jù)段將減小到1、且重發(fā)間隔不斷增加。以達(dá)到幫助路由器緩解擁塞的作用。

  • 慢啟動(dòng)機(jī)制

當(dāng)路由器從擁塞狀態(tài)恢復(fù)之后、TCP恢復(fù)到正常發(fā)送窗口的過(guò)程。

TCP不會(huì)立即將發(fā)送窗口恢復(fù)到與接收窗口匹配的狀態(tài)、以防再次造成擁塞。

具體機(jī)制是這樣的:
TCP在《新啟動(dòng)一個(gè)連接》或者《擁塞結(jié)束》時(shí)、將擁塞窗口的大小設(shè)置為一個(gè)數(shù)據(jù)段的大小。
然后每收到一個(gè)確認(rèn)段、TCP就將擁塞窗口的大小增加一倍數(shù)據(jù)段

雖然稱為慢啟動(dòng)、但是如果網(wǎng)絡(luò)暢通、窗口將會(huì)以指數(shù)級(jí)增加、該機(jī)制并不會(huì)影響傳輸?shù)乃俣取?/em>

  • 擁塞避免機(jī)制

為了防止慢啟動(dòng)時(shí)窗口擴(kuò)大過(guò)快、TCP還有這樣一個(gè)機(jī)制:
《在擁塞發(fā)生并恢復(fù)后》、一旦通過(guò)慢啟動(dòng)機(jī)制使得擁塞窗口的大小達(dá)到了擁塞之前的一半
TCP將會(huì)啟動(dòng)一個(gè)擁塞避免機(jī)制以減緩窗口擴(kuò)大的速度。
在該機(jī)制下、只有當(dāng)前窗口所有的數(shù)據(jù)確認(rèn)后、才增加一個(gè)數(shù)據(jù)段大小。

————————

緊急數(shù)據(jù)的傳輸

緊急數(shù)據(jù)是指發(fā)送是不經(jīng)過(guò)緩沖區(qū)的拼接。應(yīng)用層要求直接發(fā)送到接收方的數(shù)據(jù)。

緊急數(shù)據(jù)分為以下兩種:

  • 普通緊急數(shù)據(jù)

數(shù)據(jù)雖然會(huì)馬上發(fā)送給另一端、但是還需要對(duì)方按照字節(jié)流的順序進(jìn)行處理、因此需要先將前面的數(shù)據(jù)發(fā)送或處理后才能發(fā)送該緊急數(shù)據(jù)。

比如主機(jī)A的輸入框輸入了幾個(gè)字符、這些字符需要馬上發(fā)送給主機(jī)B。但是幾個(gè)字符的數(shù)據(jù)量不足以組成一個(gè)TCP數(shù)據(jù)段。因此需要用這個(gè)機(jī)制將用戶指定的數(shù)據(jù)馬上發(fā)送出去。但主機(jī)B處理的時(shí)候依舊是按照接收順序來(lái)處理的。

  • 帶外數(shù)據(jù)

數(shù)據(jù)也會(huì)馬上發(fā)送給另一端、同時(shí)需要接收方馬上處理(而不管接收時(shí)的字節(jié)序)。

也就是所帶外數(shù)據(jù)存在于另一個(gè)數(shù)據(jù)流中、比普通數(shù)據(jù)的優(yōu)先級(jí)更高。

————————

超時(shí)的判斷

確認(rèn)重發(fā)機(jī)制要求發(fā)送方在發(fā)送數(shù)據(jù)后啟動(dòng)一個(gè)計(jì)時(shí)器、在計(jì)時(shí)器超時(shí)后就假設(shè)數(shù)據(jù)丟失并重發(fā)。


TCP的傻窗口癥狀

由于接收方的處理速度過(guò)慢、導(dǎo)致接收方窗口很小(比如1B)。
進(jìn)而導(dǎo)致發(fā)送方每次發(fā)送數(shù)據(jù)也很小。浪費(fèi)了帶寬加重了發(fā)送方和接收方底層協(xié)議的負(fù)擔(dān)

  • 如何解決傻窗口的問題呢?

對(duì)于接收方--防止發(fā)送小窗口

在發(fā)送過(guò)0大小的窗口確認(rèn)通知后。
如果緩存區(qū)沒有擴(kuò)大到足夠的大小、則延遲發(fā)出確認(rèn)通知。
等待應(yīng)用程序處理一定量的數(shù)據(jù)知道騰出足夠大小的緩沖區(qū)(最大可用緩沖區(qū)大小的一半/最大數(shù)據(jù)段二者中較小的值)、再發(fā)送新的確認(rèn)通知擴(kuò)大窗口。

不過(guò)延遲確認(rèn)發(fā)送也有自己的缺點(diǎn)。

  1. 可能會(huì)造成無(wú)用的重發(fā)
  2. 由于TCP需要通過(guò)確認(rèn)到達(dá)的時(shí)間來(lái)估算RTT(數(shù)據(jù)來(lái)回時(shí)間)、因此延遲確認(rèn)會(huì)增大RTT最終導(dǎo)致對(duì)丟失的數(shù)據(jù)重發(fā)等待的時(shí)間太長(zhǎng)。

于是、TCP規(guī)定數(shù)據(jù)確認(rèn)的延遲不能超過(guò)500ms。另外、為了讓發(fā)送方能夠收集足夠的RTT樣本、TCP規(guī)定對(duì)其他情況的數(shù)據(jù)段必須逐一進(jìn)行確認(rèn)。

對(duì)于發(fā)送方--防止在數(shù)據(jù)段中每次只發(fā)送小字節(jié)

利用緩沖區(qū)的機(jī)制實(shí)現(xiàn)、當(dāng)應(yīng)用程序提交了一個(gè)數(shù)據(jù)交給TCP協(xié)議發(fā)送、TCP都會(huì)現(xiàn)將該數(shù)據(jù)緩存。而后分為兩種情況:

  1. 如果TCP正在等待之前的確認(rèn)信息
    直到能夠組成一個(gè)最大數(shù)據(jù)段時(shí)再發(fā)送。
  2. 如果收到了一個(gè)確認(rèn)信息
    不在等待、直接發(fā)送。

以上的策略、對(duì)push操作也適用。
這個(gè)策略被稱為Nagle算法。幾乎不需要發(fā)送方為該算法進(jìn)行額外的計(jì)算、也不需要計(jì)時(shí)器這種資源。而且對(duì)各種應(yīng)用都適用、不會(huì)影響他們的吞吐量和反應(yīng)速度。


TCP協(xié)議與UDP協(xié)議的比較

面向連接的傳輸服務(wù)

  • TCP以連接作為協(xié)議數(shù)據(jù)的最終目標(biāo)
    TCP協(xié)議的端口是可以復(fù)用

對(duì)于TCP協(xié)議,要成功建立一個(gè)新的鏈接,需要保證新鏈接四個(gè)要素組合體的唯一性:客戶端的IP、客戶端的port、服務(wù)器端的IP、服務(wù)器端的port。也就是說(shuō),服務(wù)器端的同一個(gè)IP和port,可以和同一個(gè)客戶端的多個(gè)不同端口成功建立多個(gè)TCP鏈接(與多個(gè)不同的客戶端當(dāng)然也可以),只要保證【Server IP + Server Port + Client IP + Client Port】這個(gè)組合唯一不重復(fù)即可。

  • UDP以端口作為協(xié)議數(shù)據(jù)的最終目標(biāo)
    UDP協(xié)議的端口不可不用

對(duì)于UDP協(xié)議、是以監(jiān)聽端口作為操作的。而且在協(xié)議中、源端口和源IP地址都是可選項(xiàng)。哪怕不填(只制定了目的端口和目的地址)也可以成功發(fā)送。

  • TCP協(xié)議需要先建立連接、然后才能發(fā)送/接收數(shù)據(jù)
    并且需要對(duì)很多細(xì)節(jié)進(jìn)行協(xié)商(最大數(shù)據(jù)長(zhǎng)度、窗口大小、初始序列號(hào)等)
  • UDP協(xié)議直接發(fā)送/接收數(shù)據(jù)

可靠的傳輸服務(wù)

  • TCP協(xié)議提供的是可靠的傳輸服務(wù)
    以序列號(hào)保證有序、以重發(fā)機(jī)制保證成功發(fā)送。
  • UDP協(xié)議提供的是不可靠的傳輸服務(wù)
    可能會(huì)丟失、失序、重復(fù)等。

面向字節(jié)流的傳輸服務(wù)

  • TCP協(xié)議是以字節(jié)為單位流式傳輸數(shù)據(jù)
    TCP的傳輸是無(wú)邊界的

TCP通過(guò)字節(jié)流傳輸,即TCP將應(yīng)用程序看成是一連串的無(wú)結(jié)構(gòu)的字節(jié)流。每個(gè)TCP套接口有一個(gè)發(fā)送緩沖區(qū),如果字節(jié)流太長(zhǎng)時(shí),TCP會(huì)將其拆分進(jìn)行發(fā)送。當(dāng)字節(jié)流太短時(shí),TCP會(huì)等待緩沖區(qū)中的字節(jié)流達(dá)到一定程度時(shí)再構(gòu)成報(bào)文發(fā)送出去。

  • UDP協(xié)議是以數(shù)據(jù)塊傳輸數(shù)據(jù)
    UDP的傳輸是有邊界的

而UDP傳輸報(bào)文的方式是由應(yīng)用程序控制的,應(yīng)用層交給UDP多長(zhǎng)的報(bào)文,UDP照樣發(fā)送,既不拆分,也不合并,而是保留這些報(bào)文的邊界,即一次發(fā)送一個(gè)報(bào)文。


TCP應(yīng)用于UDP應(yīng)用

  • TCP應(yīng)用

大文件傳輸--FTP

FTP是TCP/IP協(xié)議族之一、屬于依賴TCP的應(yīng)用層協(xié)議。
適用于大批量、高可靠性要求的應(yīng)用

遠(yuǎn)程登錄--Telnet

適用于小批量、長(zhǎng)時(shí)間、高可靠性要求的應(yīng)用

可靠傳輸--http
  • UDP應(yīng)用

實(shí)時(shí)應(yīng)用

TCP的傳輸時(shí)有先后順序的、所以有時(shí)會(huì)阻塞

多播式應(yīng)用

TCP是面向連接的、如果要多播需要建立n*(n-1)個(gè)連接。

某些時(shí)候的降級(jí)策略

引用美團(tuán)移動(dòng)網(wǎng)絡(luò)優(yōu)化策略里的一段話:

當(dāng)TCP通道無(wú)法建立或者發(fā)生故障時(shí),可以使用UDP面向無(wú)連接的特性提供另一條請(qǐng)求通道,或者繞過(guò)代理長(zhǎng)連服務(wù)器之間向業(yè)務(wù)服務(wù)器發(fā)起HTTP公網(wǎng)請(qǐng)求。

  • 使用UDP時(shí)應(yīng)該注意一下幾點(diǎn):

1. 應(yīng)用程序必須自己來(lái)保證可靠性
應(yīng)用程序必須有自己的重發(fā)機(jī)制、數(shù)據(jù)失序處理、流量控制等。
2. 應(yīng)用程序必須自己來(lái)處理大塊數(shù)據(jù)
發(fā)送方對(duì)大塊數(shù)據(jù)進(jìn)行分割、接收方還要進(jìn)行重組


QQ關(guān)于TCP以及UDP的一些帖子整理

(需要聲明的是我找到的都是一兩年前的貼、現(xiàn)在QQ爸爸技術(shù)進(jìn)步實(shí)在是太快、沒準(zhǔn)哪天就改了)

  • QQ既有UDP也有TCP

不管UDP還是TCP,最終登陸成功之后,QQ都會(huì)有一個(gè)TCP連接來(lái)保持在線狀態(tài)。這個(gè)TCP連接的遠(yuǎn)程端口一般是80,采用UDP方式登陸的時(shí)候,端口是8000。

  • 早期客戶端與客戶端主要使用UDP直接通訊

QQ客戶端之間的消息傳送也采用了UDP模式,因?yàn)閲?guó)內(nèi)的網(wǎng)絡(luò)環(huán)境非常復(fù)雜,而且很多用戶采用的方式是通過(guò)代理服務(wù)器共享一條線路上網(wǎng)的方式,在這些復(fù)雜的情況下,客戶端之間能彼此建立起來(lái)TCP連接的概率較小,嚴(yán)重影響傳送信息的效率。而UDP包能夠穿透大部分的代理服務(wù)器,因此QQ選擇了UDP作為客戶之間的主要通信協(xié)議。

  • 有了代理服務(wù)器之后的當(dāng)下

現(xiàn)在采用UDP協(xié)議,通過(guò)服務(wù)器中轉(zhuǎn)方式。因此,現(xiàn)在的IP偵探在你僅僅跟對(duì)方發(fā)送聊天消息的時(shí)候是無(wú)法獲取到IP的。大家都知道,UDP 協(xié)議是不可靠協(xié)議,它只管發(fā)送,不管對(duì)方是否收到的,但它的傳輸很高效。但是,作為聊天軟件,怎么可以采用這樣的不可靠方式來(lái)傳輸消息呢?于是,騰訊采用了上層協(xié)議來(lái)保證可靠傳輸:如果客戶端使用UDP協(xié)議發(fā)出消息后,服務(wù)器收到該包,需要使用UDP協(xié)議發(fā)回一個(gè)應(yīng)答包。如此來(lái)保證消息可以無(wú)遺漏傳輸。之所以會(huì)發(fā)生在客戶端明明看到“消息發(fā)送失敗”但對(duì)方又收到了這個(gè)消息的情況,就是因?yàn)榭蛻舳税l(fā)出的消息服務(wù)器已經(jīng)收到并轉(zhuǎn)發(fā)成功,但客戶端由于網(wǎng)絡(luò)原因沒有收到服務(wù)器的應(yīng)答包引起的。

  • 為什么最初選擇UDP

某次架構(gòu)師大會(huì)上那個(gè)58同城做即時(shí)通信的人說(shuō):原因是因?yàn)楫?dāng)時(shí)沒有epoll這種可以支持成千上萬(wàn)tcp并發(fā)連接的技術(shù),所以他們使用了udp,然后在udp上面封裝了一下,模擬了一下tcp,解決了大并發(fā)的問題,之后因?yàn)樽龅暮躰b了,雖然epoll這種技術(shù)出現(xiàn)了,還是沒有改回使用tcp了.現(xiàn)在再做類似的東西就不需要使用udp了.

  • QQ的TCP長(zhǎng)連接到底在做什么、為什么不用已經(jīng)建立好的TCP卻還是用UDP發(fā)送消息?

分析一下QQ你就會(huì)發(fā)現(xiàn),QQ登錄時(shí)會(huì)發(fā)送請(qǐng)求給服務(wù)器,QQ的服務(wù)器并不是一直監(jiān)聽客戶端,反之是通過(guò)30秒的一個(gè)時(shí)間間隔來(lái)確定客戶端是否還在,這個(gè)主要的以客戶端完成,30秒以內(nèi)客戶端會(huì)和服務(wù)器握手,告訴服務(wù)器我還在,狀態(tài)正常,而服務(wù)器會(huì)記錄下這個(gè)時(shí)間,如果30后再次收到客戶端握手則維護(hù)當(dāng)前狀態(tài),如果收不到則表示客戶端掉線了。然后服務(wù)器清除當(dāng)前客戶端的狀態(tài)和注銷用戶。這就是為什么如果我們是退出QQ,對(duì)方很快就看到我們下線了,不超過(guò)五秒,但是如果我們的網(wǎng)絡(luò)出現(xiàn)故障意外斷線了,對(duì)方要看到你下線需要在大約30-40秒之后。所以使用UDP是為了降低服務(wù)器的壓力,

  • 大文件傳輸/視頻下的TCP

QQ在文件傳輸和媒體通訊過(guò)程中使用UDP降低了很大的服務(wù)器承載(《《《關(guān)于這個(gè)問題、有人也說(shuō)使用的是UDP直接在兩個(gè)客戶端之間打洞》》》)。我自己也做IM,早期的版本全跑TCP,用戶承載沒問題,但是當(dāng)有文件傳輸時(shí) 服務(wù)器承載能力大打折扣,更關(guān)鍵的是多人視頻,使用UDP點(diǎn)對(duì)點(diǎn)之后客戶端內(nèi)存增加了,CPU也增加了,但是服務(wù)器承載變強(qiáng)了。在使用TCP時(shí) ,200人同時(shí)視頻通話 服務(wù)器程序就吃掉970MB內(nèi)存,2GB的服務(wù)器 只能承載200人就差不多極限,8GB內(nèi)存 4核CPU的服務(wù)器也只能承載500-800人的視頻通話,改成UDP之后差不多只占原來(lái)服務(wù)器資源的十五分之一。但是客戶端內(nèi)存消耗增加,使用服務(wù)器中轉(zhuǎn)時(shí),客戶端9個(gè)視頻,內(nèi)存消耗大約90MB,UDP之后這個(gè)內(nèi)存飆升到170MB。


參考資料

讀懂TCP狀態(tài)轉(zhuǎn)移
理解TCP序列號(hào)(Sequence Number)和確認(rèn)號(hào)(Acknowledgment Number)
淺析TCP之頭部可選項(xiàng)
TCP server 為什么一個(gè)端口可以建立多個(gè)連接?
美團(tuán)點(diǎn)評(píng)移動(dòng)網(wǎng)絡(luò)優(yōu)化實(shí)踐
困惑的QQ:既然有了TCP長(zhǎng)連接為什么還要用UDP來(lái)中轉(zhuǎn)數(shù)據(jù)呢?
為什么QQ用的是UDP協(xié)議而不是TCP協(xié)議?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,530評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評(píng)論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,759評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,204評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,415評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,955評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,782評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,983評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,222評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,675評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,967評(píng)論 2 374