咱們在介紹TCP頭的時候,提到過其中有個RST標誌位。當一個TCP報文中這個標誌位打開的時候,咱們叫作reset包(嚴格的說應該叫作reset段,可是不少時候段包幀並不加以區分)或者簡單稱呼爲reset、RST。一般reset的產生是因爲一個異常包致使,reset通常會致使TCP鏈接的快速斷開。產生reset的幾種常見的情形以下linux
向一個未打開的端口發送鏈接請求緩存
應用程序主動終止一個鏈接sass
應用程序尚未接收緩存中的數據,鏈接被提早關閉socket
TWA(TIME-WAIT Assassination)
tcp
半開鏈接的狀況下發送數據spa
注意咱們這裏是描述的reset產生的一些場景(並且是部分場景),在具體reset的產生緣由上可能會有重複,好比第一、四、5均可以認爲是對端沒有打開相對應的TCP端口。3d
1、向一個未打開的端口發送鏈接請求orm
reset產生的一個常見的緣由是,在一個未打開的端口發送鏈接請求。這裏未打開的端口是指沒有應用程序在監聽這個端口等待鏈接。server
以下圖wireshark抓包,再不開啓server狀況下,沒有應用程序監聽9877端口,當client鏈接9877端口的時候就會產生reset消息。注意我截圖中的系列號,在這個截圖中我設置了wireshark顯示絕對系列號(前面文章截圖顯示的都是相對系列號),由於SYN包中的ACK標誌沒有置位,ack number字段無效(實際是以0填充的),因此在reset中的seq=0,ack則爲SYN包中的seq+1(由於SYN標誌和FIN標誌在邏輯上佔1byte)。一個reset包若是要被TCP endpoint接收,ACK標誌必須置位且ack number落在有效的窗口區間內(窗口相關知識後面細講)。這幫助阻止了一個簡單的reset攻擊(RFC5961),不然攻擊者能夠經過僞造源IP地址、源端口號構造一個reset消息來中斷TCP鏈接。blog
2、應用程序主動經過reset消息終止一個鏈接
應用程序能夠經過socket API接口設置主動reset一個TCP鏈接,這種狀況下的鏈接中斷過程稱爲abortive release。以前咱們介紹過的四次揮手之類的經過FIN終止鏈接的過程咱們稱呼爲orderly release。在abortive release狀況下,reset包的發送端緩存的待發送數據都會直接丟棄,接收端接收到reset後也能夠知道是對端主動abort了這個tcp鏈接。
wireshark截圖以下,注意client發送reset後,並不會引發server迴應ACK之類的消息。
能夠看到RST消息的seq正好是對端指望接收的seq,按照RFC5961要求,非SYN_SENT狀態下,接收端須要判斷RST消息的系列號seq爲正好爲本身指望接收的seq,纔會認爲這個RST消息有效。若是RST的系列號seq落在接收窗口內但不是指望的seq時候,接收端須要發送challenge ACK,若是落在接收窗口外則會直接丟棄這個RST消息。(實際上challenge ACK也會引發RST,詳見RFC5961,此處不作介紹。)
3、應用層尚未讀取完接收緩存中的數據,鏈接被提早關閉
當TCP接收端緩存中還有緩存數據而沒有被應用層接收,可是應用層直接關閉TCP鏈接時候就會產生reset,以下圖wireshark抓包所示,鏈接創建後client向server發送10bytes的數據,server應用層並無讀取這10bytes的數據而是直接關閉tcp鏈接就會產生reset。
4、TWA(TIME-WAIT Assassination)
TCP在TIME-WAIT狀態下的時候,若是接收到reset包,它可能會提早結束TIME-WAIT狀態,這種行爲即叫作TIME-WAIT Assassination(TWA),數據包的流程以下圖所示
爲了不TIME-WAIT狀態提早被reset結束,一些系統的TCP實如今TIME-WAIT狀態下不會響應rst消息。linux則能夠經過net.ipv4.tcp_rfc1337設置TCP在TIME-WAIT下是否響應reset,若是設置tcp_rfc1337爲0,在TIME-WAIT下若是接收到reset則會直接關閉tcp鏈接,而不會等到2MSL超時。
5、半開鏈接的狀況下發送數據
關於半開鏈接咱們前面已經進行了介紹,同時wireshark抓包中也有對應的reset的展現,此處再也不介紹。