Windows客戶機訪問Linux的Apache服務,tcpdump抓包截圖 瀏覽器
Windows客戶端(瀏覽器):192.168.11.145 緩存
Linux服務器(Apache):192.168.11.243 安全
服務器:192.168.11.244 服務器
客戶端(瀏覽器):192.168.11.243
網絡
提示:客戶端與服務器端的鏈接狀態可使用netstat -an|more命令來查看 併發
(1)客戶端(192.168.11.243)發起訪問請求,第一次握手,發送【SYN】Seq=x socket
當客戶端調用connect函數發起鏈接時,首先發SYN給服務端,而後本身進入SYN_SENT狀態,並等待服務端發送ACK+SYN。 tcp
此時進入 SYN_SENT狀態:表示客戶端已經發送了SYN報文,等待服務器端迴應,這段時間一直處於這種狀態。 函數
10122 0.280825 192.168.11.243 192.168.11.244 TCP 74 22649→80 [SYN] Seq=0 Win=5840 Len=0 MSS=1460 SACK_PERM=1 TSval=3301310567 TSecr=0 WS=128 工具
(2)服務器端(192.168.11.244)響應請求,第二次握手,發送【SYN,ACK】Seq=y Ack=x+1
服務端收到這個報文後,進入SYN_RECV狀態,而後發送ACK+SYN給客戶端,ACK用於對客戶端SYN的迴應,SYN用於詢問客戶端是否準備好進行數據傳輸。
此時進入SYN_RECV狀態:表示服務端收到客戶端發送ACK+SYN報文。
10123 0.280828 192.168.11.244 192.168.11.243 TCP 74 80→22649 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 SACK_PERM=1 TSval=2610666359 TSecr=3301310567 WS=128
(3)客戶端(192.168.11.243)響應,第三次握手,發送【ACK】Seq=x+1 Ack=y+1
客戶端收到ACK後進入ESTABLISHED狀態,而後發送一個帶ACK標誌的TCP報文做爲服務器端SYN的迴應告訴服務器端準備好數據傳輸,發送SYN通知服務器端開始傳輸。
服務器端收到最後一個SYN+ACK後,進入到ESTABLISHED狀態。
ESTABLISHED:表示鏈接已經創建成功了。
10124 0.280886 192.168.11.243 192.168.11.244 TCP 66 22649→80 [ACK] Seq=1 Ack=1 Win=5888 Len=0 TSval=3301310567 TSecr=2610666359
1)Client
Client發送一個帶SYN標誌的TCP報文(報文1)到服務器端,表示但願創建一個TCP鏈接。
此時程序上,Client端調用socket函數調用時,至關於Client端產生了一個處於Closed狀態的套接字。
Client端又調用connect函數調用,系統爲Client隨機分配一個端口,連同傳入connect中的參數(Server的IP和端口),這就造成了一個鏈接四元組,connect調用讓Client端的socket處於SYN_SENT狀態。
當Server返回確認,併發送SYN,Client返回確認及SYN後,套接字處於ESTABLISHED階段,此時雙方的鏈接已經能夠進行讀寫操做。
2)Server
Server發送一個帶ACK標誌和SYN標誌的TCP報文(報文2)給客戶端,ACK用於對報文1的迴應,SYN用於詢問客戶端是否準備好進行數據傳輸。
此時在程序上,Server端調用socket函數調用時,至關於Server端產生了一個處於Closed狀態的監聽套接字。
Server端調用bind操做,將監聽套接字與指定的地址和端口關聯,而後又調用listen函數,系統會爲其分配未完成隊列和完成隊列,此時的監聽套接字能夠接受Client的鏈接,監聽套接字狀態處於LISTEN狀態。
客戶端發送一個帶ACK標誌的TCP報文(報文3),做爲報文2的迴應。
當Server端調用accept操做時,會從完成隊列中取出一個已經完成的client鏈接,同時在server這端會產生一個會話套接字,用於和client端套接字的通訊,這個會話套接字的狀態是ESTABLISH。
與鏈接創建分爲server/client不一樣,鏈接關閉並無絕對的server/client之分,鏈接關閉分爲主動關閉和被動關閉。 Server和client均可以擔任這兩個角色中的任意一個。如client能夠關閉它與server的鏈接,一樣的server同樣也能夠關閉一些長時間無讀寫事件發生的鏈接。既然這麼說了,下面就會分紅兩個部分:server主動關閉,client主動關閉。
服務器(Apache):172.21.50.1
客戶端(瀏覽器):192.168.144.129
(1)服務器端(172.21.50.1)主動發起關閉請求,第一次握手開始,調用close函數發送【FIN,ACK】,Seq=x(492)Ack=y(360)到客戶端
7 0.014071 172.21.50.1 192.168.144.129 TCP 54 80→54658 [FIN, ACK] Seq=492 Ack=360 Win=15744 Len=0
此時服務器端處於FIN_WAIT1狀態。
(2)客戶端(192.168.144.129)收到這個【FIN,ACK】,發送迴應的【ACK】開始第二次握手,Seq=y(360)Ack=x+1(493)給服務器。
8 0.017852 192.168.144.129 172.21.50.1 TCP 60 54658→80 [ACK] Seq=360 Ack=493 Win=65208 Len=0
服務器端收到這個【ACK】後進入FIN_WAIT2狀態。
(3)客戶端(192.168.144.129)也沒有數據要傳輸了,開始關閉與客戶端的鏈接,第三次握手開始,也須要調用close函數發送一個【FIN,ACK】,Seq=y(360)Ack=x+1(493)給服務器。
9 0.018789 192.168.144.129 172.21.50.1 TCP 60 54658→80 [FIN, ACK] Seq=360 Ack=493 Win=65208 Len=0
此時客戶端的狀態爲LAST_ACK。
(4)服務器端(172.21.50.1)收到來自客戶端的【FIN,ACK】後,服務器端的套接字處於TIME_WAIT狀態,第四次握手,它會向客戶端再發送一個【ACK】確認,Seq=x+1(493)Ack=y+1(361)。
10 0.018801 172.21.50.1 192.168.144.129 TCP 54 80→54658 [ACK] Seq=493 Ack=361 Win=15744 Len=0
當客戶端收到ACK後就處於CLOSED狀態。
(這個有點不一樣,有5次握手)
服務器(Apache):172.21.50.1
客戶端(瀏覽器):192.168.144.129
(1)服務器端(172.21.50.1)主動發起關閉請求,第一次握手開始,調用close函數發送【FIN,ACK】,Seq=x(3766)Ack=y(823)到客戶端
32 0.419117 172.21.50.1 192.168.144.129 TCP 54 443→52666 [FIN, ACK] Seq=3766 Ack=823 Win=17920 Len=0
此時服務器端處於FIN_WAIT1狀態。
(2)客戶端(192.168.144.129)收到這個【FIN,ACK】,發送迴應的【ACK】開始第二次握手,Seq=y(823)Ack=x(3766)給服務器。
33 0.422318 192.168.144.129 172.21.50.1 TCP 60 52666→443 [ACK] Seq=823 Ack=3766 Win=64852 Len=0
服務器端收到這個【ACK】後進入FIN_WAIT2狀態。
(3)客戶端(192.168.144.129)再發送一個迴應的【ACK】,開始第三次握手,Seq=y(823)Ack=x+1(3767)給服務器。
34 0.422405 192.168.144.129 172.21.50.1 TCP 60 52666→443 [ACK] Seq=823 Ack=3767 Win=64852 Len=0
此時客戶端處於CLOSE_WAIT狀態。
(4)客戶端(192.168.144.129)也沒有數據要傳輸了,開始關閉與客戶端的鏈接,第四次握手開始,也須要調用close函數發送一個【FIN,ACK】,Seq=y(823)Ack=x+1(3767)給服務器。
35 0.423136 192.168.144.129 172.21.50.1 TCP 60 52666→443 [FIN, ACK] Seq=823 Ack=3767 Win=64852 Len=0
此時客戶端的狀態爲LAST_ACK。
(5)服務器端(172.21.50.1)收到來自客戶端的【FIN,ACK】後,服務器端的套接字處於TIME_WAIT狀態,第五次握手,它會向客戶端再發送一個【ACK】確認,Seq=x+1(3767)Ack=y+1(824)。
36 0.423147 172.21.50.1 192.168.144.129 TCP 54 443→52666 [ACK] Seq=3767 Ack=824 Win=17920 Len=0
當客戶端收到ACK後就處於CLOSED狀態。
因爲在鏈接關閉後,還不能肯定全部鏈接關閉前的包都被客戶端接受到了(包的接受是沒有前後順序的),所以有了TIME_WAIT狀態。在這個狀態中,服務器仍然在等待客戶機發送的【ACK】包。這個狀態將保持2*MSL的時間,這裏的MSL指的是一個TCP包在網絡中存在的最長時間,通常狀況下 2*MSL=240秒。
原理與上面相同,略。
首先說一下TCP/IP詳解中描述的關於TIME_WAIT的描述及其存在的必要性:
主動關閉的socket當收到對端的FIN操做後,該socket就會處於TIME_WAIT狀態,處於TIME_WAIT狀態的socket會存活2MSL(Max Segment Lifetime),
之因此存活這麼長時間是有理由的:
一方面是可靠的實現TCP全雙工鏈接的終止,也就是當最後的ACK丟失後,被動關閉端會重發FIN,所以主動關閉端須要維持狀態信息,以容許它從新發送最終的ACK。
另外一方面TCP在2MSL等待期間,定義這個鏈接(4元組)不能再使用,任何遲到的報文都會丟棄。設想若是沒有2MSL的限制,剛好新到的鏈接正好知足原先的4元組,這時候鏈接就可能接收到網絡上的延遲報文就可能干擾最新創建的鏈接。重複的分節在網絡中消逝。
有沒有什麼狀況使主動關閉的socket直接進入CLOSED狀態呢?
主動關閉的一方在發送最後一個 ack 後就會進入 TIME_WAIT 狀態 停留2MSL(max segment lifetime)時間,這個是TCP/IP必不可少的,也就是「解決」不了的。也就是TCP/IP設計者原本是這麼設計的。
主要有兩個緣由:
一、防止上一次鏈接中的包,迷路後從新出現,影響新鏈接(通過2MSL,上一次鏈接中全部的重複包都會消失)
二、可靠的關閉TCP鏈接。在主動關閉方發送的最後一個 ack(fin) ,有可能丟失,這時被動方會從新發 fin, 若是這時主動方處於 CLOSED 狀態 ,就會響應 rst 而不是 ack。因此 主動方要處於 TIME_WAIT 狀態,而不能是 CLOSED 。
特別提示的是:爲何TIME_WAIT狀態還須要等待2MSL才能回到CLOSED狀態?或者爲何TCP要引入TIME_WAIT狀態?
《TCP/IP詳解》中如此解釋:當TCP執行一個主動關閉,併發回最後一個ACK後,該鏈接必須在TIME_WAIT狀態停留的時間爲2倍的MSL,這樣可讓TCP再次發送最後的ACK以防止這個ACK丟失(另外一端超時重發最後的FIN)。
附註:MSL(Maximum Segment Lifetime)即最大生存時間,RFC 793中指出MSL爲2分鐘,可是實現中的經常使用值爲30秒、1分鐘或者2分鐘。
因爲TCP鏈接是全雙工的,所以每一個方向都必須單獨進行關閉。原則是主動關閉的一方(如已傳輸完全部數據等緣由)發送一個FIN報文來表示終止這個方向的鏈接,收到一個FIN意味着這個方向再也不有數據流動,但另外一個方向仍能繼續發送數據,直到另外一個方向也發送FIN報文。
四次揮手的具體過程以下:
客戶端發送一個FIN報文(報文4)給服務器,表示我將關閉客戶端到服務器端這個方向的鏈接。
服務器收到報文4後,發送一個ACK報文(報文5)給客戶端,序號爲報文4的序號加1。
服務器發送一個FIN報文(報文6)給客戶端,表示本身也將關閉服務器端到客戶端這個方向的鏈接。
客戶端收到報文6後,發回一個ACK報文(報文7)給服務器,序號爲報文6的序號加1。
至此,一個TCP鏈接就關閉了。(4次揮手不是關閉TCP鏈接的惟一辦法,見五、TCP/IP答疑解惑)
TCP三次握手,四次揮手的時序圖:
由於當處於LISTEN 狀態的服務器端SOCKET當收到SYN報文(客戶端但願新建一個TCP鏈接)後,它能夠把ACK(應答做用)和SYN(同步做用)放在同一個報文裏來發 送給客戶端。但在關閉TCP鏈接時,當收到對方的FIN報文時,對方僅僅表示對方沒有數據發送給你了,但未必你的全部數據都已經所有發送給了對方,因此你 大可沒必要立刻關閉SOCKET(發送一個FIN報文),等你發送完剩餘的數據給對方以後,再發送FIN報文給對方來表示你贊成如今關閉鏈接了,因此一般情 況下,這裏的ACK報文和FIN報文都是分開發送的。
由於雖然雙方都贊成關閉鏈接了,並且握手的4個報文也都發送完畢,按理能夠直接回到CLOSED 狀態(就比如從SYN_SENT 狀態到ESTABLISH 狀態那樣),可是咱們必須假想網絡是不可靠的,你沒法保證你(客戶端)最後發送的ACK報文必定會被對方收到,就是說對方處於LAST_ACK 狀態下的SOCKET可能會由於超時未收到ACK報文,而重發FIN報文,因此這個TIME_WAIT 狀態的做用就是用來重發可能丟失的ACK報文。
不必定,4次揮手關閉TCP鏈接是最安全的作法。但在有些時候,咱們不喜歡TIME_WAIT 狀態(如當MSL數值設置過大致使服務器端有太多TIME_WAIT狀態的TCP鏈接,減小這些條目數能夠更快地關閉鏈接,爲新鏈接釋放更多資源),這時 咱們能夠經過設置SOCKET變量的SO_LINGER標誌來避免SOCKET在close()以後進入TIME_WAIT狀態,這時將經過發送RST強 制終止TCP鏈接(取代正常的TCP四次握手的終止方式)。但這並非一個很好的主意,TIME_WAIT 對於咱們來講每每是有利的。
SYN攻擊原理:
SYN Flood利用TCP協議缺陷,發送了大量僞造的TCP鏈接請求,使得被攻擊方資源耗盡,沒法及時迴應或處理正常的服務請求。一個正常的TCP鏈接須要三次握手,首先客戶端發送一個包含SYN標誌的數據包,其後服務器返回一個SYN/ACK的應答包,表示客戶端的請求被接受,最後客戶端再返回一個確認包ACK,這樣才完成TCP鏈接。在服務器端發送應答包後,若是客戶端不發出確認,服務器會等待到超時,期間這些半鏈接狀態都保存在一個空間有限的緩存隊列中;若是大量的SYN包發到服務器端後沒有應答,就會使服務器端的TCP資源迅速耗盡,致使正常的鏈接不能進入,甚至會致使服務器的系統崩潰。
tcp 0 0 10.11.11.11:23 236.219.139.207:49162 SYN_RECV -
上面是在LINUX系統中看到的,不少鏈接處於SYN_RECV狀態(在WINDOWS系統中是SYN_RECEIVED狀態),源IP地址都是隨機的,代表這是一種帶有IP欺騙的SYN攻擊。