1. TCP簡介及報文格式
1.1 簡介
TCP(Transmission Control Protocol)傳輸控制協議是一種面向連接的、可靠的、基於字節流的傳輸層協議。
1.2 報文格式

重要字段:
- 端口號:16位,用來標識同一台計算機的不同的應用進程。
-
1)源端口:源端口和IP地址的作用是標識報文的返回地址。
-
2)目的端口:端口指明接收方計算機上的應用程序接口。
-
序號:Seq序號,32位,一次TCP通信 (從TCP連接建立到斷開) 過程中某一個傳輸方向上的字節流的每個字節的編號。
-
確認序號:ACK序號,32位,用作對另一方發送來的TCP報文段的響應。只有ACK標志位為1時,確認序號字段才有效,其值是收到的TCP報文段的序號值加1。
-
標志位:共6個,即URG、ACK、PSH、RST、SYN、FIN 等。
標志位 名稱 具體含義 URG 緊急標志 緊急指針(urgent pointer)有效 ACK 確認標志 確認序號有效 PSH 推標志 接收方應盡快將報文交給應用層 RST 復位標志 重置連接 SYN 同步標志 發起一個新連接 FIN 結束標志 釋放一個連接
2. 三次握手(Three-way Handshake)
2.1 含義
建立TCP連接時,客戶端和服務器總共發送3個包。
2.2 三次握手過程

- 客戶端發送syn報文。SYN標志為1,指明客戶端打算連接的服務器的端口,以及初始序號X,保存在包頭的序列號(Sequence Number)字段里。
- 服務器發回確認syn+ack報文。SYN標志為1,確認序號(Acknowledgement Number)為客戶端的序列號加1,即X+1,並設置發送序號為Y。
- 客戶端再次發送ack報文。 SYN標志位為0,確認序號(Acknowledgement Number)為服務器的序列號加1,即Y+1,並設置發送序號為Z。
問題1:為什么兩次握手不行?
三次握手是為了讓雙方驗證各自接收能力和發送能力。
-
1st:A發送SYN給B,B接收到。這里B能確認A的發送能力和B的接收能力。
-
2nd:B發送SYNACK給A,A收到。這里A能確認A的接收能力和B的發送能力。此外,A收到SYNACK,說明前面A的SYN成功到達B,也能確認A自己的發送能力和B的接收能力。至此,A已經確認雙方各自發送能力和接收能力OK,因此轉為ESTABLISHED狀態。
-
3rd:A發送ACK到B,B接收。這里B能確認A的發送能力和B的接收能力。由於B能收到ACK,說明前面發送的SYNACK已經被接受了,說明A的接收能力和B的發送能力正常。
若使用兩次握手,就不能確認上述四種能力,可能有問題。
-
假定A發的SYN報文沒消失,而是在某網絡節點長時間滯留了,以至於到連接釋放后的某個時間才到達B。
-
本來這是一個早已失效的報文段,但B收到此失效連接請求報文段后,卻誤以為是A又發出一次新的連接請求,於是B就發出確認報文段,同時建立連接。
-
由於現在A並沒有發出建立連接請求,因此不理睬B的SYNACK報文,也不會向B發送數據,但B卻以為新連接已經建立,並一直等待A發來的數據,B的許多資源被白白浪費。
問題2:三次握手過程中可以攜帶數據嗎?
前兩次不行,第三次可以攜帶。 假如第一次可以,如果有人惡意攻擊服務器,那他在第一次SYN 報文中放入大量數據。因為攻擊者根本不理會服務器的接收、發送能力是否正常,只是瘋狂重復發 SYN 報文,這會讓服務器花費很多內存與時間來接收這些報文。也就是說,第一次握手不能放數據,1個簡單原因服務器會更容易受到攻擊。而對於第三次,此時客戶端處於 ESTABLISHED 狀態,已經建立起連接,知道服務器接收與發送能力正常,所以攜帶數據也沒毛病。
問題3:ISN(Initial Sequence Number)是固定的嗎?
不固定,client_isn是隨機生成的,而server_isn則需要根據SYN報文中的源、IP和端口,加上服務器本身密碼數進行相同散列得到,顯然也不固定。
問題4:第三次握手失敗了怎么辦?
- 在第2次握手中,server向client發送SYN+ACK報文后,就會啟動一個定時器,等待client返回的ACK報文。
- 如果第三次失敗,client給server返回了ACK報文,server並不能收到這個ACK報文。那server就會啟動超時重傳機制,超過規定時間會重新發起第2次握手,向client發送SYNACK。重傳次數默認5次。
- 如果到重傳指定次數,仍未收到ACK應答,那一段時間后server會關閉這個連接。但client認為這個連接已建立,如果它向server寫數據,server將回應RST包、強制關閉TCP連接,以防止SYN攻擊。
問題5:什么是SYN攻擊?如何防范?
在三次握手過程中,服務器發送SYN-ACK之后,收到客戶端ACK之前的TCP連接稱為半連接(half-open connect)。此時服務器處於SYN-RECV。當收到ACK后,服務器轉入ESTABLISHED狀態。
SYN攻擊就是攻擊客戶端,在短時間內偽造大量不存在的IP地址,向服務器不斷發送SYN包,服務器回復確認包,並等待客戶確認。 由於源地址不存在,服務器需要不斷重發直至超時,這些偽造SYN包將長時間占用未連接隊列,正常SYN請求被丟棄,目標系統運行緩慢,嚴重者引起網絡堵塞甚至系統癱瘓。
SYN攻擊是一種典型的DoSe/DDoS攻擊。
問題6:如何檢測SYN攻擊?
檢測SYN攻擊非常方便,當你在服務器上看到大量半連接狀態時,特別是IP地址是隨機的,基本可以斷定這是一次SYN攻擊,在Linux/Unix可用netstat命令檢測。
問題7:如何防御SYN攻擊?
SYN攻擊不能完全被阻止,除非重新設計TCP協議。能做的就是就是盡可能減輕SYN攻擊危害,常見防御方法有:縮短超時(SYN Timeout)時間、增大最大半連接數、過濾網關防護、SYN cookies技術。
問題8:握手過程中除了序號的同步,還會同步什么信息?
- 1)序號;
- 2)標志位:SYN(發起一個連接)、ACK(確認序號有效);
- 3)窗口(流量控制中的接收窗口)。
問題9:通過什么方式去知道某台電腦上還能建立多少個TCP連接?
系統用四元組{Local IP,Local Port,Remote IP,Remote Port} 來唯一標識TCP連接。
Client發起TCP連接時,系統通常會選取一個空閑本地端口(Local Port),該端口,類型是Unsigned short,最大65536,端口0有特殊含義,因此最大可用端口,即最大TCP連接數只有65535。
Server部分的Remote IP和Remote Port可變,因此最大TCP連接為客戶端IP數*客戶端Port數,IPV4簡單情況(不考慮地址分類) 最大連接數為232(IP數)* 216(Port數) ,也就是Server端單機最大TCP連接數約為248 。
3. 四次揮手(four-way handshake)
3.1 含義
TCP連接拆除需要發送四個包,因此稱為四次揮手。
Client或Server均可主動發起。在socket編程中,執行close()操作即可產生揮手操作。
3.2 四次揮手過程

-
客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送。
-
服務器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1。和SYN一樣,一個FIN將占用一個序號。
-
服務器B關閉與客戶端A的連接,發送一個FIN給客戶端A。
-
客戶端A發回ACK報文確認,並將確認序號設置為收到序號加1。
3.3 深入理解TCP連接的釋放
由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務后就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP連接在收到一個FIN后仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
TCP協議的連接是全雙工連接,一個TCP連接存在雙向的讀寫通道。
簡單說來是 “先關讀,后關寫”,一共需要四個階段。以客戶機發起關閉連接為例:
- 服務器讀通道關閉
- 客戶機寫通道關閉
- 客戶機讀通道關閉
- 服務器寫通道關閉
關閉行為是在發起方數據發送完畢之后,給對方發出一個FIN(finish)數據段。直到接收到對方發送的FIN,且對方收到了接收確認ACK之后,雙方的數據通信完全結束,過程中每次接收都需要返回確認數據段ACK。
3.4 TCP狀態遷移

客戶端TCP狀態遷移:
CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服務器TCP狀態遷移:
CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
各個狀態的意義如下:
LISTEN - 偵聽來自遠方TCP端口的連接請求;
SYN-SENT - 在發送連接請求后等待匹配的連接請求;
SYN-RECEIVED - 在收到和發送一個連接請求后等待對連接請求的確認;
ESTABLISHED - 代表一個打開的連接,數據可以傳送給用戶;
FIN-WAIT-1 - 等待遠程TCP的連接中斷請求,或先前的連接中斷請求的確認;
FIN-WAIT-2 - 從遠程TCP等待連接中斷請求;
CLOSE-WAIT - 等待從本地用戶發來的連接中斷請求;
CLOSING - 等待遠程TCP對連接中斷的確認;
LAST-ACK - 等待原來發向遠程TCP的連接中斷請求的確認;
TIME-WAIT - 等待足夠的時間以確保遠程TCP接收到連接中斷請求的確認;
CLOSED - 沒有任何連接狀態;
SYN_RECV
服務端收到建立連接的SYN沒有收到ACK包的時候處在SYN_RECV狀態。處在SYNC_RECV的TCP連接稱為半連接,並存儲在內核的半連接隊列中,在內核收到對端發送的ack包時會查找半連接隊列,並將符合的requst_sock信息存儲到完成三次握手的連接的隊列中,然后刪除此半連接 。
CLOSE_WAIT
發起TCP連接關閉的一方稱為client,被動關閉的一方稱為server。在已經接收到FIN,但是還沒有發送自己的FIN的時刻,連接處於CLOSE_WAIT狀態。出現這種狀況一般都是由於server端代碼的問題,如果服務器出現大CLOSE_WAIT,應該要考慮檢查代碼 。
TIME_WAIT
根據TCP協議定義的3次握手斷開連接規定,發起socket主動關閉的一方 socket將進入TIME_WAIT狀態。TIME_WAIT狀態將持續2個MSL(Max Segment Lifetime),在Windows下默認為4分鍾,即240秒。TIME_WAIT狀態下的socket不能被回收使用。
問題1:為什么建立連接是三次握手,而關閉連接卻是四次揮手呢?
關鍵在中間兩步:
- 這是因為服務端在LISTEN狀態下,收到建立連接請求的SYN報文后,把ACK和SYN放在一個報文里發送給客戶端。
- 而關閉連接時,收到對方的FIN報文時,僅表示對方不再發送,但還能接收數據,己方也未必全部發送完畢。所以只能先回復一個ACK報文,告訴客戶端“你發的FIN報文已收到”。等服務器所有報文發送/接收完,才能發送FIN報文。因此ACK和SYN分開發送,要四次握手。
問題2:四次揮手釋放連接時,等待2MSL的意義?
- 保證客戶端發送的最后1個ACK報文能到達服務器。假設網絡不可靠,ACK報文丟失。如果服務端發出FIN報文后沒收到ACK報文,就會重發FIN報文(收到不再發),此時處於TIME-WAIT的客戶端就會重發ACK報文。
當然,客戶端也不能無限等待這個FIN報文,需要設置一個定時器,2MSL正好,因為1個最大發送和1個回復最長時間沒收到FIN,可以推斷ACK報文已經被服務器接收,所以結束TCP連接。 - 防止已失效的連接請求報文段出現在新連接中。客戶端發完最后1個ACK報文后,再經過2MSL時間,就可以使網絡不通暢產生的滯留報文段失效,這樣下一個新連接中就不會出現舊的連接請求報文。
問題3:四次揮手過程中服務端的哪幾種狀態,哪幾種包?
- 收到FIN之前是ESTABLISHED狀態;
- 發完ACK過程變為CLOSE_WAIT(關閉等待) 狀態;
- 發完FIN報文后是LAST_ACK(最后確認) 狀態;
- 收到客戶端ACK報文后變為CLOSED狀態。
問題4:查看當前系統下所有連接狀態的數
[root@vps ~]#netstat -n|awk '/^tcp/{++S[$NF]}END{for (key in S) print key,S[key]}'
TIME_WAIT 286
FIN_WAIT1 5
FIN_WAIT2 6
ESTABLISHED 269
SYN_RECV 5
CLOSING 1
問題5:如何處理大量TIME_WAIT狀態的連接
如發現系統存在大量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
然后執行 /sbin/sysctl -p 讓參數生效。
net.ipv4.tcp_syncookies = 1 表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊,默認為0,表示關閉;
net.ipv4.tcp_tw_reuse = 1 表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認為0,表示關閉;
net.ipv4.tcp_tw_recycle = 1 表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉;
net.ipv4.tcp_fin_timeout 修改系默認的 TIMEOUT 時間。
注:
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
設置這兩個參數: reuse是表示是否允許重新應用處於TIME-WAIT狀態的socket用於新的TCP連接; recycle是加速TIME-WAIT sockets回收。
SYN Cookie是專門用來防范SYN Flood攻擊的一種手段。它的原理是,在TCP服務器收到TCP SYN包並返回TCP SYN+ACK包時,不分配一個專門的數據區,而是根據這個SYN包計算出一個cookie值。在收到TCP ACK包時,TCP服務器在根據那個cookie值檢查這個TCP ACK包的合法性。如果合法,再分配專門的數據區進行處理未來的TCP連接。