1、建立連線協定(三向交握)
(1)用戶端發送一個帶SYN標誌的TCP報文到伺服器。這是三向交握過程中的報文1。
(2) 伺服器端回應用戶端的,這是三向交握中的第2個報文,這個報文同時帶ACK標誌和SYN標誌。因此它表示對剛才用戶端SYN報文的回應;同時又標誌SYN給用戶端,詢問用戶端是否準備好進行資料通訊。
(3) 客戶必須再次回應服務段一個ACK報文,這是報文段3。
2、串連終止協議(四次揮手)
由於TCP串連是全雙工系統的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的資料發送任務後就能發送一個FIN來終止這個方向的串連。收到一個 FIN只意味著這一方向上沒有資料流動,一個TCP串連在收到一個FIN後仍能發送資料。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
(1) TCP用戶端發送一個FIN,用來關閉客戶到伺服器的資料傳送(報文段4)。
(2) 伺服器收到這個FIN,它發回一個ACK,確認序號為收到的序號加1(報文段5)。和SYN一樣,一個FIN將佔用一個序號。
(3) 伺服器關閉用戶端的串連,發送一個FIN給用戶端(報文段6)。
(4) 客戶段發回ACK報文確認,並將確認序號設定為收到序號加1(報文段7)。
CLOSED: 這個沒什麼好說的了,表示初始狀態。 LISTEN: 這個也是非常容易理解的一個狀態,表示伺服器端的某個SOCKET處於監聽狀態,可以接受串連了。
SYN_RCVD: 這個狀態表示接受到了SYN報文,在正常情況下,這個狀態是伺服器端的SOCKET在建立TCP串連時的三向交握會話過程中的一個中間狀態,很短暫,基本上用netstat你是很難看到這種狀態的,除非你特意寫了一個用戶端測試程式,故意將三次TCP握手過程中最後一個ACK報文不予發送。因此這種狀態時,當收到用戶端的ACK報文後,它會進入到ESTABLISHED狀態。
SYN_SENT: 這個狀態與SYN_RCVD遙想呼應,當用戶端SOCKET執行CONNECT串連時,它首先發送SYN報文,因此也隨即它會進入到了SYN_SENT狀態,並等待服務端的發送三向交握中的第2個報文。SYN_SENT狀態表示用戶端已發送SYN報文。
ESTABLISHED:這個容易理解了,表示串連已經建立了。
FIN_WAIT_1: 這個狀態要好好解釋一下,其實FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是:FIN_WAIT_1狀態實際上是當SOCKET在ESTABLISHED狀態時,它想主動關閉串連,向對方發送了FIN報文,此時該SOCKET即進入到FIN_WAIT_1狀態。而當對方回應ACK報文後,則進入到FIN_WAIT_2狀態,當然在實際的正常情況下,無論對方何種情況下,都應該馬上回應ACK報文,所以FIN_WAIT_1狀態一般是比較難見到的,而FIN_WAIT_2狀態還有時常常可以用netstat看到。
FIN_WAIT_2:上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2狀態下的SOCKET,表示半串連,也即有一方要求close串連,但另外還告訴對方,我暫時還有點資料需要傳送給你,稍後再關閉串連。
TIME_WAIT: 表示收到了對方的FIN報文,並發送出了ACK報文,就等2MSL後即可回到CLOSED可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶 FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。
CLOSING: 這種狀態比較特殊,實際情況中應該是很少見,屬於一種比較罕見的例外狀態。正常情況下,當你發送FIN報文後,按理來說是應該先收到(或同時收到)對方的 ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示你發送FIN報文後,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼情況下會出現此種情況呢?其實細想一下,也不難得出結論:那就是如果雙方几乎在同時close一個SOCKET的話,那麼就出現了雙方同時發送FIN報文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET串連。
CLOSE_WAIT: 這種狀態的含義其實是表示在等待關閉。怎麼理解呢?當對方close一個SOCKET後發送FIN報文給自己,你系統毫無疑問地會回應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正需要考慮的事情是察看你是否還有資料發送給對方,如果沒有的話,那麼你也就可以 close這個SOCKET,發送FIN報文給對方,也即關閉串連。所以你在CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉串連。
LAST_ACK: 這個狀態還是比較容易好理解的,它是被動關閉一方在發送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也即可以進入到CLOSED可用狀態了。
最後有2個問題的回答,我自己分析後的結論(不一定保證100%正確)
1、 為什麼建立連線協定是三向交握,而關閉串連卻是四次握手呢?這是因為服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一個報文裡來發送。但關閉串連時,當收到對方的FIN報文通知時,它僅僅表示對方沒有資料發送給你了;但未必你所有的資料都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些資料給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉串連了,所以它這裡的ACK報文和FIN報文多數情況下都是分開發送的。
2、 為什麼TIME_WAIT狀態還需要等2MSL後才能返回到CLOSED狀態?這是因為:雖然雙方都同意關閉串連了,而且握手的4個報文也都協調和發送完畢,按理可以直接回到CLOSED狀態(就好比從SYN_SEND狀態到 ESTABLISH狀態那樣);但是因為我們必須要假想網路是不可靠的,你無法保證你最後發送的ACK報文會一定被對方收到,因此對方處於 LAST_ACK狀態下的SOCKET可能會因為逾時未收到ACK報文,而重發FIN報文,所以這個TIME_WAIT狀態的作用就是用來重發可能丟失的 ACK報文。
(轉載)
發現很多情況下,MSN傳輸檔案比QQ要慢,倒不是說msn沒有快的時候,但是大部分的時候是真的比QQ慢,連我這種神經比較大條的人都注意到了,Google了一下,早就有人做瞭解答,基本上就是說msn傳輸檔案是使用TCP,而QQ使用UDP,剩下的事情就是論證TCP傳輸檔案沒有UDP快。大概的就是下面的幾個觀點:
1. TCP是可靠的,需要驗證資料是否到達和是否正確,而UDP是不可靠的,少做了很多事情,所以MSN的檔案傳輸比QQ慢。
我看了當時就想笑,也用了QQ不少時日了,從來也沒有發現傳輸檔案有問題的,用UDP作協議也很久了,不做應用程式層驗證重傳的代碼,我還真不敢寫。這個理由,失敗。
2. TCP建立串連需要3次握手,而UDP不需要,所以TCP慢。
3次握手這個事實倒是千真萬確,還好我沒有那麼容易被忽悠,兩個人談話之前要握握手的確要稍微費上幾秒鐘,但是這個關談話的語速啥事情?假如網路的ping值達到300ms,各位看官喜歡網路遊戲的,估計都不會玩了,否則垂死的boss會高興的發現你忽然變成了木偶可以隨便毆打不還手,最後你只能罵罵電信網通然後複活幾分鐘後又是一條好漢。但是對於TCP,這樣的ping值,3次握手一般都不需要1秒鐘,把這個定為檔案慢慢傳的罪魁禍首,似乎太不靠譜了,這個理由還是失敗。
3. TCP一旦建立連結,路由就確定了,而UDP是不確定的路由方式,誰速度快走誰的線路。
這樣說就說明沒有作者好好理解TCP/IP協議了,TCP的鏈路只是一個邏輯的,又沒有建立物理鏈路,下面跑的還是IP包,這個包走這條路,那個包完全可能走另外的路,這點TCP和UDP沒有兩樣。理由繼續失敗。
4. msn伺服器在國外?
有些道理,但是我聽一個美國的朋友說他也喜歡用QQ傳檔案的。
那到底是怎麼回事呢?是因為微軟沒有做好?(題外話,個人的確覺得MSN相比QQ的飛速進步而顯得動作遲緩)QQ的Fans開始摩拳擦掌,一些不那麼喜歡M$的估計就要開始丟板磚了。不管立場如何,事實還是要探尋一下,本著不求甚解,薄積薄發,淺入淺出的精神,我認為有幾個可能原因:
1. 兩個傳檔案用戶端都在NAT後面的時候 (你不知道NAT啥意思?比如多個人通過路由器共用一個貓上網,那麼你們一般就是在NAT後面了),從技術實現上講,TCP在這種情況下穿越NAT比UDP麻煩得多。UDP只要開始幾個穿越NAT的協商包需要伺服器轉一下,後面的檔案資料可以兩個用戶端之間直接傳輸搞定,但是一般TCP就只能全程由伺服器中轉了,你說哪一個會比較快? 為什麼TCP需要伺服器中轉?先看看NAT吧,聽說有高人可以用raw sock搞定,反正我沒有中間伺服器搞不定。
2. 但是即使上面的條件不成立,msn還是一般比QQ慢的。問題還是在出在前面提到的驗證資料可靠性上面,TCP是可靠的,UDP是不可靠的,但是用UDP做傳輸檔案這檔子事情,肯定要在應用程式層寫一個驗證的協議,否則傳過來的檔案缺胳膊少腿,會被使用者罵死的。說是協議,其實也不難,打個比方吧:
long long ago,賈寶玉在北京,林黛玉在長沙,怎麼互訴衷腸呢,派家丁送信!路途遙遠,怎麼知道信收到沒有?打電話問?那時候發明了這個就不用送信了,只能看家丁是否帶了回信來了。假如發現一個家丁一個月還沒有回,那就多半迷路堵車遭遇山賊或者開小差到揚州花差快活去了,再派一個人送吧! TCP就是這麼做的,UDP在應用程式層協議一般也需要這麼做,但是實現上有時候往往有區別。
北京到長沙之間的路,並不是只有一個人跑的,常常很擁堵,假如發現家丁好久沒有回了,TCP版的賈寶玉再派人送信是要的,但是他會比較識大體,他會少寫信,降低發送速度,原來一天一封,現在可能一周一封了。他想,大家假如都這樣,路就不會那麼擁擠了。這做法很有道理,假如塞車了大家都想見縫插針,只能越來越塞,最後大家都動不了,還不如彼此容讓慢慢排隊。而UDP版本的賈寶玉假如也這麼集體主義,那麼他就叫做TCP友好流,就假如它只管自己拚命擠,就是非TCP友好的。
所有的TCP協議假如發現擁塞,都會馬上降低自己的發送速度。假如基於UDP的協議不這麼做的話,原來擁塞的IP包加上重發的包,網路只會越來越擁塞,最後所有的堅持集體主義的TCP流都會做出犧牲,把頻寬讓給一些非TCP友好的UDP流。所以除非網路狀況非常好,否則TCP是拼不過非TCP友好的協議的。
我懷疑(僅僅是懷疑而已,我也沒有條件和時間驗證),QQ的檔案傳輸機制是不那麼TCP友好的,它搶頻寬更加"我能",這樣雖然對於整個網路負荷不是什麼好事,但是對於單個使用者,就見仁見智了,就好像大家看待多線程下載或者p2p一樣。