文章目錄
- 前言
- 一、前置準備
- 二、三報文握手過程抓包
- 2.1、第一次握手
- 2.2、第二次握手
- 2.3、第三次握手
- 三、通信過程抓包
- 3.1、報文 44379 – 客戶端發數據(PSH, ACK)
- 3.2、 報文 44380 – 服務端確認收到數據(ACK)
- 3.3、報文 44469 – 服務端回顯(PSH, ACK)
- 3.4、報文 44470 – 客戶端確認收到回顯(ACK)
- 四、四報文握手過程抓包
- 4.1、第一次揮手
- 4.2、第二次揮手
- 4.3、第三次揮手
- 4.4、第四次揮手
前言
??Wireshark
是一款功能強大的網絡抓包和協議分析工具,用于實時捕獲和解析網絡通信數據,幫助用戶診斷網絡問題、分析協議細節或進行安全審計。
??本篇將用Wireshark
工具對TCP通信案例進行抓包,分析三報文握手
和四報文揮手
。
一、前置準備
??服務端案例工程:
public class BIOServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(1848);System.out.println("EchoServer started on port 1848");//阻塞Socket clientSocket = serverSocket.accept();handleClient(clientSocket);}private static void handleClient(Socket clientSocket) {try(OutputStream out = clientSocket.getOutputStream();InputStream in = clientSocket.getInputStream()){byte[] bytes = new byte[1024];int len;while ((len = in.read(bytes))!= -1){out.write(bytes, 0, len);out.flush(); // 回顯}}catch (Exception e){System.out.println("Client disconnected");}}
}
??客戶端案例工程:
public class BIOClient {public static void main(String[] args) throws IOException {Socket socket = new Socket("localhost", 1848);OutputStream out = socket.getOutputStream();InputStream in = socket.getInputStream();out.write(("Hello Server:" + Thread.currentThread().getName()).getBytes());out.flush();byte[] buffer = new byte[1024];int len = in.read(buffer);System.out.println("Server Echo: " + new String(buffer, 0, len));socket.close();}
}
??Wireshark
工具設置,選擇Adapter for loopback traffic capture
??設置抓包過濾選項,為案例中的端口號1848
:
二、三報文握手過程抓包
??案例工程中,三報文握手在java層面體現在客戶端
的這一行代碼上,創建 Socket 時,會自動連接服務端并完成三次握手;
Socket socket = new Socket("localhost", 1848);
??服務端
阻塞在 accept(),等待連接;一旦返回,說明三次握手完成;
Socket clientSocket = serverSocket.accept();
??先啟動服務端,再啟動客戶端:
進入斷點,準備建立連接
??釋放客戶端的斷點,進入服務端的處理消息的邏輯:
??此時三報文握手的過程已經結束了,觀察Wireshark
2.1、第一次握手
??第一次握手的報文10358 371.405938 127.0.0.1 127.0.0.1 TCP 56 49931 → 1848 [SYN] Seq=0 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM
解釋:
字段 | 含義 |
---|---|
10358 | 報文編號,在這次抓包中的編號 |
371.405938 | 抓包開始后的時間戳(秒) |
127.0.0.1 → 127.0.0.1 | 源 IP 和目的 IP(都是 localhost) |
TCP | 協議類型 |
56 | 報文字節數(含 TCP 首部) |
49931 → 1848 | 源端口(49931,客戶端臨時端口)到目標端口(1848,服務端監聽端口) |
[SYN] | 報文類型標志(發起連接請求) |
Seq=0 | 初始序列號,客戶端初始發送序號 |
Win=65535 | 窗口大小(接收緩沖區大小) |
Len=0 | 數據長度(SYN 報文本身不帶數據) |
MSS=65495 | 最大報文段長度(Maximum Segment Size)是對方發送給我的最大 TCP 有效載荷,協商用 |
WS=256 | 窗口擴大因子(Window Scale),用于擴大窗口大小(65535×256) |
SACK_PERM | 表示支持選擇確認(Selective Acknowledgement),提高網絡性能 |
2.2、第二次握手
??第二次握手的報文10359 371.405972 127.0.0.1 127.0.0.1 TCP 56 1848 → 49931 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM
的解釋:
字段 | 含義 |
---|---|
10359 | 報文編號 |
371.405972 | 時間戳 |
1848 → 49931 | 服務端端口(1848)回應客戶端臨時端口(49931) |
[SYN, ACK] | 服務端回應連接請求,并確認客戶端的 SYN 報文 |
Seq=0 | 服務端自己的初始序列號為 0 |
Ack=1 | 確認號 = 客戶端 Seq(0)+ 1,表示收到了客戶端的 SYN,對于第一次握手中的seq的確認 |
Win=65535 | 服務端窗口大小 |
Len=0 | 無負載數據,僅握手信息 |
MSS=65495 | 服務端協商的最大段大小 |
WS=256 | 服務端窗口擴展因子 |
SACK_PERM | 服務端也支持 SACK |
2.3、第三次握手
??第三次握手的報文10360 371.405991 127.0.0.1 127.0.0.1 TCP 44 49931 → 1848 [ACK] Seq=1 Ack=1 Win=327424 Len=0
的含義:
字段 | 含義 |
---|---|
49931 → 1848 | 客戶端端口 → 服務端端口 |
[ACK] | 確認包,確認服務端的 [SYN, ACK] |
Seq=1 | 客戶端的下一個發送序列號(上次 SYN 是 Seq=0,這次是 1) |
Ack=1 | 確認號:表示已收到服務端 Seq=0 的 [SYN] 報文(對于服務端seq的確認) |
Win=327424 | 窗口大小(注意,這里乘了窗口縮放因子) |
Len=0 | 僅為控制報文,無負載數據 |
??針對原理圖,理論聯系實際,從上面的握手報文可以看出,除了最關鍵的SYN,ACK,seq,ack以外,;還體現出了對于TCP參數的協商,例如最大段大小,窗口大小,數據長度等。
三、通信過程抓包
??在案例工程中,通信過程體現在客戶端發送:
out.write(("Hello Server:" + Thread.currentThread().getName()).getBytes());
??服務端接收并回顯:
while ((len = in.read(bytes))!= -1){out.write(bytes, 0, len); // echo 回顯out.flush();
}
報文號 | 時間戳 | 源 → 目標 | 標志位 | Seq/Ack | Len | 說明 |
---|---|---|---|---|---|---|
44379 | 1279.762655 | 49931 → 1848 | [PSH, ACK] | Seq=1 Ack=1 | 17 | 客戶端發送“Hello Server…” |
44380 | 1279.762682 | 1848 → 49931 | [ACK] | Seq=1 Ack=18 | 0 | 服務端確認收到了數據 |
44469 | 1283.136551 | 1848 → 49931 | [PSH, ACK] | Seq=1 Ack=18 | 17 | 服務端回顯“Hello Server…” |
44470 | 1283.136566 | 49931 → 1848 | [ACK] | Seq=18 Ack=18 | 0 | 客戶端確認收到了回顯 |
3.1、報文 44379 – 客戶端發數據(PSH, ACK)
??49931 → 1848 [PSH, ACK] Seq=1 Ack=1 Len=17
:
- 客戶端發送數據(17 字節),“Hello Server:main”;
- PSH(Push)表示告訴 TCP 層立即發送,無需等待緩沖區滿;
- Seq=1 是三次握手后起始序列號,這里是重點,因為客戶端的第三次握手,報文段沒有攜帶數據,不占用seq,所以這次的seq還是從1開始。
- Ack=1 表示仍然確認了服務端的 SYN。
3.2、 報文 44380 – 服務端確認收到數據(ACK)
??1848 → 49931 [ACK] Seq=1 Ack=18 Len=0
- 服務端發送確認 ACK,表示已收到客戶端發送的 17 字節(1+17=18);
- 這是純 ACK,不帶數據。
3.3、報文 44469 – 服務端回顯(PSH, ACK)
??1848 → 49931 [PSH, ACK] Seq=1 Ack=18 Len=17
- 服務端將收到的數據原樣回顯;
- PSH 表示回顯數據立即發送;
- Seq=1 是服務端自己的起始序列號;
- Ack=18 表示還在確認客戶端數據;
3.4、報文 44470 – 客戶端確認收到回顯(ACK)
??49931 → 1848 [ACK] Seq=18 Ack=18 Len=0
- 客戶端收到回顯后發出確認 ACK;
- Seq=18 表示客戶端準備下次發送的序號(之前發了 17 字節);
- Ack=18 表示收到了服務端的 17 字節(1+17=18)。
四、四報文握手過程抓包
??案例工程中,四報文揮手在java層面體現在客戶端
的這一行代碼上,通信完成關閉連接時,會通知服務端斷開連接,進行四報文揮手的流程;
socket.close();
??執行斷點:
三報文握手和四報文揮手,都是客戶端
發起的
報文號 | 時間戳 | 方向 | 標志位 | Len | 說明 |
---|---|---|---|---|---|
46892 | 1380.873591 | 49931 → 1848 | [FIN, ACK] | 0 | 客戶端主動斷開連接 |
46893 | 1380.873609 | 1848 → 49931 | [ACK] | 0 | 服務端確認客戶端 FIN |
46902 | 1380.874091 | 1848 → 49931 | [FIN, ACK] | 0 | 服務端也準備斷開連接 |
46903 | 1380.874114 | 49931 → 1848 | [ACK] | 0 | 客戶端確認服務端 FIN,連接徹底關閉 |
4.1、第一次揮手
??第一次揮手報文段46892 1380.873591 127.0.0.1 127.0.0.1 TCP 44 49931 → 1848 [FIN, ACK] Seq=18 Ack=18 Win=327424 Len=0
的解釋:
字段 | 含義 |
---|---|
46892 | 報文編號 |
1380.873591 | 抓包開始以來的時間戳(秒) |
127.0.0.1 → 127.0.0.1 | 客戶端 → 服務端 |
TCP | 協議類型 |
44 | 報文長度(含 TCP 頭部) |
49931 → 1848 | 客戶端臨時端口 → 服務端端口 |
[FIN, ACK] | 標志位:客戶端請求關閉連接,同時確認服務端數據 |
Seq=18 | 序列號,表示上一次客戶端發送的數據是以 Seq=17 結尾的(如 “Hello Server” 17 字節),FIN 占用 1 |
Ack=18 | 確認號:客戶端已收到服務端 Seq=0~17 的數據 |
Win=327424 | 客戶端窗口大小 |
Len=0 | 無數據,僅控制報文 |
4.2、第二次揮手
??第二次揮手報文段46893 1380.873609 127.0.0.1 127.0.0.1 TCP 44 1848 → 49931 [ACK] Seq=18 Ack=19 Win=2161152 Len=0
的解釋:
字段 | 含義 |
---|---|
46893 | 報文編號 |
1380.873609 | 時間戳 |
1848 → 49931 | 服務端端口 → 客戶端端口 |
[ACK] | 服務端確認客戶端關閉請求 |
Seq=18 | 服務端數據已經發到 Seq=17,FIN 尚未發送 |
Ack=19 | 確認號:確認客戶端 FIN,占用一個序號(Seq=18 → Ack=19) |
Win=2161152 | 服務端窗口大小 |
Len=0 | 僅 ACK,沒有數據 |
4.3、第三次揮手
??第三次揮手報文段46902 1380.874091 127.0.0.1 127.0.0.1 TCP 44 1848 → 49931 [FIN, ACK] Seq=18 Ack=19 Win=2161152 Len=0
的解釋:
字段 | 含義 |
---|---|
46902 | 報文編號 |
1380.874091 | 時間戳 |
1848 → 49931 | 服務端 → 客戶端 |
[FIN, ACK] | 服務端請求關閉,同時 ACK |
Seq=18 | 服務端發出 FIN,占用序號 18(和第二次揮手一致,因為過程中沒有發送其他數據) |
Ack=19 | 繼續確認客戶端的 FIN |
Win=2161152 | 服務端窗口大小 |
Len=0 | 無數據,僅控制位 |
4.4、第四次揮手
??第四次揮手報文段46903 1380.874114 127.0.0.1 127.0.0.1 TCP 44 49931 → 1848 [ACK] Seq=19 Ack=19 Win=327424 Len=0
的解釋:
字段 | 含義 |
---|---|
46903 | 報文編號 |
1380.874114 | 時間戳 |
49931 → 1848 | 客戶端 → 服務端 |
[ACK] | 客戶端確認服務端的關閉 |
Seq=19 | 客戶端上一次是 Seq=18,FIN 占 1,+1 到 19 |
Ack=19 | 表示已經收到服務端的 FIN(Seq=18) |
Win=327424 | 客戶端窗口大小 |
Len=0 | 無數據 |