一. 預(yù)備工作
測試server代碼
import socket
import sys
import os
addr = ('127.0.0.1', 9988)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(addr)
server.listen(10)
while True:
connection, address = server.accept()
print 'connection ip:', address
測試client代碼
from socket import *
import time
addr = ('127.0.0.1', 9988)
client = socket(AF_INET, SOCK_STREAM)
client.connect(addr)
抓包命令
tcpdump -i lo port 9988 -S
抓包數(shù)據(jù)
16:34:33.611019 IP localhost.41631 > localhost.nsesrvr: Flags [S], seq 2970024578, win 32792, options [mss 16396,sackOK,TS val 514254463 ecr 0,nop,wscale 7], length 0
16:34:33.611035 IP localhost.nsesrvr > localhost.41631: Flags [S.], seq 2032818397, ack 2970024579, win 32768, options [mss 16396,sackOK,TS val 514254463 ecr 514254463,nop,wscale 7], length 0
16:34:33.611045 IP localhost.41631 > localhost.nsesrvr: Flags [.], ack 2032818398, win 257, options [nop,nop,TS val 514254463 ecr 514254463], length 0
# == 三次握手結(jié)束==
# == 四次揮手開始==
16:34:33.611150 IP localhost.nsesrvr > localhost.41474: Flags [F.], seq 507613731, ack 3023763844, win 256, options [nop,nop,TS val 514254463 ecr 514208493], length 0
16:34:33.611163 IP localhost.41474 > localhost.nsesrvr: Flags [.], ack 507613732, win 257, options [nop,nop,TS val 514254463 ecr 514254463], length 0
16:34:33.612059 IP localhost.41631 > localhost.nsesrvr: Flags [F.], seq 2970024579, ack 2032818398, win 257, options [nop,nop,TS val 514254464 ecr 514254463], length 0
16:34:33.612718 IP localhost.nsesrvr > localhost.41631: Flags [.], ack 2970024580, win 256, options [nop,nop,TS val 514254465 ecr 514254464], length 0
二. 三次握手
流程圖
中間的標(biāo)志位
S=SYN,發(fā)起連接標(biāo)志袄秩。
P=PUSH阵翎,傳送數(shù)據(jù)標(biāo)志。
F=FIN之剧,關(guān)閉連接標(biāo)志郭卫。
ack,表示確認(rèn)包背稼。
RST=RESET贰军,異常關(guān)閉連接。
.蟹肘,表示沒有任何標(biāo)志词疼。
1.三次握手具體分析
- 第1行:16:34:33.611019,從localhost(client)的臨時端口41631向localhost.nsesrvr(server)的9988監(jiān)聽端口發(fā)起連接帘腹,client初始包序號seq為2970024578贰盗,滑動窗口大小為32792字節(jié)(滑動窗口即tcp接收緩沖區(qū)的大小,用于tcp擁塞控制)阳欲,mss大小為16396(即可接收的最大包長度舵盈,通常為MTU減40字節(jié)陋率,IP頭和TCP頭各20字節(jié))』嗤恚【seq=2970024578瓦糟,ack=0,syn=1】
- 第2行:16:34:33.611035赴蝇,server響應(yīng)連接狸页,同時帶上第一個包的ack信息,為client端的初始包序號seq加1扯再,即1944916151芍耘,即server端下次等待接受這個包序號的包,用于tcp字節(jié)流的順序控制熄阻。Server端的初始包序號seq為2032818397斋竞,mss也是16396⊥貉常【seq=2032818397坝初,ack=2970024579,syn=1】
- 第3行:16:34:33.611045钾军,client再次發(fā)送確認(rèn)連接鳄袍,tcp連接三次握手完成,等待傳輸數(shù)據(jù)包吏恭∞中。【ack=2032818398,seq=2970024579】
2. 為什么是三次握手而不是兩次?
對于step3的作用樱哼,假設(shè)一種情況哀九,客戶端A向服務(wù)器B發(fā)送一個連接請求數(shù)據(jù)報,然后這個數(shù)據(jù)報在網(wǎng)絡(luò)中滯留導(dǎo)致其遲到了搅幅,雖然遲到了阅束,但是服務(wù)器仍然會接收并發(fā)回一個確認(rèn)數(shù)據(jù)報。但是A卻因?yàn)榫镁檬詹坏紹的確認(rèn)而將發(fā)送的請求連接置為失效茄唐,等到一段時間后息裸,接到B發(fā)送過來的確認(rèn),A認(rèn)為自己現(xiàn)在沒有發(fā)送連接沪编,而B卻一直以為連接成功了呼盆,于是一直在等待A的動作,而A將不會有任何的動作了漾抬。這會導(dǎo)致服務(wù)器資源白白浪費(fèi)掉了宿亡,因此,兩次握手是不行的纳令,因此需要再加上一次挽荠,對B發(fā)過來的確認(rèn)再進(jìn)行一次確認(rèn)克胳,即確認(rèn)這次連接是有效的,從而建立連接圈匆。
3. 對于雙方漠另,發(fā)送序號的初始化為何值?
有的系統(tǒng)中是顯式的初始化序號是0,但是這種已知的初始化值是非常危險的跃赚,因?yàn)檫@會使得一些黑客鉆漏洞笆搓,發(fā)送一些數(shù)據(jù)報來破壞連接。因此纬傲,初始化序號因?yàn)槿‰S機(jī)數(shù)會更好一些满败,并且是越隨機(jī)越安全。
二. 四次揮手
連接雙方在完成數(shù)據(jù)傳輸之后就需要斷開連接叹括。由于TCP連接是屬于全雙工的算墨,即連接雙方可以在一條TCP連接上互相傳輸數(shù)據(jù),因此在斷開時存在一個半關(guān)閉狀態(tài)汁雷,即有有一方失去發(fā)送數(shù)據(jù)的能力净嘀,卻還能接收數(shù)據(jù)。因此侠讯,斷開連接需要分為四次挖藏。主要過程如下:
step1. 主機(jī)A向主機(jī)B發(fā)起斷開連接請求,之后主機(jī)A進(jìn)入FIN-WAIT-1狀態(tài)厢漩;
step2. 主機(jī)B收到主機(jī)A的請求后膜眠,向主機(jī)A發(fā)回確認(rèn),然后進(jìn)入CLOSE-WAIT狀態(tài)袁翁;
step3. 主機(jī)A收到B的確認(rèn)之后柴底,進(jìn)入FIN-WAIT-2狀態(tài),此時便是半關(guān)閉狀態(tài)粱胜,即主機(jī)A失去發(fā)送能力,但是主機(jī)B卻還能向A發(fā)送數(shù)據(jù)狐树,并且A可以接收數(shù)據(jù)焙压。此時主機(jī)B占主導(dǎo)位置了,如果需要繼續(xù)關(guān)閉則需要主機(jī)B來操作了抑钟;
step4. 主機(jī)B向A發(fā)出斷開連接請求涯曲,然后進(jìn)入LAST-ACK狀態(tài);
step5. 主機(jī)A接收到請求后發(fā)送確認(rèn)在塔,進(jìn)入TIME-WAIT狀態(tài)幻件,等待2MSL之后進(jìn)入CLOSED狀態(tài),而主機(jī)B則在接受到確認(rèn)后進(jìn)入CLOSED狀態(tài)蛔溃;
為何主機(jī)A在發(fā)送了最后的確認(rèn)后沒有進(jìn)入CLOSED狀態(tài)绰沥,反而進(jìn)入了一個等待2MSL的TIME-WAIT?
- 第一篱蝇,確保主機(jī)A最后發(fā)送的確認(rèn)能夠到達(dá)主機(jī)B。如果處于LAST-ACK狀態(tài)的主機(jī)B一直收不到來自主機(jī)A的確認(rèn)徽曲,它會重傳斷開連接請求零截,然后主機(jī)A就可以有足夠的時間去再次發(fā)送確認(rèn)。但是這也只能盡最大力量來確保能夠正常斷開秃臣,如果主機(jī)A的確認(rèn)總是在網(wǎng)絡(luò)中滯留失效涧衙,從而超過了2MSL,最后也無法正常斷開奥此;
- 第二弧哎,如果主機(jī)A在發(fā)送了確認(rèn)之后立即進(jìn)入CLOSED狀態(tài)。假設(shè)之后主機(jī)A再次向主機(jī)B發(fā)送一條連接請求稚虎,而這條連接請求比之前的確認(rèn)報文更早地到達(dá)主機(jī)B撤嫩,則會使得主機(jī)B以為這條連接請求是在舊的連接中A發(fā)出的報文,并不看成是一條新的連接請求了祥绞,即使得這個連接請求失效了非洲,增加2MSL的時間可以使得這個失效的連接請求報文作廢,這樣才不影響下次新的連接請求中出現(xiàn)失效的連接請求蜕径。
為什么斷開連接請求報文只有三個两踏,而不是四個?
因?yàn)樵赥CP連接過程中,確認(rèn)的發(fā)送有一個延時(即經(jīng)受延時的確認(rèn))兜喻,一端在發(fā)送確認(rèn)的時候?qū)⒌却欢螘r間梦染,如果自己在這段事件內(nèi)也有數(shù)據(jù)要發(fā)送,就跟確認(rèn)一起發(fā)送朴皆,如果沒有帕识,則確認(rèn)單獨(dú)發(fā)送。而我們的抓包實(shí)驗(yàn)中遂铡,由服務(wù)器端先斷開連接肮疗,之后客戶端在確認(rèn)的延遲時間內(nèi),也有請求斷開連接需要發(fā)送扒接,于是就與上次確認(rèn)一起發(fā)送伪货,因此就只有三個數(shù)據(jù)報了。
整體的圖
TCP狀態(tài)轉(zhuǎn)移要點(diǎn)
LISTENING狀態(tài)
FTP服務(wù)啟動后首先處于偵聽(LISTENING)狀態(tài)钾怔。
ESTABLISHED狀態(tài)
ESTABLISHED的意思是建立連接碱呼。表示兩臺機(jī)器正在通信。
CLOSE_WAIT
對方主動關(guān)閉連接或者網(wǎng)絡(luò)異常導(dǎo)致連接中斷宗侦,這時我方的狀態(tài)會變成CLOSE_WAIT 此時我方要調(diào)用close()來使得連接正確關(guān)閉
TIME_WAIT
我方主動調(diào)用close()斷開連接愚臀,收到對方確認(rèn)后狀態(tài)變?yōu)門IME_WAIT。TCP協(xié)議規(guī)定TIME_WAIT狀態(tài)會一直持續(xù)2MSL(即兩倍的分段最大生存期)矾利,以此來確保舊的連接狀態(tài)不會對新連接產(chǎn)生影響姑裂。處于TIME_WAIT狀態(tài)的連接占用的資源不會被內(nèi)核釋放馋袜,所以作為服務(wù)器,在可能的情況下炭分,盡量不要主動斷開連接桃焕,以減少TIME_WAIT狀態(tài)造成的資源浪費(fèi)。
參考文章:
https://my.oschina.net/xianggao/blog/678644
http://coolshell.cn/articles/11564.html
http://www.cnblogs.com/chobits/archive/2012/08/29/2662336.html