一、TCP/IP概念模型與OSI參考模型對比
OSI參考模型只是一個理想的概念,分為7層,從最底層的物理層、鏈路層、網(wǎng)絡層、傳輸層、會話層、表示層、應用層,但是TCP/IP的概念模型一般分為四層,即鏈路層、網(wǎng)絡層、傳輸層、應用層。
OSI中的物理層和鏈路層被歸為鏈路層,網(wǎng)絡層和傳輸層不變,最后三層統(tǒng)稱應用層。

在具體實現(xiàn)中,一般都是TCP/IP模型,分成四層:應用層、傳輸層、網(wǎng)絡層、鏈路層
1.TCP/IP協(xié)議并不是必須從應用層->傳輸層->網(wǎng)絡層的。

這里的ping命令其實就沒有經(jīng)過傳輸層,而是直接從應用層到了網(wǎng)絡層
2.TCP和UDP的區(qū)別
- 1、TCP面向連接(如打電話要先撥號建立連接);UDP是無連接的,即發(fā)送數(shù)據(jù)之前不需要建立連接
- 2、TCP提供可靠的服務。也就是說,通過TCP連接傳送的數(shù)據(jù),無差錯,不丟失,不重復,且按序到達;UDP盡最大努力交付,即不保證可靠交付
Tcp通過校驗和,重傳控制,序號標識,滑動窗口、確認應答實現(xiàn)可靠傳輸。如丟包時的重發(fā)控制,還可以對次序亂掉的分包進行順序控制。 - 3、UDP具有較好的實時性,工作效率比TCP高,適用于對高速傳輸和實時性有較高的通信或廣播通信。
- 4、每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信
- 5、TCP對系統(tǒng)資源要求較多,UDP對系統(tǒng)資源要求較少。
TCP連接通過三次握手確保連接的可靠性。
3.TCP特性
- 面向連接的
- 可靠性
- RTT(往返時延,Round-Trip Time)和RTO(重傳超時,Retransmission TimeOut)
- 數(shù)據(jù)排序
- 流量控制
- 全雙工
流量控制:滑動窗口,其實很簡單,就是每次tcp傳輸過程中,server和client會攜帶一個滑動窗口大小,意味著此時它能一次接受多少數(shù)據(jù),因為數(shù)據(jù)在客戶端和服務器之間是有緩存的,滑動窗口也就相當于對方告訴自己目前緩沖區(qū)的大小,不需要每次發(fā)一個數(shù)據(jù)包就回一個ACK,通過滑動窗口來進行簡單的擁塞控制。 - 發(fā)送方和接收方都會維護一個數(shù)據(jù)幀的序列,這個序列被稱為窗口
- 發(fā)送方的窗口大小由接收方確認
比如第一次客戶端向服務端發(fā)送了1000個數(shù)據(jù),但是服務端處理不過來,則在確認應答的時候,告訴客戶端不需要一次發(fā)送那么多數(shù)據(jù),比如發(fā)送500就可以了。這也可以調整大,比如變成3000
使用滑動窗口的目的:
(1)確保數(shù)據(jù)不丟失
如果發(fā)送的數(shù)據(jù)丟失了可以重新發(fā)送
(2)控制發(fā)送速度
控制發(fā)送速度,以免接收方的緩存不夠大導致溢出,同時控制流量也可以避免網(wǎng)絡擁塞
4.TCP/IP四層模型,網(wǎng)絡層和傳輸層有什么區(qū)別?網(wǎng)絡層的主要工作是什么?
網(wǎng)絡層負責ip數(shù)據(jù)報的產(chǎn)生以及ip數(shù)據(jù)包在邏輯網(wǎng)絡上的路由轉發(fā)。
網(wǎng)絡層只是根據(jù)網(wǎng)絡地址將源結點發(fā)出的數(shù)據(jù)包傳送到目的結點(點到點),其主要任務是:通過路由選擇算法,為報文或分組通過通信子網(wǎng)選擇最適當?shù)穆窂?。該層控制?shù)據(jù)鏈路層與傳輸層之間的信息轉發(fā),建立、維持和終止網(wǎng)絡的連接。具體地說,數(shù)據(jù)鏈路層的數(shù)據(jù)在這一層被轉換為數(shù)據(jù)包,然后通過路徑選擇、分段組合、順序、進/出路由等控制,將信息從一個網(wǎng)絡設備傳送到另一個網(wǎng)絡設備。
傳輸層提供端到端通信服務層次,提供可靠及非可靠連接,TCP和UDP。
傳輸層則負責將數(shù)據(jù)可靠地傳送到相應的端口(端到端),傳輸層提供了主機應用程序進程之間的端到端的服務。傳輸層利用網(wǎng)絡層提供的服務,并通過傳輸層地址提供給高層用戶傳輸數(shù)據(jù)的通信端口,使高層用戶看到的只是在兩個傳輸實體間的一條端到端的、可由用戶控制和設定的、可靠的數(shù)據(jù)通路。
二、TCP連接的三次握手
第一次握手
建立連接??蛻舳税l(fā)送連接請求報文段,將SYN位置為1,Seq序列號(Sequence Number)設置為x(系統(tǒng)隨機生成的);然后,客戶端進入SYN_SEND狀態(tài),等待服務器的確認;
第一次握手,就是客戶端向服務器發(fā)送一個請求連接的報文,等待服務器確認,客戶端進入SYN_SEND狀態(tài)。
第二次握手
服務器收到SYN報文段。服務器收到客戶端的SYN報文段,需要對這個SYN報文段進行確認,設置Ack確認序列號(Acknowledgment Number)為x+1(Sequence Number+1),這個是Ack確認序列號,確認序列號都是為收到的報文的序列號+1得到的;同時,自己自己還要發(fā)送SYN請求信息,將SYN標志位置為1,并且將ACK標志位也置為1,Seq序列號(Sequence Number)為y(Seq為y,Seq系統(tǒng)隨機生成的);服務器端將上述所有信息放到一個報文段(即SYN+ACK報文段)中,一并發(fā)送給客戶端,此時服務器進入SYN_RECV狀態(tài);客戶端進入ESTABLISHED狀態(tài)(established)。這是服務端請求與客戶端建立連接
第二次握手,就是服務器收到客戶端的報文段,對報文確認,然后服務器向客戶端發(fā)送一個SYN+ACK報文請求信息,服務器進入SYN_RECV狀態(tài)。這是服務端請求與客戶端建立連接
第三次握手
客戶端收到服務器的SYN+ACK報文段,檢查服務端發(fā)送過來的報文段ACK標志位是否為1,ack確認序列號是否為x+1。如果正確,然后將Ack確認序列號(Acknowledgment Number)設置為y+1,這個y是客戶端收到服務器端發(fā)送過來的報文的序列號y,將ACK標志位置為1,向服務器發(fā)送ACK報文段。這個報文段發(fā)送完畢以后,服務端接收到報文,檢查ACK標志位是否為1,且ack確認序列號是否為y+1,如果正確則連接建立成功,此時客戶端和服務器端都進入ESTABLISHED狀態(tài),完成TCP三次握手。第三次握手時,客戶端發(fā)送給服務端的報文的的SYN標志位為0
第三次握手,是客戶端回應服務器的ACK報文信息,向服務器發(fā)送一個確認。
為什么要進行三次握手
為了防止已失效的連接請求報文段突然又傳送到了服務端,因而產(chǎn)生錯誤。其實就是為了解決可靠性。三次握手發(fā)送的報文段都會有一個序號,三次握手就是為了交換TCP的初始序號,即客戶端發(fā)一個Seq=X,服務端就發(fā)送確認信號Ack為X+1。客戶端發(fā)送的連接,并不是先發(fā)的就先到,所以要避免混亂就需要通過序號和確認序號。三次握手,其實就是客戶端和服務端都需要將自己的初始序列號告訴對方,并且收到對方的確認信息。
TCP是面向連接的,所以需要雙方都確認連接的建立。如果是兩次連接,那么客戶端與服務端建立了連接,但是服務端向客戶端進行連接的時候,服務端就無法收到客戶端的確認信息,這樣服務端與客戶端的連接就沒有建立。
這里的初始序列號,那么下次進行通信的時候,就是從該初始序列號加上當前要發(fā)送的數(shù)據(jù)的長度,比如客戶端的初始序列號seq=500,要發(fā)送500bytes字節(jié)數(shù)據(jù),那么下一次客戶端要發(fā)送的數(shù)據(jù)的序列號就是從511開始
三次握手的過程就是通信雙方互相告知初始序列號的值,并且確認對方已經(jīng)收到了初始序列號值的一個步驟。
如果只進行兩次握手,至多只有連接發(fā)起方的初始序列號能被確認,而服務端的初始序列號并不能被客戶端確認,這樣客戶端就無法知道服務端的初始序列號,則無法正常接收對應的數(shù)據(jù)序列。
防止服務器端因為接收了早已失效的連接請求報文從而一直等待客戶端請求,從而浪費資源
- “已失效的連接請求報文段”的產(chǎn)生在這樣一種情況下:Client發(fā)出的第一個連接請求報文段并沒有丟失,而是在某個網(wǎng)絡結點長時間的滯留了,以致延誤到連接釋放以后的某個時間才到達server。
- 這是一個早已失效的報文段。但Server收到此失效的連接請求報文段后,就誤認為是Client再次發(fā)出的一個新的連接請求。
- 于是就向Client發(fā)出確認報文段,同意建立連接。
- 假設不采用“三次握手”:只要Server發(fā)出確認,新的連接就建立了。
- 由于現(xiàn)在Client并沒有發(fā)出建立連接的請求,因此不會向Server發(fā)送數(shù)據(jù)。
- 但Server卻以為新的運輸連接已經(jīng)建立,并一直等待Client發(fā)來數(shù)據(jù)。>- 這樣,Server的資源就白白浪費掉了。
如果不采用三次握手,那么服務端接收一個早已經(jīng)失效的報文并且確認,此時就會認為連接已經(jīng)確認,但是因為客戶端這個時候并沒有發(fā)送一個有效的連接報文,而是服務端接收了一個失效的連接報文,這樣就可能客戶端認為沒連接,而服務端認為客戶端已經(jīng)連接,造成服務端在等待客戶端發(fā)來的數(shù)據(jù),導致服務端的資源浪費。
因為tcp是全雙工,為保證傳輸?shù)目煽啃?,需要給每次傳輸?shù)臄?shù)據(jù)段添加序號,那么初始的序列號就是tcp三次握手真正的意義所在,而為了確保交換雙方的初始序號,最少需要三次才行
全雙工、半雙工、單工的解釋
全雙工:客戶端能給服務端發(fā)數(shù)據(jù),服務端同時也可以給客戶端發(fā)數(shù)據(jù)
半雙工:客戶端給服務端發(fā)數(shù)據(jù)的時候,服務端只能接收;服務端接收完成之后,可以給客戶端發(fā),但是這個時候客戶端就不能給服務端發(fā)
單工:只能客戶端給服務端發(fā)
TCP連接是全雙工的。
三次握手的漏洞
SYN洪泛攻擊:通過網(wǎng)絡服務所在的端口發(fā)送大量偽造原地址的攻擊報文,發(fā)送到服務端,造成服務端上的半開連接隊列被占滿,從而阻止其他用戶進行訪問。
原理:攻擊者客戶端利用偽造的IP地址向服務端發(fā)出請求(第一次握手),而服務的響應(第二次握手)的報文將永遠發(fā)送不到真實的客戶端,服務端在等待客戶端的第三次握手(永遠都不會有),服務端在等待這種半開的連接過程中消耗了資源,如果有成千上萬的這種連接,主機資源將耗盡,從而達到攻擊的目的。
解決的方案:
- 無效連接監(jiān)控釋放
- 延緩TCB分配方法
- 防火墻(最好的方案)
SYN洪泛攻擊,一般服務端向客戶端發(fā)起應答請求的時候,是需要客戶端的ip,而洪泛攻擊就是偽造這個ip,往服務端發(fā)送第一次握手的數(shù)據(jù),由于這第一次握手的客戶端ip是偽造的,那么服務端往客戶端進行第二次握手的時候由于客戶端ip是偽造的,則第二次握手就沒有結果,所以容易讓服務器被拖累,造成死機等問題。
什么時候可以給服務端發(fā)送數(shù)據(jù)?
其實當客戶端收到服務端的確認報文之后和請求建立連接的握手之后,向服務端發(fā)送第三次連接的時候,就可以帶有數(shù)據(jù)發(fā)送給服務端。因為三次握手只需要交換完了初始序列號之后就可以發(fā)送數(shù)據(jù)了。
最早可以給服務端發(fā)送數(shù)據(jù),必須是在第三次握手的時候,由客戶端向服務端發(fā)送服務端連接的確認信息。
但是如果TCP三次握手連接成功之后,雙方一直不發(fā)送數(shù)據(jù),那么在一定時間之后,TCP連接就會斷開,那么下次就需要重新進行連接
總結
從三次握手,可以總結看出,每次確認的確認序列號都是為收到的報文的序列號+1,SYN是在第一次和第二次的時候為1,ACK是在第二次和第三次的時候為1。
第一次SYN為1是為了讓服務器端確認,第二次為1是因為這個值需要客戶端來修改,ACK第二次為1是為了讓客戶端確認,第三次為1,是為了讓服務器端確認來進行連接。
Socket的三次握手

這里要注意Socket的connect()和accept()函數(shù)分別在三次握手的第幾次后返回?
connect在第二次握手,accept在第三次握手。
第一次握手時,創(chuàng)建連接,connect處于阻塞狀態(tài),等待服務器accept,第二次握手成功,connect從阻塞返回,accept同樣的需要收到客戶端ack以后才從阻塞返回,連接建立完成。在accept接收到客戶端發(fā)送過來的第一次握手報文的時候,此時accept是處于阻塞狀態(tài),并且返回一個確認信息和請求連接信息給客戶端,客戶端connect接收到后,返回信息給服務端,accept等到接收到客戶端返回過來的這個報文信息后,就會返回。
這個過程,其實可以認為是客戶端在第一個握手的時候調用connect視圖連接服務端,這個時候客戶端因為connect處于阻塞狀態(tài),等待服務端的accept返回,第二次握手的時候,服務端返回信息,客戶端接收到之后,connect就會從阻塞狀態(tài)返回,然后向服務端發(fā)送ack確認請求進行第三次握手,當服務端收到確認信息之后,accept就會從阻塞狀態(tài)返回。
accept是在發(fā)送第二次握手報文的時候,進入阻塞狀態(tài)。connect是客戶端在發(fā)送第一次握手報文的時候進入阻塞狀態(tài)。

三、TCP連接的四次揮手

第一次揮手
主機1(可以使客戶端,也可以是服務器端),設置Sequence Number,向主機2發(fā)送一個FIN報文段(其實就是標志位FIN=1),此時也會發(fā)送一個seq序列號,該序列號的值是隨機的,比如是x;此時,主機1進入FIN_WAIT_1狀態(tài);這表示主機1沒有數(shù)據(jù)要發(fā)送給主機2了;發(fā)送FIN包
第二次揮手
主機2收到了主機1發(fā)送的FIN報文段,向主機1回一個ACK報文段,ACK標志位為1,Acknowledgment Number為Sequence Number加1,即確認序列號ack=x+1;主機1收到主機2的應答報文后,進入FIN_WAIT_2狀態(tài);主機2告訴主機1,我“同意”你的關閉請求;此時主機2進入CLOSE_WAIT狀態(tài),此時與后面的CLOSED狀態(tài)還是有區(qū)別,服務端此時還沒有真的關閉。服務端進入CLOSE_WAIT狀態(tài),表示當前自己是半關閉狀態(tài),即對端已經(jīng)沒有數(shù)據(jù)發(fā)送給自身,自身不需要接收數(shù)據(jù)了,但是自身與對端之間的連接還并沒有關閉,因為此時服務端還可能有數(shù)據(jù)發(fā)送給客戶端,還與客戶端保持連接。需要服務端向客戶端發(fā)起第三次揮手,告訴客戶端需要關閉服務端與客戶端之間的連接,然后客戶端確認之后,服務端才會進入CLOSED狀態(tài)
第三次揮手
主機2向主機1發(fā)送FIN報文段(即標志位FIN=1),并且發(fā)送一個序列號seq,序列號的值是隨機的,比如seq=y,并且還會發(fā)送一個確認序列號,該確認序列號其實是與第二次分手時的確認序列號一直,即ack=x+1,請求關閉連接,同時主機2進入LAST_ACK狀態(tài);發(fā)送FIN包,服務端進入LAST_ACK狀態(tài),是要等待客戶端的確認包
第四次揮手
主機1收到主機2發(fā)送的FIN報文段,向主機2發(fā)送ACK報文段(標志位ACK=1),并且發(fā)送序列號,該序列號其實就是與第三次分手時的確認序列號一直,即seq=x+1,并且發(fā)送一個確認序列號,該確認序列號為第三次分手時的序列號+1,即ack=y+1,然后主機1進入TIME_WAIT狀態(tài);主機2收到主機1的ACK報文段以后,就關閉連接,進入CLOSED狀態(tài);此時,主機1等待2MSL后依然沒有收到回復,則證明Server端已正常關閉,那好,主機1也可以關閉連接了,進入CLOSED狀態(tài)。
比如:當主機1發(fā)送關閉請求,并且主機2確認了該關閉請求后,主機1不能發(fā)送數(shù)據(jù),但是可以繼續(xù)接收數(shù)據(jù)。只有當主機2發(fā)送了關閉請求,并且主機1確認關閉進入CLOSED狀態(tài)后,主機1才不能接收數(shù)據(jù)。
1.為什么要四次分手
TCP協(xié)議是一種面向連接的、可靠的、基于字節(jié)流的運輸層通信協(xié)議。TCP是全雙工模式,這就意味著,當主機1發(fā)出FIN報文段時,只是表示主機1已經(jīng)沒有數(shù)據(jù)要發(fā)送了,主機1告訴主機2,它的數(shù)據(jù)已經(jīng)全部發(fā)送完畢了;但是,這個時候主機1還是可以接受來自主機2的數(shù)據(jù);當主機2返回ACK報文段時,表示它已經(jīng)知道主機1沒有數(shù)據(jù)發(fā)送了,但是主機2還是可以發(fā)送數(shù)據(jù)到主機1的;當主機2也發(fā)送了FIN報文段時,這個時候就表示主機2也沒有數(shù)據(jù)要發(fā)送了,就會告訴主機1,我也沒有數(shù)據(jù)要發(fā)送了,之后彼此就會愉快的中斷這次TCP連接。
全雙工:其實就是客戶端可以向服務端發(fā)送,服務端也可以同時向客戶端發(fā)送,是有兩條連接。
揮手要四次:主要是客戶端和服務端都需要分別關閉請求,而關閉請求發(fā)送出去都需要接收到對方的同意,這樣兩個關閉請求,兩個同意就四次揮手。
四次揮手有可能是服務端發(fā)起的,也有可能是客戶端發(fā)起的。
四次揮手是有可能變成三次的。第二次跟第三次可以合并為一個一次性發(fā)送給客戶端。但是這樣的情況比較小。在主機2發(fā)送確認報文給主機1的時候,當主機2沒有額外的報文發(fā)送給主機1了,那么第二次和第三次分手有可能合并。
而FIN報文也是有可能與數(shù)據(jù)一起發(fā)送,比如主機1發(fā)送一個數(shù)據(jù)給主機2,此時該數(shù)據(jù)是最后一次發(fā)送,那么也有可能同時發(fā)送FIN報文。
但是第一次分手也有可能隨著最后一次數(shù)據(jù)一起發(fā)送
2.四次揮手的狀態(tài)轉移:
客戶端向服務端發(fā)送第一次揮手:客戶端從established->FIN_WAIT_1狀態(tài)
服務端向客戶端發(fā)送第二次揮手:客戶端從FIN_WAIT_1->FIN_WAIT_2狀態(tài)
服務端向客戶端發(fā)送第三次揮手:服務端從established->LAST_ACK狀態(tài),
客戶端向服務端發(fā)送第四次揮手:客戶端從FIN_WAIT_2->TIME_WAIT狀態(tài),此時客戶端必須在此停留超過2MSL時間之后,客戶端就默認斷開。一來一回最大的時間
為什么是要兩個MSL時間?
- 保證TCP協(xié)議的全雙工連接能夠可靠關閉
- 保證這次連接的重復數(shù)據(jù)段從網(wǎng)絡中消失
解釋一:
如果在兩個MSL時間內(nèi),比如第四次揮手丟了,那么對方就需要在超時后重發(fā)第三次揮手的FIN包,客戶端接收到服務端的重發(fā)的FIN包之后,可以再發(fā)一個ACK確認包。如果服務端沒收到客戶端最后發(fā)過來的這個ACK確認包,那么這一來一回,即客戶端接收一個重發(fā)的FIN包,又重發(fā)一個ACK確認包給服務端,這樣的一來一回,就是兩個MSL時間。
客戶端在兩個MSL時間內(nèi),是不能使用的,需要等到2MSL時間結束之后才可以繼續(xù)使用。
解釋二:
這里規(guī)定,在一端處于TIME_WAIT狀態(tài)的時候,其端口是不允許建立連接的。
如果Client直接CLOSED,而不在TIME_WAIT等待2*MSL時間,此時又有一個客戶端又再向Server發(fā)起一個新連接,我們不能保證這個新連接與剛關閉的連接的端口號是不同的。也就是說有可能新連接和老連接的端口號是相同的。一般來說不會發(fā)生什么問題,但是還是有特殊情況出現(xiàn):假設新連接和已經(jīng)關閉的老連接端口號是一樣的,如果前一次連接的某些數(shù)據(jù)仍然滯留在網(wǎng)絡中,這些延遲數(shù)據(jù)在建立新連接之后才到達Server,由于新連接和老連接的端口號是一樣的,又因為TCP協(xié)議判斷不同連接的依據(jù)是socket pair,于是,TCP協(xié)議就認為那個延遲的數(shù)據(jù)是屬于新連接的,這樣就和真正的新連接的數(shù)據(jù)包發(fā)生混淆了。所以TCP連接還要在TIME_WAIT狀態(tài)等待2倍MSL,這樣可以保證本次連接的所有數(shù)據(jù)都從網(wǎng)絡中消失。即前一個連接的延遲數(shù)據(jù)包,要到達服務端,然后服務端要響應該延遲數(shù)據(jù),這樣的一來一回才會最終消失數(shù)據(jù)段。
MSL:Max Seqment Lifetime,報文最大生存時間(RFC定義為2分鐘,大部分操作系統(tǒng)只有30秒)
TIME_WAIT時間,一般是1~4分鐘。
四次分手發(fā)起者
其實分手的主動發(fā)起者并不一定是客戶端,客戶端和服務端都是可以主動發(fā)起分手。