轉自:https://www.cnblogs.com/zmlctt/p/3690998.htmlhtml
相對於SOCKET開發者,TCP建立過程和連接折除過程是由TCP/IP協議棧自動建立的.所以開發者並不須要控制這個過程.可是對於理解TCP底層運做機制,至關有幫助.linux
並且對於有網絡協議工程師之類筆試,幾乎是必考的內容.企業對這個問題熱情之高,出乎個人意料:-)。有時上午面試前強調這個問題,並重復講一次,下午幾乎每個人都被問到這個問題。面試
所以在這裏詳細解釋一下這兩個過程。編程
TCP三次握手ubuntu
所謂三次握手(Three-way Handshake),是指創建一個TCP鏈接時,須要客戶端和服務器總共發送3個包。windows
三次握手的目的是鏈接服務器指定端口,創建TCP鏈接,並同步鏈接雙方的序列號和確認號並交換 TCP 窗口大小信息.在socket編程中,客戶端執行connect()時。將觸發三次握手。
第一次握手:
客戶端發送一個TCP的SYN標誌位置1的包指明客戶打算鏈接的服務器的端口,以及初始序號X,保存在包頭的序列號(Sequence Number)字段裏。
第二次握手:
服務器發回確認包(ACK)應答。即SYN標誌位和ACK標誌位均爲1同時,將確認序號(Acknowledgement Number)設置爲客戶的I S N加1以.即X+1。
centos
第三次握手.
客戶端再次發送確認包(ACK) SYN標誌位爲0,ACK標誌位爲1.而且把服務器發來ACK的序號字段+1,放在肯定字段中發送給對方.而且在數據段放寫ISN的+1
SYN攻擊
在三次握手過程當中,服務器發送SYN-ACK以後,收到客戶端的ACK以前的TCP鏈接稱爲半鏈接(half-open connect).此時服務器處於Syn_RECV狀態.當收到ACK後,服務器轉入ESTABLISHED狀態.
Syn攻擊就是 攻擊客戶端 在短期內僞造大量不存在的IP地址,向服務器不斷地發送syn包,服務器回覆確認包,並等待客戶的確認,因爲源地址是不存在的,服務器須要不斷的重發直 至超時,這些僞造的SYN包將長時間佔用未鏈接隊列,正常的SYN請求被丟棄,目標系統運行緩慢,嚴重者引發網絡堵塞甚至系統癱瘓。
Syn攻擊是一個典型的DDOS攻擊。檢測SYN攻擊很是的方便,當你在服務器上看到大量的半鏈接狀態時,特別是源IP地址是隨機的,基本上能夠判定這是一次SYN攻擊.在Linux下能夠以下命令檢測是否被Syn攻擊
netstat -n -p TCP | grep SYN_RECV
通常較新的TCP/IP協議棧都對這一過程進行修正來防範Syn攻擊,修改tcp協議實現。主要方法有SynAttackProtect保護機制、SYN cookies技術、增長最大半鏈接和縮短超時時間等.
可是不能徹底防範syn攻擊。
TCP 四次揮手
TCP的鏈接的拆除須要發送四個包,所以稱爲四次揮手(four-way handshake)。客戶端或服務器都可主動發起揮手動做,在socket編程中,任何一方執行close()操做便可產生揮手操做。安全
參見wireshark抓包,實測的抓包結果並無嚴格按揮手時序。我估計是時間間隔過短形成。
注意上面的字段標號地段和發送接收的內容序號,可能有個有錯,記不住哪一個了,後頭要細看看服務器
第二部分:補充tcp鏈接過程cookie
在TCP/IP協議中,TCP協議提供可靠的鏈接服務,採用三次握手創建一個鏈接,如圖1所示。
(1) 第一次握手:創建鏈接時,客戶端A發送SYN包(SYN=j)到服務器B,並進入SYN_SEND狀態,等待服務器B確認。
(2) 第二次握手:服務器B收到SYN包,必須確認客戶A的SYN(ACK=j+1),同時本身也發送一個SYN包(SYN=k),即SYN+ACK包,此時服務器B進入SYN_RECV狀態。
(3) 第三次握手:客戶端A收到服務器B的SYN+ACK包,向服務器B發送確認包ACK(ACK=k+1),此包發送完畢,客戶端A和服務器B進入ESTABLISHED狀態,完成三次握手。
完成三次握手,客戶端與服務器開始傳送數據。
圖1 TCP三次握手創建鏈接
因爲TCP鏈接是全雙工的,所以每一個方向都必須單獨進行關閉。這個原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的鏈接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP鏈接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另外一方執行被動關閉。
(1)客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送(報文段4)。
(2)服務器B收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1(報文段5)。和SYN同樣,一個FIN將佔用一個序號。
(3)服務器B關閉與客戶端A的鏈接,發送一個FIN給客戶端A(報文段6)。
(4)客戶端A發回ACK報文確認,並將確認序號設置爲收到序號加1(報文段7)。
TCP採用四次揮手關閉鏈接如圖2所示。
圖2 TCP四次揮手關閉鏈接
1.爲何創建鏈接協議是三次握手,而關閉鏈接倒是四次握手呢?
這是由於服務端的LISTEN狀態下的SOCKET當收到SYN報文的鏈接請求後,它能夠把ACK和SYN(ACK起應答做用,而SYN起同步做用)放在一個報文裏來發送。但關閉鏈接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你全部的數據都所有發送給對方了,因此你可能未必會立刻會關閉SOCKET,也即你可能還須要發送一些數據給對方以後,再發送FIN報文給對方來表示你贊成如今能夠關閉鏈接了,因此它這裏的ACK報文和FIN報文多數狀況下都是分開發送的。
2.爲何TIME_WAIT狀態還須要等2MSL後才能返回到CLOSED狀態?
這個問題能夠參考《unix 網絡編程》(第三版,2.7 TIME_WAIT狀態)。
TIME_WAIT狀態由兩個存在的理由。
(1)可靠的實現TCP全雙工連接的終止。
這是由於雖然雙方都贊成關閉鏈接了,並且握手的4個報文也都協調和發送完畢,按理能夠直接回到CLOSED狀態(就比如從SYN_SEND狀態到ESTABLISH狀態那樣);可是由於咱們必需要假想網絡是不可靠的,你沒法保證你最後發送的ACK報文會必定被對方收到,所以對方處於LAST_ACK狀態下的SOCKET可能會由於超時未收到ACK報文,而重發FIN報文,因此這個TIME_WAIT狀態的做用就是用來重發可能丟失的ACK報文。
(2)容許老的重複的分節在網絡中消逝。
假 設在12.106.32.254的1500端口和206.168.1.112.219的21端口之間有一個TCP鏈接。咱們關閉這個連接,過一段時間後在 相同的IP地址和端口創建另外一個鏈接。後一個連接成爲前一個的化身。由於它們的IP地址和端口號都相同。TCP必須防止來自某一個鏈接的老的重複分組在連 接已經終止後再現,從而被誤解成屬於同一連接的某一個某一個新的化身。爲作到這一點,TCP將不給處於TIME_WAIT狀態的連接發起新的化身。既然 TIME_WAIT狀態的持續時間是MSL的2倍,這就足以讓某個方向上的分組最多存活msl秒即被丟棄,另外一個方向上的應答最多存活msl秒也被丟棄。 經過實施這個規則,咱們就能保證每成功創建一個TCP鏈接時。來自該連接先前化身的重複分組都已經在網絡中消逝了。
3. 爲何不能用兩次握手進行鏈接?
咱們知道,3次握手完成兩個重要的功能,既要雙方作好發送數據的準備工做(雙方都知道彼此已準備好),也要容許雙方就初始序列號進行協商,這個序列號在握手過程當中被髮送和確認。
如今把三次握手改爲僅須要兩次握手,死鎖是可能發生的。做爲例子,考慮計算機S和C之間的通訊,假定C給S發送一個鏈接請求分組,S收到了這個分組,併發 送了確認應答分組。按照兩次握手的協定,S認爲鏈接已經成功地創建了,能夠開始發送數據分組。但是,C在S的應答分組在傳輸中被丟失的狀況下,將不知道S 是否已準備好,不知道S創建什麼樣的序列號,C甚至懷疑S是否收到本身的鏈接請求分組。在這種狀況下,C認爲鏈接還未創建成功,將忽略S發來的任何數據分 組,只等待鏈接確認應答分組。而S在發出的分組超時後,重複發送一樣的分組。這樣就造成了死鎖。
補充:
a. 默認狀況下(不改變socket選項),當你調用close( or closesocket,如下說close再也不重複)時,若是發送緩衝中還有數據,TCP會繼續把數據發送完。
b. 發送了FIN只是表示這端不能繼續發送數據(應用層不能再調用send發送),可是還能夠接收數據。
c. 應用層如何知道對端關閉?一般,在最簡單的阻塞模型中,當你調用recv時,若是返回0,則表示對端關閉。在這個時候一般的作法就是也調用close,那麼TCP層就發送FIN,繼續完成四次握手。若是你不調用close,那麼對端就會處於FIN_WAIT_2狀態,而本端則會處於CLOSE_WAIT狀態。這個能夠寫代碼試試。
d. 在不少時候,TCP鏈接的斷開都會由TCP層自動進行,例如你CTRL+C終止你的程序,TCP鏈接依然會正常關閉,你能夠寫代碼試試。
插曲:
特別的TIME_WAIT狀態:
從以上TCP鏈接關閉的狀態轉換圖能夠看出,主動關閉的一方在發送完對對方FIN報文的確認(ACK)報文後,會進入TIME_WAIT狀態。TIME_WAIT狀態也稱爲2MSL狀態。
什麼是2MSL?MSL即Maximum Segment Lifetime,也就是報文最大生存時間,引用《TCP/IP詳解》中的話:「它(MSL)是任何報文段被丟棄前在網絡內的最長時間。」那麼,2MSL也就是這個時間的2倍。其實我以爲不必把這個MSL的確切含義搞明白,你所須要明白的是,當TCP鏈接完成四個報文段的交換時,主動關閉的一方將繼續等待必定時間(2-4分鐘),即便兩端的應用程序結束。你能夠寫代碼試試,而後用setstat查看下。
爲何須要2MSL?根據《TCP/IP詳解》和《The TCP/IP Guide》中的說法,有兩個緣由:
其一,保證發送的ACK會成功發送到對方,如何保證?我以爲多是經過超時計時器發送。這個就很難用代碼演示了。
其二,報文可能會被混淆,意思是說,其餘時候的鏈接可能會被看成本次的鏈接。直接引用《The TCP/IP Guide》的說法:The second is to provide a 「buffering period」 between the end of this connection and any subsequent ones. If not for this period, it is possible that packets from different connections could be mixed, creating confusion.
TIME_WAIT狀態所帶來的影響:
當某個鏈接的一端處於TIME_WAIT狀態時,該鏈接將不能再被使用。事實上,對於咱們比較有現實意義的是,這個端口將不能再被使用。某個端口處於TIME_WAIT狀態(其實應該是這個鏈接)時,這意味着這個TCP鏈接並無斷開(徹底斷開),那麼,若是你bind這個端口,就會失敗。對於服務器而言,若是服務器忽然crash掉了,那麼它將沒法再2MSL內從新啓動,由於bind會失敗。解決這個問題的一個方法就是設置socket的SO_REUSEADDR選項。這個選項意味着你能夠重用一個地址。
對於TIME_WAIT的插曲:
當創建一個TCP鏈接時,服務器端會繼續用原有端口監聽,同時用這個端口與客戶端通訊。而客戶端默認狀況下會使用一個隨機端口與服務器端的監聽端口通訊。有時候,爲了服務器端的安全性,咱們須要對客戶端進行驗證,即限定某個IP某個特定端口的客戶端。客戶端可使用bind來使用特定的端口。對於服務器端,當設置了SO_REUSEADDR選項時,它能夠在2MSL內啓動並listen成功。可是對於客戶端,當使
用bind並設置SO_REUSEADDR時,若是在2MSL內啓動,雖然bind會成功,可是在windows平臺上connect會失敗。而在linux上則不存在這個問題。(個人實驗平臺:winxp, ubuntu7.10)
要解決windows平臺的這個問題,能夠設置SO_LINGER選項。SO_LINGER選項決定調用close時TCP的行爲。SO_LINGER涉及到linger結構體,若是設置結構體中l_onoff爲非0,l_linger爲0,那麼調用close時TCP鏈接會馬上斷開,TCP不會將發送緩衝中未發送的數據發送,而是當即發送一個RST報文給對方,這個時候TCP鏈接就不會進入TIME_WAIT狀態。如你所見,這樣作雖然解決了問題,可是並不安全。經過以上方式設置SO_LINGER狀態,等同於設置SO_DONTLINGER狀態。
斷開鏈接時的意外:
這個算不上斷開鏈接時的意外,當TCP鏈接發生一些物理上的意外狀況時,例如網線斷開,linux上的TCP實現會依然認爲該鏈接有效,而windows則會在必定時間後返回錯誤信息。這彷佛能夠經過設置SO_KEEPALIVE選項來解決,不過不知道這個選項是否對於全部平臺都有效。
第三部分:常見面試題
- TCP協議和UDP協議的區別是什麼
- TCP協議是有鏈接的,有鏈接的意思是開始傳輸實際數據以前TCP的客戶端和服務器端必須經過三次握手創建鏈接,會話結束以後也要結束鏈接。而UDP是無鏈接的
- TCP協議保證數據按序發送,按序到達,提供超時重傳來保證可靠性,可是UDP不保證按序到達,甚至不保證到達,只是努力交付,即使是按序發送的序列,也不保證按序送到。
- TCP協議所需資源多,TCP首部需20個字節(不算可選項),UDP首部字段只需8個字節。
- TCP有流量控制和擁塞控制,UDP沒有,網絡擁堵不會影響發送端的發送速率
- TCP是一對一的鏈接,而UDP則能夠支持一對一,多對多,一對多的通訊。
- TCP面向的是字節流的服務,UDP面向的是報文的服務。
- TCP介紹和UDP介紹
- 請詳細介紹一下TCP協議創建鏈接和終止鏈接的過程?
- 三次握手創建鏈接時,發送方再次發送確認的必要性?
- 主 要是爲了防止已失效的鏈接請求報文段忽然又傳到了B,於是產生錯誤。假定出現一種異常狀況,即A發出的第一個鏈接請求報文段並無丟失,而是在某些網絡結 點長時間滯留了,一直延遲到鏈接釋放之後的某個時間纔到達B,原本這是一個早已失效的報文段。但B收到此失效的鏈接請求報文段後,就誤認爲是A又發出一次 新的鏈接請求,因而就向A發出確認報文段,贊成創建鏈接。假定不採用三次握手,那麼只要B發出確認,新的鏈接就創建了,這樣一直等待A發來數據,B的許多 資源就這樣白白浪費了。
- 四次揮手釋放鏈接時,等待2MSL的意義?
- 第 一,爲了保證A發送的最有一個ACK報文段可以到達B。這個ACK報文段有可能丟失,於是使處在LAST-ACK狀態的B收不到對已發送的FIN和ACK 報文段的確認。B會超時重傳這個FIN和ACK報文段,而A就能在2MSL時間內收到這個重傳的ACK+FIN報文段。接着A重傳一次確認。
- 第二,就是防止上面提到的已失效的鏈接請求報文段出如今本鏈接中,A在發送完最有一個ACK報文段後,再通過2MSL,就可使本鏈接持續的時間內所產生的全部報文段都從網絡中消失。
- 常見的應用中有哪些是應用TCP協議的,哪些又是應用UDP協議的,爲何它們被如此設計?
- 如下應用通常或必須用udp實現?
- 多播的信息必定要用udp實現,由於tcp只支持一對一通訊。
- 若是一個應用場景中大可能是簡短的信息,適合用udp實現,由於udp是基於報文段的,它直接對上層應用的數據封裝成報文段,而後丟在網絡中,若是信息量太大,會在鏈路層中被分片,影響傳輸效率。
- 若是一個應用場景重性能甚於重完整性和安全性,那麼適合於udp,好比多媒體應用,缺一兩幀不影響用戶體驗,可是須要流媒體到達的速度快,所以比較適合用udp
- 若是要求快速響應,那麼udp聽起來比較合適
- 若是又要利用udp的快速響應優勢,又想可靠傳輸,那麼只能考上層應用本身制定規則了。
- 常見的使用udp的例子:ICQ,QQ的聊天模塊。
- 以qq爲例的一個說明(轉載自知乎)
登錄採用TCP協議和HTTP協議,你和好友之間發送消息,主要採用UDP協議,內網傳文件採用了P2P技術。總來的說:
1.登錄過程,客戶端client 採用TCP協議向服務器server發送信息,HTTP協議下載信息。登錄以後,會有一個TCP鏈接來保持在線狀態。
2.和好友發消息,客戶端client採用UDP協議,可是須要經過服務器轉發。騰訊爲了確保傳輸消息的可靠,採用上層協議來保證可靠傳輸。若是消息發送失敗,客戶端會提示消息發送失敗,並可從新發送。
3.若是是在內網裏面的兩個客戶端傳文件,QQ採用的是P2P技術,不須要服務器中轉。