1. 定義
IP協定
IP 協定實際上是用來查詢地址的,而它對應的層級也是網路層,也可以稱之爲網際互聯層,區別不大。
TCP協定
TCP 協定是用來規範傳輸規則的,和IP 協定是不同的,而它對應的層級是傳輸層,也就是IP去尋找地址,把所有的傳輸任務都交給TCP,而TCP相當於一個快遞員的角色。
2. 三次握手
序號
seq:sequence number 的縮寫,即序號。seq表示的則是自己傳遞的序號,TCP在傳輸的時候,其中每一個位元組,都會有一個序號,發送數據的時候,會把第一個數據的第一個序號發送給對方,就是看到的第一步,而接收的這一方面,會按照這個序號來檢查是否是一個連線完整的數據,是完整的話就繼續,不完整的話就重新發送。保證數據完整性不被破壞。
ack: 注意是小寫的 acknoledgement number的縮寫 表示確認號 ,要和ACK(確認位)區分 ,接收端用它來給發送端返回接受訊息的數據資訊,而這時候,它的值就是表明我想接收下一個數據包了。而這個值就是下一個數據包的開始的序號,而這個ack所代表的值的序號前面的數據都已經接受成功了。
ACK確認位: 只有當ACK=1的時候ack纔會起到作用,而在我們第一次請求的時候,是沒有需要確認的接受的數據的,這個時候ACK=0,正常通訊下ACK=1.
SYN: 同步位 ,作用是用於建立連線時同步序號,剛建立連線的時候,ACK=0這時ack就不起作用,當接收端收到SYN=1的報文的時候,會將ack設定爲接收到的seq+1的值,這時候的ack的值就是根據SYN來設定的。
FIN:終止位,在本圖沒有體現,在四次揮手的時候能完全體現出來。而它是用來在數據傳輸都完成之後來釋放連線的。
上圖解析:
第一次握手(SYN=1,seq=x)
用戶端發送一個TCP的SYN標誌位置1的包,指明用戶端打算連線的伺服器的埠,以及初始序號X,儲存在包頭的序列號(seq)中。發送完畢後,用戶端進入SYN_SEND狀態
第二次握手(ack=y+1,ACK=1,seq=y,SYN=1)
伺服器發回確認包(ACK)應答。即SYN和ACK標誌位都爲1。伺服器端選擇自己ISN序列號,放到seq域中,同時將確認序號ack設定爲客戶的ISN加1,即y+1。發送完畢後,伺服器端進入SYN_RCVD狀態
第三次握手(ack=y+1,ACK=1)
用戶端再次發送確認包(ACK),SYN標誌位爲0,ACK標誌位爲1,並且把伺服器發來的ACK序號欄位+1,放在ack中返回。發送完畢後,用戶端進入ESTABLISHED狀態,當伺服器端接收到這個包時也會進入ESTABLISHED狀態,TCP握手結束。
3. 四次揮手
流程解讀:
第一次揮手(FIN=1,seq=x)
假如用戶端想要關閉連線,用戶端發送一個FIN標誌位置爲1的包,表示自己已經沒有數據可以發送了,但是任然可以接受數據,發送完畢後,用戶端進入FIN_WAIT_1狀態
第二次揮手(ACK=1,ack=x+1)
伺服器端接收到用戶端發送的FIN包,發送一個確認包,表明自己接受到了用戶端關閉連線的請求,但是伺服器端還沒有準備好關閉連線。發送完畢後,伺服器端進入CLOSE_WAIT狀態,用戶端收到這個確認包後,進入FIN_WAIT_2狀態,等待伺服器關閉連線。
第三次揮手(FIN=1,seq=y)
伺服器端準備好關閉連線時,向用戶端發送結束連線請求,FIN值爲1。發送完畢後,伺服器端進入LAST_ACK狀態 ,等待來自用戶端的最後一個ACK。
第四次揮手(ACK=1,ack=y+1)
用戶端收到伺服器端的關閉請求時,發送一個確認包,並且進入TIME_WAIT狀態,等待可能出現的要求重傳的ACK包。
伺服器端接收到這個包後,關閉連線,進入CLOSED狀態
用戶端等待了某個固定時間(兩個最大段生命週期,2MSL,2Maximum Segment Lifetime)之後,沒有收到伺服器端的ACK,認爲伺服器端已經正常關閉連線,於是自己也關閉連線,進入CLOSED狀態。兩次後會重傳直到超時。如果多了會有大量半鏈接阻塞佇列。
這是因爲伺服器端在LISTEN狀態下,收到建立連線請求的SYN報文後,把ACK和SYN放在一個報文發送給用戶端。而關閉連線時,當收到對方的FIN報文時,僅僅表示用戶端不再發送數據,但是還能接受數據,伺服器端是否現在關閉數據發送通道需要上層應用決定,因此ACK和FIN一般分開發送。
原因
TIME_WAIT是主動關閉連線的一方保持的狀態,對於爬蟲伺服器來說他本身就是「用戶端」,在完成一個爬取任務之後,他就 會發起主動關閉連線,從而進入TIME_WAIT的狀態,然後在保持這個狀態2MSL(max segment lifetime)時間之後,徹底關閉回收資源。
檢視伺服器所有連線狀態的命令
netstat -n|awk '/^tcp/{++S[$NF]}END{for (key in S) print key,S[key]}'
爲什麼?
防止上一次連線中的包,迷路後重新出現,影響新連線(經過2MSL,上一次連線中所有的重複包都會消失)
可靠的關閉TCP連線。在主動關閉方發送的最後一個 ack(fin) ,有可能丟失,這時被動方會重新發fin, 如果這時主動方處於 CLOSED 狀態 ,就會響應 rst 而不是 ack。所以主動方要處於 TIME_WAIT 狀態,而不能是 CLOSED 。另外這麼設計TIME_WAIT 會定時的回收資源,並不會佔用很大資源的,除非短時間內接受大量請求或者受到攻擊。
解決方案
編輯檔案/etc/sysctl.conf,加入以下內容:
net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30
原因
就是在對方關閉連線之後伺服器程式自己沒有進一步發出ack信號。換句話說,就是在對方連線關閉之後,程式裡沒有檢測到,或者程式壓根就忘記了這個時候需要關閉連線,於是這個資源就一直 被程式佔着。
比如說
伺服器A是一臺爬蟲伺服器,它使用簡單的HttpClient去請求資源伺服器B上面的apache獲取檔案資源,正常情況下,如果請求成功,那麼在抓取完 資源後,伺服器A會主動發出關閉連線的請求,這個時候就是主動關閉連線,伺服器A的連線狀態我們可以看到是TIME_WAIT。如果一旦發生異常呢?假設 請求的資源伺服器B上並不存在,那麼這個時候就會由伺服器B發出關閉連線的請求,伺服器A就是被動的關閉了連線,如果伺服器A被動關閉連線之後程式設計師忘了 讓HttpClient釋放連線,那就會造成CLOSE_WAIT的狀態了。
解決方案
此問題一般是由於程式程式碼編寫問題導致的,查程式碼吧