0. 安裝Wireshark,但是默認情況下,Wireshark無法捕獲127.0.0.1的報文
解決方案:安裝npcap,替換默認的winpacp,重新啟動Wireshark,就可以看到一個名字中含有Loopback的接口,針對它來抓包就行了
1. 用telnet向未被監聽的9090端口發送連接請求
telnet窗口一閃而過,抓包結果如下

可以看出,未被監聽的端口會直接返回RST包,導致Telnet無法正常建立連接。
2. 開啟echo server監聽9090端口,然后使用telent向9090端口發送連接請求

可以清楚的看到TCP的三次握手過程,現在Telnet已經與echo server建立了TCP連接了
3. 在第二步的基礎上,使用telenet向echo server發送數據
我在Telnet里輸入了abc三個字符,下圖是抓包結果

可以看到一共有12個包,每四個包為一組,對應於Telnet中的一次輸入
這四個包分別對應於 客戶端發送字符->服務器ack->服務器發送echo->客戶端ack
4. 在BIO模式的echo server中插入斷點
使用BIO模式的echo server,但是在調用ServerSocket.accept()方法處下斷點,讓程序跑到此處停頓。
然后啟動Telnet向服務器發送數據,結果如下:

可以看到在這種情況下,居然可以完成三次握手協議,正常建立連接(但此時服務端正卡在accept()方法上)
對於客戶端后續發送的數據,服務端也能及時發送ack,只是無法發送echo罷了。一旦放開echo server的斷點,服務器就能正常工作了,前面沒收到echo的數據也不會丟失。
5. 在NIO模式的echo server中插入斷點
與BIO模式相近,ServerSocketChannel只要與端口綁定,對於Telnet的連接請求,無需服務端調用accept方法就能完成三次握手。
客戶端后續發送的數據,服務端也能正常返回ack。
6. 為什么只需要完成監聽端口,無需調用accept方法就能完成三次握手呢?
我之前的想法是:accept方法在收到客戶端發過來的syn包后就會從阻塞狀態中退出,在此同時返回ack+syn包,完成三次握手(這個想法太奇怪了)
實際上是tcp底層維護了兩個隊列:半連接隊列與全連接隊列。操作系統收到Telnet的tcp連接請求后會自動完成三次握手建立TCP連接,然后將這個連接放到全連接隊列中。而accept方法則是將TCP連接從這個全連接隊列中提取出來罷了。
簡單測試一下,在創建ServerSocket對象的時候,將backlog設置為1,然后將斷點設置在accept方法處(不讓程序從TCP全連接隊列中提取連接)。然后開啟兩個Telnet客戶端,結果第一個Telnet可以正常連接,第二個Telnet直接閃退了。
抓包如下圖所示:

可以清楚的看到從6078到9090的TCP連接成功的完成了三次握手,從6079到9090的TCP連接則握手失敗了。
參考資料:
