一、半開連接
從協議定義的角度來說,TCP的半開連接是指TCP連接的一端異常崩潰,或者在未通知對端的情況下關閉連接,這種情況下不可以正常收發數據,否則會產生RST(后面內容我們在介紹RST)。比如一個常見的情況是TCP連接的一端異常斷電,就會導致TCP的半開連接。如果沒有數據傳輸,對端就不會知道本端的異常而一直處於ESTABLISHED狀態(TCP有存活檢測機制,后面內容我們會進行介紹)。
另外從linux實現的角度來說,因為linux內部有個半連接隊列,TCP半開連接是指發送了TCP連接請求,等待對方應答的狀態,此時連接並沒有完全建立起來,雙方還無法進行通信交互的狀態,此時就稱為半連接。由於一個完整的TCP連接需要經過三次握手才能完成,這里把三次握手之前的連接都稱之為半連接。
二、半關連接
TCP的半關連接是指TCP連接只有一方發送了FIN,另一方沒有發出FIN包,仍然可以在一個方向上正常發送數據。這種場景並不常見,一般來說Berkeley sockets API調用shutdown()接口時候就會進入半關閉狀態(調用常規的close()一般是期待完整的雙向關閉這個TCP連接),shutdown()接口相當指示程序,本端已經沒有數據待發送,所以我發送一個FIN到對端,但是我仍然想要從對端接收數據,直到對端發送一個FIN指示關閉連接為止。如下圖所示,在紅色背景文本框標注的數據傳輸場景下就是TCP的半關連接
三、wireshrk抓包示例
首先注意半開連接是不能正常傳輸數據的,而半關連接是可以在其中的一個方向上傳輸數據的。下面簡單附一下wireshark的抓包圖示,限於篇幅僅作簡要介紹,詳細請參考對應的wireshark抓包文件
1.TCP半開
通過單台筆記本的雙無線網卡測試tcp連接的半開,步驟如下
server綁定網卡A的地址
client綁定網卡B的地址並連接server 對應截圖中的No 1--No 3包
client發送"hello"消息 對應截圖中的No 4包
server正常接收到后"hello"消息后 拔掉網卡A
kill掉server進程 使server的FIN消息不能發送到client
插上網卡A 注意在路由器中綁定IP地址和MAC地址,使得網卡A的地址和之前是一致的,此時client和server即處於半開連接狀態
client向server發送"world"消息 對應截圖中的No 6包
server回復rst消息
2.TCP半關
正常建立連接后,client首先發送"hello"消息給server,然后server發送FIN給client,關閉了server到client方向的數據傳輸,但是client仍然可以向server傳輸數據,此時client和server之間的連接即處於半關連接的狀態,接下來client向server發送"world"消息,然后發送FIN給server關閉client到server方向的數據傳輸。
補充說明:
1.目前linux最新的實現已經沒有半連接隊列了,連接請求的pseudo sock已經插入ehash(e代表establish,ehash即已連接hash隊列)中了 詳情https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/net/ipv4/?id=079096f103faca2dd87342cca6f23d4b34da8871&context=3&ignorews=0&dt=1