TCP(一)
TCP的特點:三次握手、四次揮手、可靠連接、丟包重傳。所有的關鍵詞都圍繞著可靠傳輸。
實現可靠傳輸的核心機制:seq+ack。通過ack判斷是否有丟包,是否需要重傳。
三次握手
1)初始狀態:client為CLOSED,server為LISTEN,此時client 發送 syn 到server ,client狀態變為SYN_SENT;
2)server 收到 syn後回復syn+ack給client,client狀態變為SYN_RCVD;
3)client 收到syn+ack後,回復ack向server表示收到了server的syn+ack(此時client連接狀態已經是established),當Server收到ack後,狀態變成established。
為什麽要握手?
1)最重要的目的:告訴對方自己的seq,對方回復ack(收到的seq+包的大小),用於判斷是否有丟包;
2)其他目的:協商信息,例如:MSS–最大傳輸包、SACK_PERM–是否支持Selective ack(用戶優化重傳效率)等。
四次揮手
1)client發送fin包給server,client連接狀態變為FIN-WAIT-1;
2)server收到fin包後回復ack給client,表示server知道client要斷開了,server連接狀態變為CLOSE-WAIT;client收到ack後連接狀態變為FIN-WAIT-2;
3)server發送fin包給client,表示server也可以斷開了,server連接狀態變為LAST-ACK;
4)client收到fin包後回復ack給server,此時client端連接狀態先變為TIME-WAIT,等待一段時間後變為CLOSED狀態;server收到ack後狀態變為CLOSED。
為什麽三次握手、四次揮手?
之所以絕大數時候我們看到的都是四次揮手,是因為收到fin後,知道對方要關閉了,然後OS通知應用層要關閉啥的,這裏應用層可能需要做些準備工作,有一些延時,所以先回ack,準備好了再發fin 。 握手過程沒有這個準備過程所以可以立即發送syn+ack。
這是因為服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它可以把ACK和SYN(ACK起應答用,而SYN起同步作用)在一個報文裏來發送。但關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之後,再發送FIN報文給對方來表示你同意現在可關閉,這裏的ACK報文和FIN報文多數情況下都是分開發送的.
為什麽是三次握手,而不是兩次?
因為tcp是可靠傳輸協議,靠seq+ack實現,因此建立一個可靠的單向通道至少需要一次seq+ack;又因為tcp是雙向通信協議,所以服務端也需要進行一次seq+ack;為了優化通信效率,服務端發送ack和seq消息合並,所以需要3次握手。
為什麽TIME_WAIT狀態還需要等2MSL後才能返回到CLOSED狀態?
這是因為雖然雙方都同意關閉連接了,而且握手的4個報文也都協調和發送完畢,按理可以直接回到CLOSED狀態(就好比STABLISH狀態那樣);但是因為我們必須要假想網絡是不可靠的,你無法保證你最後發送的ACK報文會一定被對方收到,因狀態下的SOCKET可能會因為超時未收到ACK報文,而重發FIN報文,所以這個TIME_WAIT狀態的作用就是用來重發可能丟失的ACK報文。
怎麽確定是否丟包?
ack總是等於seq+len,len為包的大小,(SYN、FIN、ACK包除外,len為0),發送方通過ack知曉接收方是否收到消息。ack表示這個數字前面的數據都收到了。
MSS
MTU(maximum transmission unit,最大傳輸單元),由硬件規定,如以太網的MTU為1500字節。
MSS(maximum segment size,最大分段大小),為TCP數據包每次傳輸的最大數據分段大小,一般由發送端向對端TCP通知對端在每個分節中能發送的最大TCP數據。MSS值為MTU值減去IPv4 Header(20 Byte)和TCP header(20 Byte)得到。
SACK_PERM
SACK_PERM 用於丟包的話提升重傳效率,比如client一次發了1、2、3、4、5 這5個包給server,實際server收到了 1、3、4、5這四個包,中間2丟掉了。這個時候server回復ack的時候,都只能回復2,表示2前面所有的包都收到了,給我發第二個包吧,如果server 收到3、4、5還是沒有收到2的話,也是回復ack 2而不是回復ack 3、4、5、6的,表示快點發2過來。
但是這個時候client雖然知道2丟了,然後會重發2,但是不知道3、4、5有沒有丟啊,實際3、4、5 server都收到了,如果支持sack,那麽可以ack 2的時候同時告訴client 3、4、5都收到了,這樣client重傳的時候只重傳2就可以,如果沒有sack的話那麽可能會重傳2、3、4、5,這樣效率就低了。
抓包實例驗證
flags 標誌由S(SYN), F(FIN), P(PUSH), R(RST), 在10.18.222.22上抓包 tcpdump -i eth0 -nn -S -vv host 10.18.222.22 and 10.18.101.91 and port 9188 and tcp ==1==客戶端10.18.101.91向服務端10.18.222.22發送一個SYN消息,seq為3105852613。 08:02:13.454857 IP (tos 0x0, ttl 126, id 14184, offset 0, flags [DF], proto TCP (6), length 52) 10.18.101.91.63376 > 10.18.222.22.9188: Flags [S], cksum 0x9640 (correct), seq 3105852613, win 8192, options [mss 1428,nop,wscale 8,nop,nop,sackOK], length 0 ==2==服務端向客戶端發送確認消息,其中ack為上一行seq+1,ack 3105852614;同時向客戶端發送SYN消息,seq 536849585。 08:02:13.454955 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52) 10.18.222.22.9188 > 10.18.101.91.63376: Flags [S.], cksum 0x57bc (incorrect -> 0xb057), seq 536849585, ack 3105852614, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0 ==3==客戶端向服務端發送確認消息,ack為上一行seq+1,ack 536849586。到此三次握手結束,連接建立。 08:02:13.455633 IP (tos 0x0, ttl 126, id 14185, offset 0, flags [DF], proto TCP (6), length 40) 10.18.101.91.63376 > 10.18.222.22.9188: Flags [.], cksum 0x2932 (correct), seq 3105852614, ack 536849586, win 256, length 0 ==4==客戶端向服務端發送數據,長度為161字節,請求序號seq 3105852614:3105852735 08:02:13.488717 IP (tos 0x0, ttl 126, id 14191, offset 0, flags [DF], proto TCP (6), length 161) 10.18.101.91.63376 > 10.18.222.22.9188: Flags [P.], cksum 0x836f (correct), seq 3105852614:3105852735, ack 536849586, win 256, length 121 ==5==服務端收到數據後向客戶端發送確認ack 3105852735 08:02:13.488816 IP (tos 0x0, ttl 64, id 44515, offset 0, flags [DF], proto TCP (6), length 40) 10.18.222.22.9188 > 10.18.101.91.63376: Flags [.], cksum 0x57b0 (incorrect -> 0x2946), seq 536849586, ack 3105852735, win 115, length 0 ==6==服務端向客戶端發送數據 08:02:19.534769 IP (tos 0x0, ttl 64, id 44516, offset 0, flags [DF], proto TCP (6), length 130) 10.18.222.22.9188 > 10.18.101.91.63376: Flags [P.], cksum 0x580a (incorrect -> 0xd5db), seq 536849586:536849676, ack 3105852735, win 115, length 90 ==7==客戶端向服務端發FIN消息 08:02:19.660406 IP (tos 0x0, ttl 126, id 14360, offset 0, flags [DF], proto TCP (6), length 40) 10.18.101.91.63376 > 10.18.222.22.9188: Flags [F.], cksum 0x285e (correct), seq 3105852735, ack 536849676, win 256, length 0 ==8==服務端向客戶端發送確認,同時發送FIN消息 08:02:19.661757 IP (tos 0x0, ttl 64, id 44517, offset 0, flags [DF], proto TCP (6), length 40) 10.18.222.22.9188 > 10.18.101.91.63376: Flags [F.], cksum 0x57b0 (incorrect -> 0x28ea), seq 536849676, ack 3105852736, win 115, length 0 ==9==客戶端向服務端發送確認。 08:02:19.662960 IP (tos 0x0, ttl 126, id 14362, offset 0, flags [DF], proto TCP (6), length 40) 10.18.101.91.63376 > 10.18.222.22.9188: Flags [.], cksum 0x285d (correct), seq 3105852736, ack 536849677, win 256, length 0 1、2、3是TCP三次握手的過程 7、8、9是TCP四次揮手的過程 疑問:為什麽TCP四次揮手只抓到3個包? TCP總是盡可能的捎帶需要回復給對方的數據
tcp連接狀態轉換圖
附錄:抓包工具----tcpdump
安裝:yum install -y tcpdump
查看幫助:tcpdump --help
-i 指定監聽的網絡接口(網卡)。
-w 直接將分組寫入文件中。
-vv 輸出詳細的報文信息。
-X,可以列出十六進制 (hex) 以及 ASCII 的數據包內容,對於監聽數據包內容很有用。
-A,數據包的內容以 ASCII 顯示,通常用來捉取 WWW 的網頁數據包資料。
-nn 不進行端口名稱的轉換。
-D 打印出系統中所有可以用tcpdump截包的網絡接口。
常用命令
1)我截取本機(192.168.31.147)和主機114.114.114.114之間的數據
tcpdump -n -i eth0 host 192.168.31.147 and 114.114.114.114
2)截取全部進入服務器的數據
tcpdump -n -i eth0 dst 192.168.31.147
服務器有多個IP 可以使用參數
tcpdump -n -i eth0 dst 192.168.31.147 or 192.168.31.157
3)抓取全部進入服務器的TCP數據包
tcpdump -n -i eth0 dst 192.168.31.147 and tcp
4)從本機出去的數據包
tcpdump -n -i eth0 src 192.168.31.147 or 192.168.31.157
5)從本機出去的數據包且端口不為22的tcp數據包
tcpdump -n -i eth0 src 192.168.31.147 or 192.168.31.157 and port ! 22 and tcp
參考資料:
就是要你懂 TCP
TCPdump抓包命令詳解
TCP(一)