之前簡(jiǎn)單的通過(guò)TCP/IP模型介紹了網(wǎng)絡(luò)編程,這篇主要介紹TCP/IP協(xié)議。
TCP/IP協(xié)議其實(shí)是一個(gè)協(xié)議簇,其中比較重要的有SLIP協(xié)議、PPP協(xié)議、IP協(xié)議、ICMP協(xié)議、ARP協(xié)議、TCP協(xié)議、UDP協(xié)議、FTP協(xié)議、DNS協(xié)議、SMTP協(xié)議等。這里我們主要介紹幾個(gè)常見(jiàn)的協(xié)議。
一、IP協(xié)議
IP協(xié)議(Internet Protocol),又叫網(wǎng)際協(xié)議,它解決了多個(gè)局域網(wǎng)互通的問(wèn)題,提供不可靠、無(wú)連接的數(shù)據(jù)報(bào)傳送服務(wù)。
- 不可靠:它不能保證IP數(shù)據(jù)報(bào)能成功到達(dá)目的地。IP協(xié)議會(huì)盡最大的努力,把數(shù)據(jù)包發(fā)送到目的地,但如果期間出現(xiàn)問(wèn)題,則發(fā)送失敗。比如路由器暫時(shí)用完了緩沖區(qū)。
- 無(wú)連接:IP并不維護(hù)任何關(guān)于后續(xù)數(shù)據(jù)報(bào)的狀態(tài)信息,可以不按發(fā)送順序接收。比如先后發(fā)送A、B兩條數(shù)據(jù)報(bào)。A、B是相互獨(dú)立的,可能會(huì)有不同的路由選擇,選擇不同的路線,所以并不能判定A、B誰(shuí)先到達(dá)。
1.IP首部
現(xiàn)在是看圖說(shuō)話時(shí)間(撿重要的說(shuō))。
- 普通的IP首部長(zhǎng)為20個(gè)字節(jié),除非含有選項(xiàng)字段。最高位在左面,記0bit,最低位在右面,記31bit。傳輸次序是大端序(Big Endian)。先傳0~7bit,其次8~15 bit,然后16~23 bit,最后是24~31 bit。
關(guān)于大端序小端序可以看這篇 - 目前通用的版本號(hào)是4,所以也叫IPv4。
- 首部長(zhǎng)度指的是首部占32 bit字的數(shù)目,包括任何選項(xiàng)。
- 總長(zhǎng)度字段是指整個(gè)IP數(shù)據(jù)報(bào)的長(zhǎng)度,最長(zhǎng)可達(dá)65535字節(jié)。
- 標(biāo)識(shí)字段唯一地標(biāo)識(shí)主機(jī)發(fā)送的每一份數(shù)據(jù)報(bào)。
- 生存時(shí)間字段設(shè)置了數(shù)據(jù)報(bào)可以經(jīng)過(guò)的最多路由器數(shù),初始值由源主機(jī)設(shè)置(通常為32或64),一旦經(jīng)過(guò)一個(gè)處理它的路由器,它的值就減去1。當(dāng)該字段的值為0時(shí),數(shù)據(jù)報(bào)就被丟棄,并發(fā)送ICMP報(bào)文通知源主機(jī)。
- 通過(guò)首部中的協(xié)議確定接收數(shù)據(jù)的上層協(xié)議(TCP/UDP/ICMP等)。
- 首部檢驗(yàn)和字段是根據(jù)IP首部計(jì)算的檢驗(yàn)和碼。它可以判斷首部在傳輸過(guò)程中有沒(méi)有發(fā)生任何差錯(cuò)。如果發(fā)生差錯(cuò)就丟棄收到的數(shù)據(jù)報(bào)。
- 源IP地址和目的IP地址在上篇提到。
- 選項(xiàng),是數(shù)據(jù)報(bào)中的一個(gè)可變長(zhǎng)的可選信息。
二、ICMP協(xié)議
ICMP(Internet Control Message Protocol),Internet控制報(bào)文協(xié)議,從技術(shù)角度來(lái)說(shuō),ICMP就是一個(gè)“錯(cuò)誤偵測(cè)與回報(bào)機(jī)制”,其目的就是讓我們能夠檢測(cè)網(wǎng)路的連線狀況﹐也能確保連線的準(zhǔn)確性﹐其主要功能包括,確認(rèn) IP 包是否成功送達(dá)目標(biāo)地址,通知在發(fā)送過(guò)程當(dāng)中 IP 包被廢棄的具體原因,改善網(wǎng)絡(luò)設(shè)置等。
三、TCP協(xié)議
TCP協(xié)議(Transmission Control Protocol),傳輸控制協(xié)議,位于傳輸層。它提供一種面向連接的、可靠的字節(jié)流服務(wù)。
- 面向連接:兩個(gè)使用TCP的應(yīng)用(通常是一個(gè)客戶和一個(gè)服務(wù)器)在彼此交換數(shù)據(jù)之前必須先建立一個(gè)TCP連接。廣播和多播不能用于TCP。
- 可靠的:TCP協(xié)議將會(huì)通過(guò)以下幾個(gè)方法提供可靠性
- 應(yīng)用數(shù)據(jù)被分割成TCP認(rèn)為最適合發(fā)送的數(shù)據(jù)塊。
- 當(dāng)TCP發(fā)出一個(gè)段后,它啟動(dòng)一個(gè)定時(shí)器,等待目的端確認(rèn)收到這個(gè)報(bào)文段。如果不能及時(shí)收到一個(gè)確認(rèn),將重發(fā)這個(gè)報(bào)文段。
- 當(dāng)TCP收到發(fā)自TCP連接另一端的數(shù)據(jù),它將發(fā)送一個(gè)確認(rèn)。這個(gè)確認(rèn)不是立即發(fā)送,通常將推遲幾分之一秒。
- TCP將保持它首部和數(shù)據(jù)的檢驗(yàn)和。這是一個(gè)端到端的檢驗(yàn)和,目的是檢測(cè)數(shù)據(jù)在傳輸過(guò)程中的任何變化。如果收到段的檢驗(yàn)和有差錯(cuò),TCP將丟棄這個(gè)報(bào)文段和不確認(rèn)收到此報(bào)文段。
- TCP數(shù)據(jù)是在IP數(shù)據(jù)包中傳輸,如果IP數(shù)據(jù)丟失,那么TCP數(shù)據(jù)也會(huì)丟失,進(jìn)行重發(fā)。如有必要,TCP將對(duì)收到的數(shù)據(jù)進(jìn)行重新排序,將收到的數(shù)據(jù)以正確的順序交給應(yīng)用層。
- TCP的接收端會(huì)丟棄重復(fù)的數(shù)據(jù)。
- TCP還能提供流量控制。TCP連接的每一方都有固定大小的緩沖空間。TCP的接收端只允許另一端發(fā)送接收端緩沖區(qū)所能接納的數(shù)據(jù)。這將防止較快主機(jī)致使較慢主機(jī)的緩沖區(qū)溢出。
其實(shí)我覺(jué)得Halfrost總結(jié)的很好:TCP就是通過(guò)檢驗(yàn)和、序列號(hào)、確認(rèn)應(yīng)答、重發(fā)控制、連接管理以及窗口控制等機(jī)制實(shí)現(xiàn)可靠性傳輸。
1.TCP首部
如果不計(jì)任選字段,它通常是20個(gè)字節(jié)。
依然進(jìn)入看圖說(shuō)話環(huán)節(jié):
- 每個(gè)TCP段都包含源端和目的端的端口號(hào),用于尋找發(fā)端和收端應(yīng)用進(jìn)程。
- 序號(hào),即Sequence Number,用來(lái)解決網(wǎng)絡(luò)包亂序問(wèn)題。
- 確認(rèn)序號(hào),即Acknowledgement Number,就是ACK——用于確認(rèn)收到,用來(lái)解決不丟包的問(wèn)題。
- 首部長(zhǎng)度,或叫數(shù)據(jù)偏移,該字段表示 TCP 所傳輸?shù)臄?shù)據(jù)部分應(yīng)該從 TCP 包的哪個(gè)位開(kāi)始計(jì)算。
- 保留,該字段主要是為了以后擴(kuò)展使用。
- 窗口控制,TCP的流量控制由連接的每一端通過(guò)聲明的窗口大小來(lái)提供。
- 檢驗(yàn)和,它覆蓋了整個(gè)的TCP報(bào)文段:TCP首部和TCP數(shù)據(jù)。這是一個(gè)強(qiáng)制性的字段,一定是由發(fā)端計(jì)算和存儲(chǔ),并由收端進(jìn)行驗(yàn)證。
- 緊急指針,只有當(dāng)URG標(biāo)志置1時(shí)緊急指針才有效
2.TCP三次握手、四次揮手
2.1三次握手
簡(jiǎn)單來(lái)說(shuō)過(guò)程就是這樣的:
- 客戶端首先向服務(wù)端發(fā)送一個(gè)SYN包和一個(gè)隨機(jī)序列號(hào) J
- 服務(wù)端收到后會(huì)回復(fù)客戶端一個(gè) SYN-ACK 包以及一個(gè)確認(rèn)號(hào)(用于確認(rèn)收到 SYN)J+1,同時(shí)再發(fā)送一個(gè)隨機(jī)序列號(hào) K
- 客戶端收到后會(huì)發(fā)送一個(gè) ACK 包和確定號(hào) K+1 給服務(wù)端
我們看一個(gè)例子:
% sudo tcpdump -c 3 -i en3 -nS host 23.63.125.15
18:31:29.140787 IP 10.0.1.6.52181 > 23.63.125.15.80: Flags [S], seq 1721092979, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 743929763 ecr 0,sackOK,eol], length 0
18:31:29.150866 IP 23.63.125.15.80 > 10.0.1.6.52181: Flags [S.], seq 673593777, ack 1721092980, win 14480, options [mss 1460,sackOK,TS val 1433256622 ecr 743929763,nop,wscale 1], length 0
18:31:29.150908 IP 10.0.1.6.52181 > 23.63.125.15.80: Flags [.], ack 673593778, win 8235, options [nop,nop,TS val 743929773 ecr 1433256622], length 0
第一行:
client -> server
左面是發(fā)送時(shí)間18:31
,IP
代表的是這些都是 IP 協(xié)議數(shù)據(jù)包。
10.0.1.6.52181 > 23.63.125.15.80
,代表源和目標(biāo)端的 IP 地址+端口。比如10.0.1.6.52181
,10.0.1.6
是IP地址,52181
是端口號(hào)。
Flags
表示 TCP 報(bào)文段 header 信息中的一些縮寫標(biāo)識(shí)。S
代表 SYN
,.
代表ACK
,P
代表PUSH
,F
是FIN
。
1721092979
是隨機(jī)號(hào)seq
第二行
server -> client
前面幾個(gè)就不多做說(shuō)明,Flags [S.]
,表示報(bào)文段 header中帶有SYN
和ACK
。
ack
為1721092980
,即第一行的seq+1
,seq
為673593777
第三行
client -> server
客戶端接收到信息后,發(fā)送Flags [.]
,報(bào)文段 header中ACK
。
ack
為673593778
,即第二行的seq+1
。
2.2四次揮手
- 客戶端要斷開(kāi)連接了,向服務(wù)器發(fā)送
FIN
信號(hào),告訴它我的數(shù)據(jù)發(fā)送完了,但是如果你有數(shù)據(jù),我還可以接受,然后進(jìn)入FIN_WAIT_1
狀態(tài) - 服務(wù)器接確認(rèn)
FIN
包后,發(fā)送一個(gè)ack
確認(rèn)包,告訴客戶端我已接收到信息,但還沒(méi)有做好關(guān)閉準(zhǔn)備。發(fā)送完畢后,服務(wù)器端進(jìn)入CLOSE_WAIT
狀態(tài),客戶端接收到這個(gè)確認(rèn)包之后,進(jìn)入FIN_WAIT_2
狀態(tài),等待服務(wù)器端關(guān)閉連接。 - 服務(wù)器端準(zhǔn)備好關(guān)閉連接時(shí),向客戶端發(fā)送
FIN
包,要斷開(kāi)連接。然后進(jìn)入服務(wù)器進(jìn)入LAST_ACK
狀態(tài)。 - 客戶端接收到來(lái)自服務(wù)器端的關(guān)閉請(qǐng)求,發(fā)送一個(gè)確認(rèn)包,并進(jìn)入
TIME_WAIT
狀態(tài),等待可能出現(xiàn)的要求重傳的ACK
包。服務(wù)器端接收到這個(gè)確認(rèn)包之后,關(guān)閉連接,進(jìn)入CLOSED
狀態(tài)。等過(guò)了一定時(shí)間后,客戶端沒(méi)有收到服務(wù)器的ACK
包,就知道已經(jīng)斷開(kāi)連接,進(jìn)入CLOSED
狀態(tài)。
2.3為什么要進(jìn)行三次握手?
通常我們會(huì)覺(jué)得,客戶端告訴服務(wù)器,我要和你握手了,然后服務(wù)器告訴客戶端,我知道了,這樣就可以了。為什么要多客戶端再給服務(wù)器確認(rèn)一遍呢?
這是因?yàn)槲覀儸F(xiàn)實(shí)生活中的網(wǎng)絡(luò)是復(fù)雜的,TCP數(shù)據(jù)在傳輸?shù)倪^(guò)程中,很有可能會(huì)出現(xiàn)延遲到達(dá)或者重發(fā)的情況。我們?cè)谥罢劦絋CP的可靠性時(shí),說(shuō)到當(dāng)TCP發(fā)出一個(gè)段后,它啟動(dòng)一個(gè)定時(shí)器,等待目的端確認(rèn)收到這個(gè)報(bào)文段。如果不能及時(shí)收到一個(gè)確認(rèn),將重發(fā)這個(gè)報(bào)文段。這個(gè)時(shí)候TCP首部的序列號(hào)是相同的。
如果因?yàn)榫W(wǎng)絡(luò)問(wèn)題,第二個(gè)SYN
包提前到達(dá),在連接結(jié)束后第一個(gè)SYN
包才到,服務(wù)器又會(huì)發(fā)一次ACK
建立連接。此時(shí)服務(wù)器建立了連接,客戶端沒(méi)有連接,從而導(dǎo)致服務(wù)端建立了一個(gè)空的連接,浪費(fèi)資源。
如果是三次握手,客戶端再次收到相同的ACK
時(shí),會(huì)丟棄這個(gè)包,不向服務(wù)端發(fā)送ACK
和ack
,從而避免了空連接。
2.4為什么進(jìn)行四次揮手
因?yàn)門CP是雙全工模式,即可以同時(shí)發(fā)送和接收數(shù)據(jù),兩條通道是完全獨(dú)立的。客戶端向服務(wù)器揮手關(guān)閉的時(shí)候,服務(wù)器會(huì)繼續(xù)傳送之前沒(méi)有傳完的數(shù)據(jù)。在二三揮手之間,多了一個(gè)數(shù)據(jù)傳送的過(guò)程,這也是為什么ACK
和FIN
不能同時(shí)發(fā)送的原因。
3. SYN攻擊
3.1 SYN攻擊是什么
在三次握手過(guò)程中,服務(wù)器發(fā)送SYN-ACK
之后,收到客戶端的ACK
之前的TCP連接成為半連接(half-open connect)。此時(shí)服務(wù)器處于SYN_RCVD
狀態(tài)。當(dāng)收到ACK
之后,服務(wù)器才能轉(zhuǎn)入ESTABLISHED
狀態(tài)。
SYN攻擊指的是,攻擊客戶端在短時(shí)間內(nèi)偽造大量不存在的IP地址,向服務(wù)器不斷發(fā)送SYN
包,服務(wù)器回復(fù)確認(rèn)包,并等待客戶的確認(rèn)。由于源地址是不存在的,服務(wù)器需要不斷地重發(fā)直至超時(shí),這些偽造的SYN
包將長(zhǎng)時(shí)間占用未連接隊(duì)列,正常的SYN
請(qǐng)求被丟棄,導(dǎo)致目標(biāo)系統(tǒng)運(yùn)行緩慢,嚴(yán)重會(huì)造成網(wǎng)絡(luò)堵塞甚至系統(tǒng)癱瘓。
SYN攻擊是一種典型的DoS/DDoS
攻擊。
3.2 如何檢測(cè)SYN攻擊
檢測(cè)SYN攻擊非常的方便,當(dāng)你在服務(wù)器上看到大量的半連接狀態(tài)時(shí),特別是源IP地址是隨機(jī)的,基本上可以斷定這是一次SYN攻擊。在 Linux/Unix 上可以使用系統(tǒng)自帶的netstats
命令來(lái)檢測(cè) SYN 攻擊。
3.3 如何防御SYN攻擊
SYN攻擊不能完全被阻止,除非將TCP協(xié)議重新設(shè)計(jì)。我們所做的是盡可能的減輕SYN攻擊的危害,常見(jiàn)的防御 SYN 攻擊的方法有如下幾種:
- 縮短超時(shí)(SYN Timeout)時(shí)間
- 增加最大半連接數(shù)
- 過(guò)濾網(wǎng)關(guān)防護(hù)
- SYN cookies技術(shù)
4. TCP KeepAlive
TCP 通信雙方建立交互的連接,但是并不是一直存在數(shù)據(jù)交互,有些連接會(huì)在數(shù)據(jù)交互完畢后,主動(dòng)釋放連接,而有些不會(huì)。交互雙方出現(xiàn)死機(jī)、異常重啟等情況都不會(huì)使TCP連接及時(shí)正常釋放,造成端系統(tǒng)資源的消耗和浪費(fèi)。為了解決這個(gè)問(wèn)題,在傳輸層可以利用 TCP 的 KeepAlive 機(jī)制實(shí)現(xiàn)來(lái)實(shí)現(xiàn)。主流的操作系統(tǒng)基本都在內(nèi)核里支持了這個(gè)特性。
TCP KeepAlive 的基本原理是,隔一段時(shí)間給連接對(duì)端發(fā)送一個(gè)探測(cè)包,如果收到對(duì)方回應(yīng)的 ACK,則認(rèn)為連接還是存活的,在超過(guò)一定重試次數(shù)之后還是沒(méi)有收到對(duì)方的回應(yīng),則丟棄該 TCP 連接。
但TCP KeepAlive 是有局限。首先 TCP KeepAlive 監(jiān)測(cè)的方式是發(fā)送一個(gè) probe 包,會(huì)給網(wǎng)絡(luò)帶來(lái)額外的流量,另外 TCP KeepAlive 只能在內(nèi)核層級(jí)監(jiān)測(cè)連接的存活與否,而連接的存活不一定代表服務(wù)的可用。例如當(dāng)一個(gè)服務(wù)器 CPU 進(jìn)程服務(wù)器占用達(dá)到 100%,已經(jīng)卡死不能響應(yīng)請(qǐng)求了,此時(shí) TCP KeepAlive 依然會(huì)認(rèn)為連接是存活的。因此 TCP KeepAlive 對(duì)于應(yīng)用層程序的價(jià)值是相對(duì)較小的。需要做連接保活的應(yīng)用層程序,例如 QQ,往往會(huì)在應(yīng)用層實(shí)現(xiàn)自己的心跳功能。
四、UDP協(xié)議
UDP協(xié)議(User Datagram Protocol),用戶數(shù)據(jù)報(bào)協(xié)議。UDP 不提供復(fù)雜的控制機(jī)制,利用 IP 提供面向無(wú)連接的通信服務(wù)。傳輸途中即使出現(xiàn)丟包,UDP 也不負(fù)責(zé)重發(fā),甚至當(dāng)出現(xiàn)包的到達(dá)順序亂掉時(shí)也沒(méi)有糾正的功能。UDP 面向無(wú)連接,它可以隨時(shí)發(fā)送數(shù)據(jù)。
- 面向無(wú)連接:通信雙方不需要事先建立一條連接,而是把每個(gè)帶有目的地址的包送到線路上,由系統(tǒng)自主選定路線進(jìn)行傳輸。
- 不可靠性:它把應(yīng)用程序傳給IP層的數(shù)據(jù)發(fā)送出去,但是并不保證它們能到達(dá)目的。
1.UDP首部
- 端口號(hào)不在說(shuō)明了,和TCP一樣,只不過(guò)TCP端口號(hào)由TCP來(lái)查看,而UDP端口號(hào)由UDP來(lái)查看,這是根據(jù)IP數(shù)據(jù)報(bào)里面的協(xié)議來(lái)區(qū)分的。
- UDP長(zhǎng)度字段指的是UDP首部和UDP數(shù)據(jù)的字節(jié)長(zhǎng)度
- UDP檢驗(yàn)和覆蓋UDP首部和UDP數(shù)據(jù)。和TCP區(qū)別是,TCP檢驗(yàn)和是必需的,而UDP的檢驗(yàn)和是可選的。
2.TCP和UDP區(qū)別
- 數(shù)據(jù)發(fā)送方式區(qū)別:TCP是建立在兩端連接之上的協(xié)議,UDP本身發(fā)送的就是一份份數(shù)據(jù)報(bào)。
- 數(shù)據(jù)大小的區(qū)別:TCP理論上發(fā)送的數(shù)據(jù)流沒(méi)有上限,但是由于緩沖區(qū)有大小限制,但如果TCP數(shù)據(jù)過(guò)大,會(huì)被截為好幾段,接收方依次接收。UDP報(bào)文長(zhǎng)度不能超過(guò)2^16=65536(如果想具體了解UDP包長(zhǎng)限制,可以看這篇).
- 數(shù)據(jù)有序性的區(qū)別:TCP本身有著超時(shí)重傳、錯(cuò)誤重傳、還有等等一系列復(fù)雜的算法保證了 TCP 的數(shù)據(jù)是有序的。UDP并不具備數(shù)據(jù)糾正功能。
- 數(shù)據(jù)可靠性的區(qū)別:TCP 的超時(shí)重傳、錯(cuò)誤重傳、TCP 的流量控制、阻塞控制、慢熱啟動(dòng)算法、擁塞避免算法、快速恢復(fù)算法等方法都使得TCP在保持連接的過(guò)程中是可靠的。UDP是一個(gè)面向非連接的協(xié)議,UDP 發(fā)送的每個(gè)數(shù)據(jù)報(bào)帶有自己的 IP 地址和接收方的 IP 地址,它本身對(duì)這個(gè)數(shù)據(jù)報(bào)是否出錯(cuò),是否到達(dá)不關(guān)心,只要發(fā)出去了就好了。
- 使用場(chǎng)景區(qū)別:UDP 主要用于那些對(duì)高速傳輸和實(shí)時(shí)性有較高要求的通信或廣播通信,比如:流媒體、物聯(lián)網(wǎng)、實(shí)時(shí)游戲等。其他就可以考慮使用TCP(只是考慮,具體問(wèn)題具體分析),比如QQ文件傳輸。
五、DNS協(xié)議
通常我們?nèi)フ?qǐng)求一個(gè)網(wǎng)址輸入的是https://github.com
這種形式,但這并不是IP地址。那我們?cè)趺慈ふ宜牡刂纺兀窟@時(shí)候就需要DNS。
DNS域名系統(tǒng)是一種用于TCP/IP應(yīng)用程序的分布式數(shù)據(jù)庫(kù)。我們向DNS服務(wù)器發(fā)送一個(gè)DNS數(shù)據(jù)包,然后DNS服務(wù)器做出響應(yīng)告訴我們github的IP地址是192.30.253.113
。
目前有很多DNS服務(wù)器,比如google的8.8.8.8
,阿里的223.5.5.0
,百度的180.76.76.76
。
有時(shí)候我們會(huì)遇到DNS被污染的情況,這個(gè)時(shí)候我們就沒(méi)辦法去請(qǐng)求某一個(gè)網(wǎng)頁(yè)。一個(gè)小技巧:在使用MAC的情況下,在跳轉(zhuǎn)路徑輸入:/private/etc/
,找到hosts文件,添加內(nèi)容即可。
以上便是我對(duì)TCP/IP協(xié)議的理解,如果不當(dāng)之處還望指出。
參考:《TCP/IP詳解 卷1:協(xié)議》
TCP/IP基礎(chǔ)概述
IP,TCP 和 HTTP