轉載自: https://mp.weixin.qq.com/s/an373dQGF68zRhXb4qTAXg
運維中有個詞叫根故障定位,實際生產中很多異常的根故障最后都會定位到網絡這一層。下面以Broken Pipe為例,簡述一下如何進行網絡問題的定位。
1. 定位思路
第一眼看異常棧直譯過來就是管道破裂,為什么會出現管道破裂呢?這篇文章就由此而來,背后牽涉的是我們常常掛在嘴邊,面試也經常被問的http 和 tcp 協議的知識,這里埋個伏筆,后面我們由淺入深,慢慢把這個問題解決,同時了解如何運用網絡知識解決實際網絡問題。
2. 開始抓包
遇到網絡問題,自然而然會想到使用tcpdump進行抓包,看看傳輸過程到底發生了什么。抓包命令如下:
tcpdump tcp port 20004 and host **.**.com -w brokenpipe.cap
這條命令監聽20004
端口,把所有的主機和域名都抓下來,保存到brokenpipe.cap
文件中。
常用的tcpdump命令
tcpdump tcp port 端口號 and host 域名 -w 保存文件
3. 在Wireshark數據包中看三次握手
如果沒有用過Wireshark 做過網絡包分析的,擔心直接上來就看包分析有點費勁,下面會對wireshark 先做個簡單的介紹。
3.1 Wireshark 簡介
首先,把抓下來的包文件存在本地, 打開wireshark 導入網絡包,開始有趣的網絡漫游之旅。
wireshark 是一款非常流行的網絡包分析工具,經常是網絡工程師/后端工程師用來分析網絡包,解決網絡問題的利器。先放一張圖出來聞聞味:
截了一張自己本機裝的 wireshark 軟件,分為四個部分:
- 頂部是菜單欄
- 淺綠色背景是過濾器,這個對于分析包很重要,后面會講
- 列表那一欄是包列表信息
- 最下面的這個是包詳細信息,包含各層協議的內容(應用層、傳輸層、網絡層、數據鏈路層、物理層)
[圖片上傳失敗...(image-1cde73-1591322222101)]
3.2 Wireshark 過濾器
過濾器單獨拿出來說下,因為確實很有用,后面異常分析會用到。
你導入的包可能內容很多,需要使用過濾器篩選一下,過濾器很多種過濾的規則,我列舉一下常用的幾種:
- 協議過濾
如果抓的包有很多種協議類型,可以輸入 tcp 回車只看tcp 協議的包
- IP 過濾
例如 ip.src == 192.168.1.23 (過濾發起地址ip)ip.dst == 12.8.0.1(過濾目標地址ip) ip == 12.0.0.1(過濾源或目的地址)
- 端口過濾
tcp.port == 4980 , 還可以 tcp.port == 4542 or tcp.port == 4528 加入表達式 and、or、in 等等
例如:tcp.port in {80 443 8080}
- HTTP 模式過濾
http.request.method == "GET" 或者 http.request.method == "POST"
- 報文內容過濾
tcp.segment_data contains "202005190001" 過濾tcp 報文內容包括 202005190001 的報文
- 更多wireshark 過濾器可以參考:wireshark-filter
wireshark 的詳細使用教程不是今天的重點,就介紹這二部分,后面分析數據包時會穿插著講,覺得大家有興趣可以自己抽空玩一玩這個軟件。
3.3 三次握手初探
寫的三次握手初探 這部分如果看不懂沒關系,這里是為了介紹Wireshark寫的三次握手,后面會詳細解釋,詳細到直接從網絡協議分層開始講起,如果你這看不懂可以Diss 。 [圖片上傳失敗...(image-85572e-1591322222101)]
如果第一次看Wireshark 網絡包,會一臉懵逼,看多了就會越看越喜歡。重點看框出來的,前三行就是三次握手的過程:
- 上圖第一行,客戶端向服務端發送SYN 數據包,數據長度len 為0,Seq(隨機生成包序列號)為2421858999;
- 上圖第二行,服務端向客戶端回應ACK 數據包,并且發送SYN 數據包,合并一起就是SYN + ACK 數據包,數據長度len 為0,Seq(隨機生成包序列號)為1988635269,ack為2421859000 = 第一次握手Seq(2421858999)+1;
- 上圖第三行,客戶端回應客戶端的SYN 數據包,發送ACK 確認數據包,Seq 為 二次握手的ack(2421859000),ack為 1988635270= 二次握手的seq(1988635269)+1;
4. 回顧網絡協議分層、三次握手、四次揮手等網絡基礎知識
4.1 網絡協議分層
在解決文章開頭的異常,分析數據包之前,我們需要一些預備知識,需要一丟丟基礎的網絡知識。
首先在直接看Wireshark 的包信息之前,需要來回顧一下計算機網絡的知識,大家知道目前主流使用的TCP/IP 五層協議,而不是國際標準化組織(ISO)出的OSI(Open System Interconnection)七層協議。TCP/IP協議棧如下圖所示: [圖片上傳失敗...(image-b176c-1591322222101)]
我們可以看到Wireshark 包詳情就是TCP/IP 五層的信息,對比上面的圖從下往上看(取每個英文單詞首字母就是協議簡稱,例如 HTTP:Hypertext Transfer Protocol ),如下:
[圖片上傳失敗...(image-994d49-1591322222101)]
后面我們看 Wireshark 數據報文時,主要看TCP 所在的傳輸層報文。
4.2 三次握手
首先我們先看下TCP 報文的報文格式: [圖片上傳失敗...(image-cea1fa-1591322222101)]
下面把TCP 報文的各個部分做了詳細說明,分析網絡問題不用全看,把加重的部分關注一下就可以了。好學的玩家可以把所有的都看了,不用記,有個概念就可以了。
- 源端口號和目的端口號:各占2個字節(16位),分別寫入源端口和目的端口;
- 序號:4字節(32位),TCP連接中字節流每個字節都按順序編號,這個序號用于標識這個報文段。例如:一段報文序號seq 是201,而報文數據長度為100,下一個報文段的數據序號應該為301(201+100)。
- 確認號 :4字節(32位),期望收到對方下一個報文的序號。這個確認號是和序號seq 有點關系的,不要和ACK(狀態標志位)混淆了。
- 首部長度:4位,表示報文數據距離報文起始位置的長度。保留:保留今后可以會用到。
- 數據報狀態標志位(非常重要),分為以下6種,二進制1 位表示一種(1代表開啟 0 關閉)
- URG:URG=1 代表報文有緊急數據
- ACK:ACK = 1,確認位,TCP中連接建立后,所有報文的ACK 位置都為1;
- PSH: 發送端和接收端都有緩沖區(發送端:寫緩沖區 接收端:讀緩沖區) 對于發送端:帶PSH=1,報文會立即從緩沖區報文推送給服務端 對于服務端:服務端立即將讀緩沖區內容推給進程。
- RST:RST=1,代表連接出現嚴重錯誤,TCP連接的一方將連接重置了,必須釋放連接,重新建立連接;
- SYN:同步SYN,在連接建立時用來同步序號。三次握手時會用到,當SYN=1,ACK =0,表明是發起方請求建立連接,服務方同意建立連接,響應報文SYN=1,ACK =1,前者表明同步連接,后者是確認報文。
- FIN:用來釋放連接。當FIN =1,表明此報文的發送方的數據已經發送完畢,并且要求釋放。
- 窗口:占2字節,通常用于告知對方自己的能夠接受的數據量大小。窗口本質就是一個緩沖區buffer,該字段的值用于告知對方自己剩余的可用緩沖區大小。
- 校驗和:奇偶校驗,此校驗和是對整個的 TCP 報文段,包括 TCP 頭部和 TCP 數據,以 16 位字進行計算所得。由發送端計算和存儲,并由接收端進行驗證。
- 緊急指針:只有當 URG 標志置 1 時緊急指針才有效。緊急指針是一個正的偏移量,和順序號字段中的值相加表示緊急數據最后一個字節的序號。
- 選項:可選的。最常見的可選字段是最長報文大小,又稱MSS(Maxinum Segment Size), 每個連接方通常在通信的第一個報文段(連接建立的SYN標志位為1的數據報文)設置這個選項,表示本端能接受的最大報文段的長度。因為長度不一定是32的整數倍,因此要加額外的0作為填充。
- 數據部分:可選的。連接建立和終止時,報文段只有TCP首部。
我們先回顧一下以前計算機網絡課堂上學過的TCP傳輸的三次握手流程: [圖片上傳失敗...(image-aac3c1-1591322222101)]
[圖片上傳失敗...(image-60f60b-1591322222101)]
三次握手的具體過程如下:
服務端(接收方)進程啟動,準備接收客戶端進程的連接請求,此時接收方進入LISTEN(監聽)模式;
-
三次握手第一步:客戶端向服務端發出連接請求報文,這時報文首部SYN 標志位為1,同時設置一個初始序列號seq = x(隨機數); 做完這步動作,發送方進入SYN_SENT (同步已發送狀態) 。
名稱解釋:SYN:同步標志位 seq:包序列編號(每個包都有一個序列號) 第一次握手客戶端發送的報文稱為同步請求報文,希望與服務端建立同步連接,SYN報文不攜帶數據。
-
三次握手第二步:服務端收到來自客戶端的連接請求報文后,需要確認收貨,響應報文中ACK(確認標志位)設置為1,將確認號ack 設置為第一步的請求序列號seq 加1(ack =x+1),另外自己也回客戶端一個SYN包(可以建立同步連接),即SYN + ACK包,包序列號seq = y,服務端進入SYN_RCVD(同步收到)狀態。
名詞解釋:ACK:確認狀態位(這里ACK=1),這個一定和ack(32位確認序號,這里ack=x+1)區分開,可以看下面的TCP 報文結構體圖,ACK是包的狀態標志,ack是確認序號。
三次握手第三步:客戶端收到來自服務端的 SYN + ACK 包,會發送一個ACK 確認包,ACK =1,seq = x+1( 第二步的ack),ack = y+1(第二步的seq+1)。
4.3 四次揮手
四次揮手的狀態圖如下所示: [圖片上傳失敗...(image-1e7837-1591322222101)]
四次揮手wireshark 包信息如下,可以對照著上圖看 [圖片上傳失敗...(image-a50345-1591322222101)]
四次揮手的具體過程如下:
客戶端發送FIN 釋放連接報文,表示結束連接,報文seq = u(等于前面已經傳送過來的數據的最后一個字節的序號加1),此時,客戶端進入FIN_WAIT1(終止等待1)狀態。
服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,并且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE_WAIT(關閉等待)狀態。TCP接收方通知上層的應用進程,客戶端向服務器方向的發送通道關閉了,這時候處于半關閉狀態,即客戶端已經沒有數據要發送了(已經發了FIN結束信號),但是服務器若發送數據,客戶端依然要接受。這個狀態要持續一段時間,也就是整個CLOSE_WAIT狀態持續的時間。
客戶端收到服務器的確認請求后,此時,客戶端就進入FIN_WAIT2(終止等待2)狀態,等待服務器發送連接釋放報文(在服務端Close_Wiat期間還可以接受服務器發送的最后的數據)。
服務端發送完最后的數據,向客戶端發送FIN 連接釋放報文,ACK =1,由于在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號為seq=w,ack 和回復ACK報文一致,ack = u+1, 此時,服務器就進入了LAST_ACK(最后確認)狀態,等待客戶端的確認。
客戶端收到服務器的連接釋放報文后,必須發出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME_WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,必須經過2 個MSL(最長報文段壽命)的時間后,當客戶端撤銷相應的TCB后,才進入CLOSED狀態。
服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB后,就結束了這次的TCP連接。