什麼是TCP?
TCP(Transmission Control Protocol)是傳輸控制協議,工做在傳輸層,具備面向鏈接、全雙工、半關閉、錯誤檢查、將數據打包成段,排序、確認機制、數據恢復,重傳、流量控制,滑動窗口、擁塞控制,慢啓動和擁塞避免算法的特性,最重要的是TCP協議是可靠的,對於不少重要數據在網絡的傳遞中起到相當重要的做用。linux
先了解一個TCP包頭
- 源端口、目標端口:計算機上的進程要和其餘進程通訊是要經過計算機端口的,而一個計算機端口某個時刻只能被一個進程佔用,因此經過指定源端口和目標端口,就能夠知道是哪兩個進程須要通訊。源端口、目標端口是用16位表示的,可推算計算機的端口個數爲2^16個
- 序列號:表示本報文段所發送數據的第一個字節的編號。在TCP鏈接中所傳送的字節流的每個字節都會按順序編號。因爲序列號由32位表示,因此每2^32個字節,就會出現序列號迴繞,再次從 0 開始
- 確認號:表示接收方指望收到發送方下一個報文段的第一個字節數據的編號。也就是告訴發送方:我但願你(指發送方)下次發送的數據的第一個字節數據的編號爲此確認號
- 數據偏移:表示TCP報文段的首部長度,共4位,因爲TCP首部包含一個長度可變的選項部分,須要指定這個TCP報文段到底有多長。它指出 TCP 報文段的數據起始處距離 TCP 報文段的起始處有多遠。該字段的單位是32位(即4個字節爲計算單位),4位二進制最大表示15,因此數據偏移也就是TCP首部最大60字節
- URG:表示本報文段中發送的數據是否包含緊急數據。後面的緊急指針字段(urgent pointer)只有當URG=1時纔有效
- ACK:表示是否前面確認號字段是否有效。只有當ACK=1時,前面的確認號字段纔有效。TCP規定,鏈接創建後,ACK必須爲1,帶ACK標誌的TCP報文段稱爲確認報文段
- PSH:提示接收端應用程序應該當即從TCP接收緩衝區中讀走數據,爲接收後續數據騰出空間。若是爲1,則表示對方應當當即把數據提交給上層應用,而不是緩存起來,若是應用程序不將接收到的數據讀走,就會一直停留在TCP接收緩衝區中
- RST:若是收到一個RST=1的報文,說明與主機的鏈接出現了嚴重錯誤(如主機崩潰),必須釋放鏈接,而後再從新創建鏈接。或者說明上次發送給主機的數據有問題,主機拒絕響應,帶RST標誌的TCP報文段稱爲復位報文段
- SYN:在創建鏈接時使用,用來同步序號。當SYN=1,ACK=0時,表示這是一個請求創建鏈接的報文段;當SYN=1,ACK=1時,表示對方贊成創建鏈接。SYN=1,說明這是一個請求創建鏈接或贊成創建鏈接的報文。只有在前兩次握手中SYN才置爲1,帶SYN標誌的TCP報文段稱爲同步報文段
- FIN:表示通知對方本端要關閉鏈接了,標記數據是否發送完畢。若是FIN=1,即告訴對方:「個人數據已經發送完畢,你能夠釋放鏈接了」,帶FIN標誌的TCP報文段稱爲結束報文段
- 窗口大小:表示如今容許對方發送的數據量,也就是告訴對方,從本報文段的確認號開始容許對方發送的數據量,達到此值,須要ACK確認後才能再繼續傳送後面數據,由Window size value * Window size scaling factor(此值在三次握手階段TCP選項Window scale協商獲得)得出此值
- 校驗和:提供額外的可靠性
- 緊急指針:標記緊急數據在數據字段中的位置
- 選項部分:其最大長度可根據TCP首部長度進行推算。TCP首部長度用4位表示,選項部分最長爲:(2^4-1)*4-20=40字節
在互聯網上創建鏈接
TCP三次握手
過程:
第一次
第一次握手:創建鏈接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。
第二次
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP鏈接成功)狀態,完成三次握手。
完成三次握手,客戶端與服務器開始傳送數據。算法
爲何要三次握手?有什麼意義?
同步鏈接雙方的序列號和確認號並交換 TCP 窗口大小信息,而且確保雙方可以相互通訊緩存
sync半鏈接和accept全鏈接隊列
服務器server從接收到客戶端client請求以後從LISTEN狀態切換成SYNC-REVD狀態,並將客戶端鏈接加入至sync queue隊列中,但這個隊列的默認值大小通常爲128,這時就有可能某些***利用這個陣列來不斷的填充你的陣列,達到形成你的服務不可訪問的效果 ,咱們能夠經過在linux系統上能夠經過echo "value" > /proc/sys/net/ipv4/tcp_max_syn_backlog在修改默認值大小,建議value=1024。
同時,從上圖咱們能夠得知/proc/sys/net/core/somaxconn爲完成鏈接隊列大小,也建議調整大小爲1024以上。服務器
TCP四次揮手
爲何要四次揮手?
TCP協議通訊雙方均可以獨立關閉本身的通訊通道,也就是半關閉。
client先發送FIN告知對方我已經完成數據發送了,server回覆ack來肯定我知道了。這樣一個流程,就關閉了client的發送信息通道。可是還能夠接收來自server方的數據。
server此時已經知道接收不到client的數據了,可是還能夠給它發送數據。若是server也沒有啥數據要發送給對方了,server也會以FIN標誌位發送一個信息給client,client接到後,也會傳遞一個ack表示知道了。這樣子,雙方都完成了關閉。網絡
有限狀態機
客戶端先發送一個FIN給服務端,本身進入了FIN_WAIT_1狀態,這時等待接收服務端的報文,該報文會有三種可能:
- 只有服務端的ACK
- 只有服務端的FIN
- 基於服務端的ACK,又有FIN
- 一、只收到服務器的ACK,客戶端會進入FIN_WAIT_2狀態,後續當收到服務端的FIN時,迴應發送一個ACK,會進入到TIME_WAIT狀態,這個狀態會持續2MSL(TCP報文段在網絡中的最大生存時間, RFC 1122標準的建議值是2min).客戶端等待2MSL,是爲了當最後一個ACK丟失時,能夠再發送一次。由於服務端在等待超時後會再發送一個FIN給客戶端,進而客戶端知道ACK已丟失
- 二、只有服務端的FIN時,迴應一個ACK給服務端,進入CLOSING狀態,而後接收到服務端的ACK時,進入TIME_WAIT狀態
- 三、同時收到服務端的ACK和FIN,直接進入TIME_WAIT狀態
- 客戶端經過connect系統調用主動與服務器創建鏈接connect系統調用首先給服務器發送一個同步報文段,使鏈接轉移到SYN_SENT狀態
- 此後connect系統調用可能由於以下兩個緣由失敗返回:
- 一、若是connect鏈接的目標端口不存在(未被任何進程監聽),或者該端口仍被處於TIME_WAIT狀態的鏈接所佔用(見後文),則服務器將給客戶端發送一個復位報文段,connect調用失敗。
- 二、若是目標端口存在,但connect在超時時間內未收到服務器的確認報文段,則connect調用失敗。
- connect調用失敗將使鏈接當即返回到初始的CLOSED狀態。若是客戶端成功收到服務器的同步報文段和確認,則connect調用成功返回,鏈接轉移至ESTABLISHED狀態
當客戶端執行主動關閉時,它將向服務器發送一個結束報文段,同時鏈接進入FIN_WAIT_1狀態。若此時客戶端收到服務器專門用於確認目的的確認報文段,則鏈接轉移至FIN_WAIT_2狀態。當客戶端處於FIN_WAIT_2狀態時,服務器處於CLOSE_WAIT狀態,這一對狀態是可能發生半關閉的狀態。此時若是服務器也關閉鏈接(發送結束報文段),則客戶端將給予確認並進入TIME_WAIT狀態
客戶端從FIN_WAIT_1狀態可能直接進入TIME_WAIT狀態(不通過FIN_WAIT_2狀態),前提是處於FIN_WAIT_1狀態的服務器直接收到帶確認信息的結束報文段(而不是先收到確認報文段,再收到結束報文段)
處於FIN_WAIT_2狀態的客戶端須要等待服務器發送結束報文段,才能轉移至TIME_WAIT狀態,不然它將一直停留在這個狀態。若是不是爲了在半關閉狀態下繼續接收數據,鏈接長時間地停留在FIN_WAIT_2狀態並沒有益處。鏈接停留在FIN_WAIT_2狀態的狀況可能發生在:客戶端執行半關閉後,未等服務器關閉鏈接就強行退出了。此時客戶端鏈接由內核來接管,可稱之爲孤兒鏈接(和孤兒進程相似)tcp
Linux爲了防止孤兒鏈接長時間存留在內核中,定義了兩個內核參數:
/proc/sys/net/ipv4/tcp_max_orphans 指定內核能接管的孤兒鏈接數目
/proc/sys/net/ipv4/tcp_fin_timeout 指定孤兒鏈接在內核中生存的時間ide