计算机网络——网络抓包实战(Tcpdump/Wireshark)
摘要
對于日常工作的開發工作中,大部分同學都沒有涉及到網絡的抓包工作,同時對于大部分開發的同學來說對于網絡抓包工具也是不熟悉。那本文將介紹計算機網絡中用于抓包的兩個常用工具tcpdump和Wireshark,通過使用tcpdump/Wireshark工具來對TCP的三次握手和四次揮手進行抓包實戰,來幫助大家更好的理解計算機網絡中的連接與斷開的原理,同時也是幫助大家學會使用網絡抓包工具來解決項目中的網絡問題。
| 計算機網絡知識腦圖 | 計算機網絡——計算機網絡知識腦圖_莊小焱的博客-CSDN博客_計算機網絡面試題總結 |
| 計算機網絡大廠面試問題集合 | 計算機網絡——大廠面試問題集合_莊小焱的博客-CSDN博客 |
| 計算機網絡基礎知識 | 計算機網絡——網絡基礎知識_莊小焱的博客-CSDN博客_數據轉發服務器 |
| IP相關基礎原理 | 計算機網絡——IP協議基礎原理_莊小焱的博客-CSDN博客_ip網絡技術 |
| HTTP協議原理 | 計算機網絡——HTTP協議原理_莊小焱的博客-CSDN博客_http協議原理 |
| HTTP的優化方式 | 計算機網絡——HTTP的優化方式_莊小焱的博客-CSDN博客 |
| HTTPS協議原理 | 計算機網絡——HTTPS協議原理_莊小焱的博客-CSDN博客_https協議原理 |
| HTTPS的優化方式 | 計算機網絡——HTTPS的優化方式_莊小焱的博客-CSDN博客 |
| TCP可靠性傳輸原理 | 計算機網絡——TCP可靠性傳輸原理_莊小焱的博客-CSDN博客_tcp的可靠性是如何實現的 |
| TCP/IP三次握手四次揮手原理 | 計算機網絡——HTTP的三次握手與四次揮手原理_莊小焱的博客-CSDN博客_三次握手和四次揮手原理 |
| TCP的優化方式 | 計算機網絡——TCP的優化方式_莊小焱的博客-CSDN博客_tcp協議優化技術 |
| DNS協議(域名解析)原理 | 計算機網絡——DNS協議(域名解析)原理_莊小焱的博客-CSDN博客_計算機網絡dns |
| ARP協議(地址解析)原理 | 計算機網絡——ARP協議(地址解析)原理_莊小焱的博客-CSDN博客_地址解析協議的工作原理 |
| ARQ協議(自動重傳請求)原理 | 計算機網絡——ARQ協議(自動重傳請求)原理_莊小焱的博客-CSDN博客_連續arq協議的原理 |
| DHCP協議原理 | 計算機網絡——DHCP(動態獲取IP)原理_莊小焱的博客-CSDN博客_計算機網絡dhcp |
| NAT協議原理 | 計算機網絡——NAT協議(網絡地址轉換)原理_莊小焱的博客-CSDN博客 |
| ICMP/IGMP協議原理 | 計算機網絡——ICMP/IGMP協議原理_莊小焱的博客-CSDN博客_計算機網絡igmp |
| HTTP網絡訪問全流程 | 計算機網絡——HTTP網絡訪問全流程_莊小焱的博客-CSDN博客_網絡訪問流程 |
| 虛擬網路模型原理 | 計算機網絡——虛擬網路模型原理_莊小焱的博客-CSDN博客 |
| 其他網絡知識 | 計算機網絡——select/poll/epoll底層原理_莊小焱的博客-CSDN博客 計算機網絡——cookie/session/token原理_莊小焱的博客-CSDN博客 計算機網絡——網絡通信加密原理_莊小焱的博客-CSDN博客_網絡通信加密 計算機網絡——GRPC通信原理_莊小焱的博客-CSDN博客_grpc原理 計算機網絡——tcpdump/Wireshark抓包實戰_莊小焱的博客-CSDN博客_網絡抓包 計算機網絡——TCP抓包連接實戰_莊小焱的博客-CSDN博客_tcp全連接和半連接 |
一、網絡實戰工具
這兩者實際上是搭配使用的,先用Tcpdump命令在 Linux 服務器上抓包,接著把抓包的文件拖出到 Windows電腦后,用Wireshark可視化分析。如果你是在Windows上抓包,只需要用Wireshark工具就可以。
- linux : tcpdump
- window : Wireshark
二、Tcpdump實戰
2.1?tcpdump使用說明
| Tcpdump使用說明 | ||
| 選項 | 示例 | 說明 |
| -i | tcpdump -i eth0 | ?指定網絡接口 默認是0 號接口,any 表示所有接口 |
| -nn | tcpdump -nn? | 不解析IP地址和端口名稱 |
| -c | tcpdump -c 5 | 限制要抓取的網絡包的個數 |
| -w | tcpdump -w file.pcap | 將信息保存到文件呢中文件以。pcap為后綴 |
| host src host dst host | tcpdump -nn host 192.168.1.100 | 主機過濾 |
| port src port dst port | tcpdump --nn port 80 | 端口過濾 |
| ip ip6 arp tcp udp | tcpdump -nn tcp | 協議過濾 |
| and or not | tcpdump -nn host 192.168.1.100 and port 80 | 邏輯表達式過濾 |
| tcp[tcoflages] | tcpdump -nn “tcp[tcpflags] & tcp-syn! =0” | 特定狀態的TCP包 |
2.2 Linux下Tcpdump實戰
tcpdump提供了大量的選項以及各式各樣的過濾表達式,來幫助你抓取指定的數據包,不過不要擔心,只需要掌握一些常用選項和過濾表達式,就可以滿足大部分場景的需要了。假設我們要抓取下面的 ping 的數據包:
# -I ens192 表示從ens192 網卡出發 # -c 3 表示發出3個icmp數據(base) mystic@k8s-master:~$ ping -I ens192 -c 3 20.12.151.195 PING 20.12.151.195 (20.12.151.195) from 20.1.206.131 ens192: 56(84) bytes of data. 64 bytes from 20.12.151.195: icmp_seq=1 ttl=62 time=1.11 ms 64 bytes from 20.12.151.195: icmp_seq=2 ttl=62 time=0.421 ms 64 bytes from 20.12.151.195: icmp_seq=3 ttl=62 time=0.347 ms--- 20.12.151.195 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2021ms rtt min/avg/max/mdev = 0.347/0.626/1.111/0.344 ms要抓取上面的 ping 命令數據包,首先我們要知道 ping 的數據包是 icmp 協議,接著在使用 tcpdump抓包的時候,就可以指定只抓 icmp 協議的數據包:
tcpdump -i ens192 icmp and host 20.12.151.195 -nn# -i ens192 # icmp # host # -nn ------------------------------------------------------------------ 返回的結果:時間戳協議源地址.源端口 > 目的地址.目的端口網絡包詳細信息從tcpdump 抓取的 icmp 數據包,我們很清楚的看到 icmp echo的交互過程了,首先發送方發起了ICMP echo request 請求報文,接收方收到后回了一個ICMP echo reply 響應報文,之后 seq是遞增的。
三、Wireshark實戰
tcpdump -i ens192 icmp and host 20.12.151.195 -w ping.pcap接著把 ping.pcap 文件拖到電腦,再用 Wireshark 打開它。打開后,你就可以看到下面這個界面:
在Wireshark 的頁面里,可以更加直觀的分析數據包,不僅展示各個網絡包的頭部信息,還會用不同的顏色來區分不同的協議,由于這次抓包只有ICMP 協議,所以只有紫色的條目。接著,在網絡包列表中選擇某一個網絡包后,在其下面的網絡包詳情中,可以更清楚的看到,這個網絡包在協議棧各層的詳細信息。
從ping 的例子中,我們可以看到網絡分層就像有序的分工,每一層都有自己的責任范圍和信息,上層協議完成工作后就交給下一層,最終形成一個完整的網絡包。
3.1 TCP三次握手抓包實戰
本次例子,我們將要訪問的 http://192.168.3.200 服務端。在終端一用 tcpdump 命令抓取數據包:
tcpdump -i ens192 icmp and host 20.12.151.195 and port 80 -w test.pcap curl http://192.168.3.200?使用 Wireshark 打開 http.pcap 后,你就可以在 Wireshark 中,看到如下的界面:
Wireshark 可以用時序圖的方式顯示數據包交互的過程,從菜單欄中,點擊 統計 (Statistics) -> 流量圖(Flow Graph),然后,在彈出的界面中的「流量類型」選擇 「TCP Flows」,你可以更清晰的看到,整個過程中 TCP 流的執行過程:
為什么三次握手連接過程的 Seq 是0?實際上是Wireshark工具幫我們做了優化,它默認顯示的是序列號 seq 是相對值,而不是真實值。如果你想看到實際的序列號的值,可以右鍵菜單, 然后找到協議首選項,接著找到RelativeSeq后,把它給取消,操作如下:
3.2 TCP四次揮手抓包實戰
為什么抓到的TCP揮手是三次,而不是書上說的四次?因為服務器端收到客戶端的 FIN 后,服務器端同時也要關閉連接,這樣就可以把 ACK 和 FIN 合并到一起發送,節省了一個包,變成了“三次揮手”。而通常情況下,服務器端收到客戶端的 FIN 后,很可能還沒發送完數據,所以就會先回復客戶端一個ACK 包,稍等一會兒,完成所有數據包的發送后,才會發送 FIN 包,這也就是四次揮手了。如下圖,就是四次揮手的過程:
3.3 TCP 三次握手異常情況實戰分析
本次實驗用了兩臺虛擬機,一臺作為服務端,一臺作為客戶端,它們的關系如下:
3.3.1 TCP 第一次握手 SYN 丟包
為了模擬 TCP 第一次握手 SYN 丟包的情況,我是在拔掉服務器的網線后,立刻在客戶端執行 curl 命令:
# 客戶端發起請求 $ date ; curl http://192.168.25.16; date# 請求阻塞中…………其間 tcpdump 抓包的命令如下:
# 客戶端執行tcpdump 抓取到服務端的HTTP的數據包$ tcpdump -i ens192 tcp and host 192.168.12.36 and port -w tcp_sys_timeout.pcap?過了一會, curl 返回了超時連接的錯誤:
# 客戶端發起請求 $ date ; curl http://192.168.25.16; date# 請求阻塞中…………# 顯示 connect timeout?從date 返回的時間,可以發現在超時接近 1 分鐘的時間后,curl 返回了錯誤。接著,把 tcp_sys_timeout.pcap 文件用 Wireshark 打開分析,顯示如下圖:
從上圖可以發現, 客戶端發起了 SYN 包后,一直沒有收到服務端的 ACK ,所以一直超時重傳了次,并且每次 RTO 超時時間是不同的:
- 第一次是在 1 秒超時重傳
- 第二次是在 3 秒超時重傳
- 第三次是在 7 秒超時重傳
- 第四次是在 15 秒超時重傳
- 第五次是在 31 秒超時重傳
可以發現,每次超時時間 RTO 是指數(翻倍)上漲的,當超過最大重傳次數后,客戶端不再發送 SYN包。在 Linux 中,第一次握手的 SYN 超時重傳次數,是如下內核參數指定的:
$ cat /proc/sys/net/ipv4/tcp_syn_retries 5tcp_syn_retries 默認值為 5,也就是 SYN 最大重傳次數是 5 次。接下來,我們繼續做實驗,把 tcp_syn_retries 設置為 2 次:
$ echo 2 > /proc/sys/net/ipv4/tcp_syn_retries重傳抓包后,用 Wireshark 打開分析,顯示如下圖:
實驗一總結:通過實驗一的實驗結果,我們可以得知,當客戶端發起的 TCP 第一次握手 SYN 包,在超時時間內沒收到服務端的 ACK,就會在超時重傳 SYN 數據包,每次超時重傳的 RTO 是翻倍上漲的,直到 SYN 包的重傳次數到達 tcp_syn_retries 值后,客戶端不再發送 SYN 包。
3.3.2 TCP 第二次握手 SYN、ACK 丟包
為了模擬客戶端收不到服務端第二次握手 SYN、ACK 包,我的做法是在客戶端加上防火墻限制,直接粗暴的把來自服務端的數據都丟棄,防火墻的配置如下:
# 客戶端配置防火墻 iptable -I input -s 192.168.25.16 -j DROP?接著,在客戶端執行 curl 命令:
# 客戶端發起請求 $ date ; curl http://192.168.25.16; date# 請求阻塞中…………# 顯示 connect timeout?從 date 返回的時間前后,可以算出大概 1 分鐘后,curl 報錯退出了。客戶端在這其間抓取的數據包,用 Wireshark 打開分析,顯示的時序圖如下:
從圖中可以發現:
- 客戶端發起 SYN 后,由于防火墻屏蔽了服務端的所有數據包,所以 curl 是無法收到服務端的SYN、ACK 包,當發生超時后,就會重傳 SYN 包。
- 服務端收到客戶的 SYN 包后,就會回 SYN、ACK 包,但是客戶端一直沒有回 ACK,服務端在超時后,重傳了 SYN、ACK 包,接著一會,客戶端超時重傳的 SYN 包又抵達了服務端,服務端收到后,超時定時器就重新計時,然后回了 SYN、ACK 包,所以相當于服務端的超時定時器只觸發了一次,又被重置了。
- 最后,客戶端 SYN 超時重傳次數達到了 5 次(tcp_syn_retries 默認值 5 次),就不再繼續發送SYN 包了。
所以,我們可以發現,當第二次握手的 SYN、ACK 丟包時,客戶端會超時重發 SYN 包,服務端也會超時重傳 SYN、ACK 包。客戶端設置了防火墻,屏蔽了服務端的網絡包,為什么 tcpdump 還能抓到服務端的網絡包?添加 iptables 限制后, tcpdump 是否能抓到包 ,這要看添加的 iptables 限制條件:
- 如果添加的是 INPUT 規則,則可以抓得到包
- 如果添加的是 OUTPUT 規則,則抓不到包
網絡包進入主機后的順序如下:
- 進來的順序 Wire -> NIC -> tcpdump -> netfilter/iptables
- 出去的順序 iptables -> tcpdump -> NIC -> Wire
tcp_syn_retries 是限制 SYN 重傳次數,那第二次握手 SYN、ACK 限制最大重傳次數是多少?TCP 第二次握手 SYN、ACK 包的最大重傳次數是通過 tcp_synack_retries 內核參數限制的,其默認值如下:
$ cat /proc/sys/net/ipv4/tcp_synack_retries 5是的,TCP 第二次握手 SYN、ACK 包的最大重傳次數默認值是 5 次。 為了驗證 SYN、ACK 包最大重傳次數是 5 次,我們繼續做下實驗,我們先把客戶端的 tcp_syn_retries 設置為 1,表示客戶端 SYN 最大超時次數是 1 次,目的是為了防止多次重傳 SYN,把服務端 SYN、 ACK 超時定時器重置。接著,還是如上面的步驟:
- 1. 客戶端配置防火墻屏蔽服務端的數據包
- 2. 客戶端 tcpdump 抓取 curl 執行時的數據包
把抓取的數據包,用 Wireshark 打開分析,顯示的時序圖如下:
從上圖,我們可以分析出:
- 客戶端的 SYN 只超時重傳了 1 次,因為 tcp_syn_retries 值為 1
- 服務端應答了客戶端超時重傳的 SYN 包后,由于一直收不到客戶端的 ACK 包,所以服務端一直在超時重傳 SYN、ACK 包,每次的 RTO 也是指數上漲的,一共超時重傳了 5 次,因為tcp_synack_retries 值為 5
接著,我把 tcp_synack_retries 設置為 2, tcp_syn_retries 依然設置為 1:
$ echo 2 > /proc/sys/net/ipv4/tcp_synack_retries $ echo 1 > /proc/sys/net/ipv4/tcp_syn_retries依然保持一樣的實驗步驟進行操作,接著把抓取的數據包,用 Wireshark 打開分析,顯示的時序圖如下:
由上圖可見:
- 客戶端的 SYN 包只超時重傳了 1 次,符合 tcp_syn_retries 設置的值;
- 服務端的 SYN、ACK 超時重傳了 2 次,符合 tcp_synack_retries 設置的值
實驗二總結: 通過實驗二的實驗結果,我們可以得知,當 TCP 第二次握手 SYN、ACK 包丟了后,客戶端 SYN 包會發生超時重傳,服務端 SYN、ACK 也會發生超時重傳。客戶端 SYN 包超時重傳的最大次數,是由 tcp_syn_retries 決定的,默認值是 5 次;服務端 SYN、ACK 包時重傳的最大次數,是由 tcp_synack_retries 決定的,默認值是 5 次。
3.3.3 TCP 第三次握手 ACK 丟包
為了模擬 TCP 第三次握手 ACK 包丟,我的實驗方法是在服務端配置防火墻,屏蔽客戶端 TCP 報文中標志位是 ACK 的包,也就是當服務端收到客戶端的 TCP ACK 的報文時就會丟棄,iptables 配置命令如下:
?接著,在客戶端執行如下 tcpdump 命令:
?然后,客戶端向服務端發起 telnet,因為 telnet 命令是會發起 TCP 連接,所以用此命令做測試:
此時,由于服務端收不到第三次握手的 ACK 包,所以一直處于 SYN_RECV 狀態:
而客戶端是已完成 TCP 連接建立,處于 ESTABLISHED 狀態:
過了 1 分鐘后,觀察發現服務端的 TCP 連接不見了:
?過了 30 分別,客戶端依然還是處于 ESTABLISHED 狀態:
接著,在剛才客戶端建立的 telnet 會話,輸入 123456 字符,進行發送:
?持續「好長」一段時間,客戶端的 telnet 才斷開連接:
以上就是本次的實現三的現象,這里存在兩個疑點:
- 為什么服務端原本處于 SYN_RECV 狀態的連接,過 1 分鐘后就消失了?
原因:服務端在重傳 SYN、ACK 包時,超過了最大重傳次數 tcp_synack_retries ,于是服務端的 TCP連接主動斷開了。
- 為什么客戶端 telnet 輸入 123456 字符后,過了好長一段時間,telnet 才斷開連接?
原因:客戶端向服務端發送數據包時,由于服務端的 TCP 連接已經退出了,所以數據包一直在超時重傳,共重傳了 15 次, telnet 就斷開了連接。
上圖的流程:
- 客戶端發送 SYN 包給服務端,服務端收到后,回了個 SYN、ACK 包給客戶端,此時服務端的TCP 連接處于 SYN_RECV 狀態;
- 客戶端收到服務端的 SYN、ACK 包后,給服務端回了個 ACK 包,此時客戶端的 TCP 連接處于ESTABLISHED 狀態;
- 由于服務端配置了防火墻,屏蔽了客戶端的 ACK 包,所以服務端一直處于 SYN_RECV 狀態,沒有進入 ESTABLISHED 狀態,tcpdump 之所以能抓到客戶端的 ACK 包,是因為數據包進入系統的順序是先進入 tcpudmp,后經過 iptables;
- 接著,服務端超時重傳了 SYN、ACK 包,重傳了 5 次后,也就是超過 tcp_synack_retries 的值(默認值是 5),然后就沒有繼續重傳了,此時服務端的 TCP 連接主動中止了,所以剛才處于SYN_RECV 狀態的 TCP 連接斷開了,而客戶端依然處于ESTABLISHED 狀態;
- 雖然服務端 TCP 斷開了,但過了一段時間,發現客戶端依然處于ESTABLISHED 狀態,于是就在客戶端的 telnet 會話輸入了 123456 字符;
- 此時由于服務端已經斷開連接,客戶端發送的數據報文,一直在超時重傳,每一次重傳,RTO 的值是指數增長的,所以持續了好長一段時間,客戶端的 telnet 才報錯退出了,此時共重傳了 15次。
四、TCP快速建立連接
客戶端在向服務端發起 HTTP GET 請求時,一個完整的交互過程,需要 2.5 個 RTT 的時延。由于第三次握手是可以攜帶數據的,這時如果在第三次握手發起 HTTP GET 請求,需要2個RTT的時延。但是在下一次(不是同個 TCP 連接的下一次)發起 HTTP GET 請求時,經歷的 RTT 也是一樣,如下圖:
在 Linux 3.7 內核版本中,提供了 TCP Fast Open 功能,這個功能可以減少 TCP 連接建立的時延。
- 在第一次建立連接的時候,服務端在第二次握手產生一個 Cookie (已加密)并通過 SYN、ACK包一起發給客戶端,于是客戶端就會緩存這個 Cookie ,所以第一次發起 HTTP Get 請求的時候,還是需要 2 個 RTT 的時延;
- 在下次請求的時候,客戶端在 SYN 包帶上 Cookie 發給服務端,就提前可以跳過三次握手的過程,因為 Cookie 中維護了一些信息,服務端可以從 Cookie 獲取 TCP 相關的信息,這時發起的 HTTP GET 請求就只需要 1 個 RTT 的時延;?
注:客戶端在請求并存儲了Fast Open Cookie 之后,可以不斷重復 TCP Fast Open 直至服務器認為Cookie 無效(通常為過期)。
在 Linux 上如何打開 Fast Open 功能?
可以通過設置 net.ipv4.tcp_fastopn 內核參數,來打開 Fast Open 功能。net.ipv4.tcp_fastopn 各個值的意義:
- 0 關閉
- 1 作為客戶端使用 Fast Open 功能
- 2 作為服務端使用 Fast Open 功能
- 3 無論作為客戶端還是服務器,都可以使用 Fast Open 功能
4.1 TCP Fast Open 抓包分析
在下圖,數據包 7 號,客戶端發起了第二次 TCP 連接時,SYN 包會攜帶 Cooike,并且長度為 5 的數據。服務端收到后,校驗 Cooike 合法,于是就回了 SYN、ACK 包,并且確認應答收到了客戶端的數據包,ACK = 5 + 1 = 6。
4.2 TCP 重復確認和快速重傳
當接收方收到亂序數據包時,會發送重復的 ACK,以便告知發送方要重發該數據包,當發送方收到 3個重復 ACK 時,就會觸發快速重傳,立刻重發丟失數據包。
TCP 重復確認和快速重傳的一個案例,用 Wireshark 分析,顯示如下:
- 數據包 1 期望的下一個數據包 Seq 是 1,但是數據包 2 發送的 Seq 卻是 10945,說明收到的是亂序數據包,于是回了數據包 3 ,還是同樣的 Seq = 1,Ack = 1,這表明是重復的 ACK;
- 數據包 4 和 6 依然是亂序的數據包,于是依然回了重復的 ACK;
- 當對方收到三次重復的 ACK 后,于是就快速重傳了 Seq = 1 、Len = 1368 的數據包 8;
- 當收到重傳的數據包后,發現 Seq = 1 是期望的數據包,于是就發送了個確認收到快速重傳的ACK。
注意:快速重傳和重復 ACK 標記信息是 Wireshark 的功能,非數據包本身的信息。
以上案例在 TCP 三次握手時協商開啟了選擇性確認 SACK,因此一旦數據包丟失并收到重復 ACK ,即使在丟失數據包之后還成功接收了其他數據包,也只需要重傳丟失的數據包。如果不啟用 SACK,就必須重傳丟失包之后的每個數據包。如果要支持 SACK ,必須雙方都要支持。在 Linux 下,可以通過 net.ipv4.tcp_sack 參數打開這個功能(Linux 2.4 后默認打開)。
4.3 TCP 流量控制
TCP 為了防止發送方無腦的發送數據,導致接收方緩沖區被填滿,所以就有了滑動窗口的機制,它可利用接收方的接收窗口來控制發送方要發送的數據量,也就是流量控制。
接收窗口是由接收方指定的值,存儲在 TCP 頭部中,它可以告訴發送方自己的 TCP 緩沖空間區大小,這個緩沖區是給應用程序讀取數據的空間:
- 如果應用程序讀取了緩沖區的數據,那么緩沖空間區就會把被讀取的數據移除
- 如果應用程序沒有讀取數據,則數據會一直滯留在緩沖區。
接收窗口的大小,是在 TCP 三次握手中協商好的,后續數據傳輸時,接收方發送確認應答 ACK 報文時,會攜帶當前的接收窗口的大小,以此來告知發送方。
假設接收方接收到數據后,應用層能很快的從緩沖區里讀取數據,那么窗口大小會一直保持不變,過程如下:
但是現實中服務器會出現繁忙的情況,當應用程序讀取速度慢,那么緩存空間會慢慢被占滿,于是為了保證發送方發送的數據不會超過緩沖區大小,服務器則會調整窗口大小的值,接著通過 ACK 報文通知給對方,告知現在的接收窗口大小,從而控制發送方發送的數據大小。
4.4 零窗口通知與窗口探測
假設接收方處理數據的速度跟不上接收數據的速度,緩存就會被占滿,從而導致接收窗口為 0,當發送方接收到零窗口通知時,就會停止發送數據。如下圖,可以看到接收方的窗口大小在不斷的收縮至 0:接著,發送方會定時發送窗口大小探測報文,以便及時知道接收方窗口大小的變化。
以下圖 Wireshark 分析圖作為例子說明:
- 發送方發送了數據包 1 給接收方,接收方收到后,由于緩沖區被占滿,回了個零窗口通知;
- 發送方收到零窗口通知后,就不再發送數據了,直到過了 3.4 秒后,發送了一個 TCP Keep-Alive 報文,也就是窗口大小探測報文;
- 當接收方收到窗口探測報文后,就立馬回一個窗口通知,但是窗口大小還是 0;
- 發送方發現窗口還是 0,于是繼續等待了 6.8 (翻倍) 秒后,又發送了窗口探測報文,接收方依然還是回了窗口為 0 的通知;
- 發送方發現窗口還是 0,于是繼續等待了 13.5 (翻倍) 秒后,又發送了窗口探測報文,接收方依然還是回了窗口為 0 的通知;?
可以發現,這些窗口探測報文以 3.4s、6.5s、13.5s 的間隔出現,說明超時時間會翻倍遞增。
4.5 TCP 延遲確認與 Nagle 算法
當我們 TCP 報文的承載的數據非常小的時候,例如幾個字節,那么整個網絡的效率是很低的,因為每個 TCP 報文中都會有 20 個字節的 TCP 頭部,也會有 20 個字節的 IP 頭部,而數據只有幾個字節,所以在整個報文中有效數據占有的比重就會非常低。
這就好像快遞員開著大貨車送一個小包裹一樣浪費。那么就出現了常見的兩種策略,來減少小報文的傳輸,分別是:
- Nagle 算法
- 延遲確認
4.6 Nagle 算法是如何避免大量 TCP 小數據報文的傳輸?
Nagle 算法做了一些策略來避免過多的小數據報文發送,這可提高傳輸效率。Nagle 算法的策略:
- 沒有已發送未確認報文時,立刻發送數據。
- 存在未確認報文時,直到「沒有已發送未確認報文」或「數據長度達到 MSS 大小」時,再發送數據。
只要沒滿足上面條件中的一條,發送方一直在囤積數據,直到滿足上面的發送條件。
- 一開始由于沒有已發送未確認的報文,所以就立刻發了 H 字符;
- 接著,在還沒收到對 H 字符的確認報文時,發送方就一直在囤積數據,直到收到了確認報文后,
- 此時沒有已發送未確認的報文,于是就把囤積后的 ELL 字符一起發給了接收方;
- 待收到對 ELL 字符的確認報文后,于是把最后一個 O 字符發送了出去
可以看出,Nagle 算法一定會有一個小報文,也就是在最開始的時候。另外,Nagle 算法默認是打開的,如果對于一些需要小數據包交互的場景的程序,比如,telnet 或 ssh這樣的交互性比較強的程序,則需要關閉 Nagle 算法。可以在 Socket 設置 TCP_NODELAY 選項來關閉這個算法(關閉 Nagle 算法沒有全局參數,需要根據每個應用自己的特點來關閉)。
4.7 那延遲確認又是什么?
事實上當沒有攜帶數據的 ACK,它的網絡效率也是很低的,因為它也有 40 個字節的 IP 頭 和 TCP
頭,但卻沒有攜帶數據報文。為了解決 ACK 傳輸效率低問題,所以就衍生出了 TCP 延遲確認。
TCP 延遲確認的策略:
- 當有響應數據要發送時,ACK 會隨著響應數據一起立刻發送給對方
- 當沒有響應數據要發送時,ACK 將會延遲一段時間,以等待是否有響應數據可以一起發送
- 如果在延遲等待發送 ACK 期間,對方的第二個數據報文又到達了,這時就會立刻發送 ACK
延遲等待的時間是在 Linux 內核中定義:關鍵就需要 HZ 這個數值大小,HZ 是跟系統的時鐘頻率有關,每個操作系統都不一樣。
延遲確認 和 Nagle 算法混合使用時,會產生新的問題?
當 TCP 延遲確認 和 Nagle 算法混合使用時,會導致時耗增長,如下圖:
發送方使用了 Nagle 算法,接收方使用了 TCP 延遲確認會發生如下的過程:
- 發送方先發出一個小報文,接收方收到后,由于延遲確認機制,自己又沒有要發送的數據,只能干等著發送方的下一個報文到達;
- 而發送方由于 Nagle 算法機制,在未收到第一個報文的確認前,是不會發送后續的數據;
- 所以接收方只能等待最大時間 200 ms 后,才回 ACK 報文,發送方收到第一個報文的確認報文后,也才可以發送后續的數據。
很明顯,這兩個同時使用會造成額外的時延,這就會使得網絡"很慢"的感覺。要解決這個問題,只有兩個辦法:
- 要不發送方關閉 Nagle 算法
- 要不接收方關閉 TCP 延遲確認
博文參考
1.5 萬字 + 40 張圖解 HTTP 常見面試題(值得收藏)_小林coding的博客-CSDN博客
總結
以上是生活随笔為你收集整理的计算机网络——网络抓包实战(Tcpdump/Wireshark)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈IPv6地址
- 下一篇: 微信公众平台开发教程第20篇-新手解惑4