一. 預(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
二. 三次握手
流程圖

中間的標志位
S=SYN,發(fā)起連接標志。
P=PUSH,傳送數(shù)據(jù)標志。
F=FIN,關(guān)閉連接標志。
ack,表示確認包。
RST=RESET,異常關(guān)閉連接。
.,表示沒有任何標志。
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é))?!緎eq=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ā)送確認連接,tcp連接三次握手完成,等待傳輸數(shù)據(jù)包?!綼ck=2032818398,seq=2970024579】
2. 為什么是三次握手而不是兩次?
對于step3的作用,假設(shè)一種情況,客戶端A向服務(wù)器B發(fā)送一個連接請求數(shù)據(jù)報,然后這個數(shù)據(jù)報在網(wǎng)絡(luò)中滯留導致其遲到了,雖然遲到了,但是服務(wù)器仍然會接收并發(fā)回一個確認數(shù)據(jù)報。但是A卻因為久久收不到B的確認而將發(fā)送的請求連接置為失效,等到一段時間后,接到B發(fā)送過來的確認,A認為自己現(xiàn)在沒有發(fā)送連接,而B卻一直以為連接成功了,于是一直在等待A的動作,而A將不會有任何的動作了。這會導致服務(wù)器資源白白浪費掉了,因此,兩次握手是不行的,因此需要再加上一次,對B發(fā)過來的確認再進行一次確認,即確認這次連接是有效的,從而建立連接。
3. 對于雙方,發(fā)送序號的初始化為何值?
有的系統(tǒng)中是顯式的初始化序號是0,但是這種已知的初始化值是非常危險的,因為這會使得一些黑客鉆漏洞,發(fā)送一些數(shù)據(jù)報來破壞連接。因此,初始化序號因為取隨機數(shù)會更好一些,并且是越隨機越安全。
二. 四次揮手
連接雙方在完成數(shù)據(jù)傳輸之后就需要斷開連接。由于TCP連接是屬于全雙工的,即連接雙方可以在一條TCP連接上互相傳輸數(shù)據(jù),因此在斷開時存在一個半關(guān)閉狀態(tài),即有有一方失去發(fā)送數(shù)據(jù)的能力,卻還能接收數(shù)據(jù)。因此,斷開連接需要分為四次。主要過程如下:

step1. 主機A向主機B發(fā)起斷開連接請求,之后主機A進入FIN-WAIT-1狀態(tài);
step2. 主機B收到主機A的請求后,向主機A發(fā)回確認,然后進入CLOSE-WAIT狀態(tài);
step3. 主機A收到B的確認之后,進入FIN-WAIT-2狀態(tài),此時便是半關(guān)閉狀態(tài),即主機A失去發(fā)送能力,但是主機B卻還能向A發(fā)送數(shù)據(jù),并且A可以接收數(shù)據(jù)。此時主機B占主導位置了,如果需要繼續(xù)關(guān)閉則需要主機B來操作了;
step4. 主機B向A發(fā)出斷開連接請求,然后進入LAST-ACK狀態(tài);
step5. 主機A接收到請求后發(fā)送確認,進入TIME-WAIT狀態(tài),等待2MSL之后進入CLOSED狀態(tài),而主機B則在接受到確認后進入CLOSED狀態(tài);
為何主機A在發(fā)送了最后的確認后沒有進入CLOSED狀態(tài),反而進入了一個等待2MSL的TIME-WAIT?
- 第一,確保主機A最后發(fā)送的確認能夠到達主機B。如果處于LAST-ACK狀態(tài)的主機B一直收不到來自主機A的確認,它會重傳斷開連接請求,然后主機A就可以有足夠的時間去再次發(fā)送確認。但是這也只能盡最大力量來確保能夠正常斷開,如果主機A的確認總是在網(wǎng)絡(luò)中滯留失效,從而超過了2MSL,最后也無法正常斷開;
- 第二,如果主機A在發(fā)送了確認之后立即進入CLOSED狀態(tài)。假設(shè)之后主機A再次向主機B發(fā)送一條連接請求,而這條連接請求比之前的確認報文更早地到達主機B,則會使得主機B以為這條連接請求是在舊的連接中A發(fā)出的報文,并不看成是一條新的連接請求了,即使得這個連接請求失效了,增加2MSL的時間可以使得這個失效的連接請求報文作廢,這樣才不影響下次新的連接請求中出現(xiàn)失效的連接請求。
為什么斷開連接請求報文只有三個,而不是四個?
因為在TCP連接過程中,確認的發(fā)送有一個延時(即經(jīng)受延時的確認),一端在發(fā)送確認的時候?qū)⒌却欢螘r間,如果自己在這段事件內(nèi)也有數(shù)據(jù)要發(fā)送,就跟確認一起發(fā)送,如果沒有,則確認單獨發(fā)送。而我們的抓包實驗中,由服務(wù)器端先斷開連接,之后客戶端在確認的延遲時間內(nèi),也有請求斷開連接需要發(fā)送,于是就與上次確認一起發(fā)送,因此就只有三個數(shù)據(jù)報了。
整體的圖

TCP狀態(tài)轉(zhuǎn)移要點
LISTENING狀態(tài)
FTP服務(wù)啟動后首先處于偵聽(LISTENING)狀態(tài)。
ESTABLISHED狀態(tài)
ESTABLISHED的意思是建立連接。表示兩臺機器正在通信。
CLOSE_WAIT
對方主動關(guān)閉連接或者網(wǎng)絡(luò)異常導致連接中斷,這時我方的狀態(tài)會變成CLOSE_WAIT 此時我方要調(diào)用close()來使得連接正確關(guān)閉
TIME_WAIT
我方主動調(diào)用close()斷開連接,收到對方確認后狀態(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)造成的資源浪費。
參考文章:
https://my.oschina.net/xianggao/blog/678644
http://coolshell.cn/articles/11564.html
http://www.cnblogs.com/chobits/archive/2012/08/29/2662336.html