[tcp] 使用socket模擬分析tcp協議

寫兩段簡單的python代碼,然後來抓包分析tcp協議

服務端IP:172.16.196.145
客戶端IP:172.16.196.142

TCP三次握手、四次揮手

server端代碼

import socket

s = socket.socket()
s.bind(('172.16.196.145',60000))
s.listen(5)

while 1:
    conn, addr = s.accept()
    date = conn.recv(1024)
    if date == 'get':
        conn.send('200 ok')
    conn.close()
    print 'Connected by', addr, 'now closed'

client端代碼

import socket
s = socket.socket()
s.connect(('172.16.196.145',60000))
s.send('get')
print s.recv(1024)
s.close()

tcpdump抓包

1 12:43:46.905723 IP 172.16.196.142.41334 > 172.16.196.145.60000: Flags [S], seq 3255498564, win 14600, options [mss 1460,sackOK,TS val 1412272238 ecr 0,nop,wscale 7], length 0
2 12:43:46.905751 IP 172.16.196.145.60000 > 172.16.196.142.41334: Flags [S.], seq 4195434198, ack 3255498565, win 14480, options [mss 1460,sackOK,TS val 1425611003 ecr 1412272238,nop,wscale 7], length 0
3 12:43:46.905987 IP 172.16.196.142.41334 > 172.16.196.145.60000: Flags [.], ack 4195434199, win 115, options [nop,nop,TS val 1412272238 ecr 1425611003], length 0
4 12:43:46.906031 IP 172.16.196.142.41334 > 172.16.196.145.60000: Flags [P.], seq 3255498565:3255498568, ack 4195434199, win 115, options [nop,nop,TS val 1412272238 ecr 1425611003], length 3
5 12:43:46.906041 IP 172.16.196.145.60000 > 172.16.196.142.41334: Flags [.], ack 3255498568, win 114, options [nop,nop,TS val 1425611003 ecr 1412272238], length 0
6 12:43:46.906265 IP 172.16.196.145.60000 > 172.16.196.142.41334: Flags [P.], seq 4195434199:4195434205, ack 3255498568, win 114, options [nop,nop,TS val 1425611003 ecr 1412272238], length 6
7 12:43:46.906305 IP 172.16.196.145.60000 > 172.16.196.142.41334: Flags [F.], seq 4195434205, ack 3255498568, win 114, options [nop,nop,TS val 1425611003 ecr 1412272238], length 0
8 12:43:46.906406 IP 172.16.196.142.41334 > 172.16.196.145.60000: Flags [.], ack 4195434205, win 115, options [nop,nop,TS val 1412272239 ecr 1425611003], length 0
9 12:43:46.906487 IP 172.16.196.142.41334 > 172.16.196.145.60000: Flags [F.], seq 3255498568, ack 4195434206, win 115, options [nop,nop,TS val 1412272239 ecr 1425611003], length 0
10 12:43:46.906500 IP 172.16.196.145.60000 > 172.16.196.142.41334: Flags [.], ack 3255498569, win 114, options [nop,nop,TS val 1425611004 ecr 1412272239], length 0

逐行分析

第一行: 客戶端172.16.196.142,端口41336向服務端172.16.196.145端口60000發起SYN主動請求,seq:3255498564
第二行: 服務端172.16.196.145.60000給客戶端172.16.196.142.41336確認ACK ack爲3255498564+1=3255498565,並同時也發起SYN同步
第三行: 客戶端回覆服務端的SYN確認,三次握手建立連接
第四行: 客戶端調用s.send('get'),發送數據,因此爲P標記(PST),seq:3255498565:3255498568
第五行: 服務端回覆客戶端ACK確認標記,ack:3255498568
第六行: 服務端調用conn.send('200 ok')給客戶端回覆數據,爲P標記(PST),seq:4195434199:4195434205
第七行: 服務端發送回覆數據後,關閉連接,發起FIN主動關閉,seq:4195434205
第八行: 客戶端回覆服務端數據(第六行)的確認ACK,ack:4195434205
第九行: 客戶端發送FIN關閉連接,seq:3255498568 ack:4195434206
第十行: 服務端發送客戶端FIN的確認,由此,整個連接關閉 ack:3255498569

從分析來看,這是一個正常的三次握手建立連接之後傳輸數據,但是說好的四次揮手呢?
第七行、第九行、第十行,其實只有三次揮手

爲什麼是三次揮手

把上面最後斷開連接的包放下來看

6 12:43:46.906265 IP 172.16.196.145.60000 > 172.16.196.142.41334: Flags [P.], seq 4195434199:4195434205, ack 3255498568, win 114, options [nop,nop,TS val 1425611003 ecr 1412272238], length 6
7 12:43:46.906305 IP 172.16.196.145.60000 > 172.16.196.142.41334: Flags [F.], seq 4195434205, ack 3255498568, win 114, options [nop,nop,TS val 1425611003 ecr 1412272238], length 0
8 12:43:46.906406 IP 172.16.196.142.41334 > 172.16.196.145.60000: Flags [.], ack 4195434205, win 115, options [nop,nop,TS val 1412272239 ecr 1425611003], length 0
9 12:43:46.906487 IP 172.16.196.142.41334 > 172.16.196.145.60000: Flags [F.], seq 3255498568, ack 4195434206, win 115, options [nop,nop,TS val 1412272239 ecr 1425611003], length 0
10 12:43:46.906500 IP 172.16.196.145.60000 > 172.16.196.142.41334: Flags [.], ack 3255498569, win 114, options [nop,nop,TS val 1425611004 ecr 1412272239], length 0

看看上面最後關閉tcp連接抓到的封包
爲什麼上面抓包只有三次揮手呢,因爲服務端在conn.send('200 ok')後馬上就調用cnn.close()發起關閉連接了,而客戶端收到後還得調用s.recv(1024),還沒有發收到數據包的確認就馬上又收到了服務端的FIN,於是客戶端先是回覆了服務端[P.]的ACK確認,然後此時客戶端已經知道了服務端要關閉連接了,所以乾脆合併FIN和ACK回覆一個包,省掉一步,減少通信

抓到正確的四次揮手

既然知道是因爲服務端太快的關閉了連接,因此修改服務端代碼

import socket
import time

s = socket.socket()
s.bind(('172.16.196.145',60000))
s.listen(5)

while 1:
    conn, addr = s.accept()
    date = conn.recv(1024)
    if date == 'get':
        conn.send('200 ok')
        time.sleep(0.1) #暫停0.1秒後關閉連接
    conn.close()
    print 'Connected by',addr,'now closed'

我們暫停1秒後關閉連接,再抓包

1 20:01:49.251026 IP 172.16.196.142.41424 > 172.16.196.145.60000: Flags [S], seq 3935290883, win 14600, options [mss 1460,sackOK,TS val 1524954581 ecr 0,nop,wscale 7], length 0
2 20:01:49.251069 IP 172.16.196.145.60000 > 172.16.196.142.41424: Flags [S.], seq 3178165011, ack 3935290884, win 14480, options [mss 1460,sackOK,TS val 1538293348 ecr 1524954581,nop,wscale 7], length 0
3 20:01:49.251291 IP 172.16.196.142.41424 > 172.16.196.145.60000: Flags [.], ack 3178165012, win 115, options [nop,nop,TS val 1524954581 ecr 1538293348], length 0
4 20:01:49.251334 IP 172.16.196.142.41424 > 172.16.196.145.60000: Flags [P.], seq 3935290884:3935290887, ack 3178165012, win 115, options [nop,nop,TS val 1524954582 ecr 1538293348], length 3
5 20:01:49.251358 IP 172.16.196.145.60000 > 172.16.196.142.41424: Flags [.], ack 3935290887, win 114, options [nop,nop,TS val 1538293349 ecr 1524954582], length 0
6 20:01:49.251544 IP 172.16.196.145.60000 > 172.16.196.142.41424: Flags [P.], seq 3178165012:3178165018, ack 3935290887, win 114, options [nop,nop,TS val 1538293349 ecr 1524954582], length 6
7 20:01:49.251663 IP 172.16.196.142.41424 > 172.16.196.145.60000: Flags [.], ack 3178165018, win 115, options [nop,nop,TS val 1524954582 ecr 1538293349], length 0
8 20:01:49.251781 IP 172.16.196.142.41424 > 172.16.196.145.60000: Flags [F.], seq 3935290887, ack 3178165018, win 115, options [nop,nop,TS val 1524954582 ecr 1538293349], length 0
9 20:01:49.291441 IP 172.16.196.145.60000 > 172.16.196.142.41424: Flags [.], ack 3935290888, win 114, options [nop,nop,TS val 1538293389 ecr 1524954582], length 0
10 20:01:49.351812 IP 172.16.196.145.60000 > 172.16.196.142.41424: Flags [F.], seq 3178165018, ack 3935290888, win 114, options [nop,nop,TS val 1538293449 ecr 1524954582], length 0
11 20:01:49.352041 IP 172.16.196.142.41424 > 172.16.196.145.60000: Flags [.], ack 3178165019, win 115, options [nop,nop,TS val 1524954682 ecr 1538293449], length 0

6 服務端調用conn.send('200 ok'),seq:3178165012:3178165018
7 客戶端收到封包的確認,ack:3178165018
8 此時發現跟之前不一樣了,由於服務端暫停了0.1秒,因此主動關閉發送FIN標記的變成了客戶端(172.16.196.142),seq:3935290887
9 服務端回覆客戶端的FIN確認ACK,ack:3935290888
10 服務端發起FIN關閉,seq:3178165018
11 行客戶端回覆服務端的FIN確認ACK,ack:3178165019

因此以上抓包抓到了TCP完整的四次揮手

CLOSING狀態的tcpdump包

由於CLOSING狀態是兩邊同時發FIN,而且還要在收到對方的ACK前收到FIN,因此代碼很難實現,我倒是在web上面很容易就抓到了這種封包

1 19:40:34.831216 IP 10.29.64.142.443 > 10.25.137.230.46556: Flags [F.], seq 3633446994, ack 1764274713, win 131, length 0
2 19:40:34.832085 IP 10.25.137.230.46556 > 10.29.64.142.443: Flags [F.], seq 1764274713, ack 3633446994, win 480, length 0
3 19:40:34.832107 IP 10.29.64.142.443 > 10.25.137.230.46556: Flags [.], ack 1764274714, win 131, length 0
4 19:40:34.832395 IP 10.25.137.230.46556 > 10.29.64.142.443: Flags [.], ack 3633446995, win 480, length 0

1 10.29.64.142 發起了FIN,seq: 3633446994
2 10.25.137.230 發起了FIN,seq: 1764274713
3 10.29.64.142 發送確認ACK,ack: 1764274714
4 10.25.137.230 發送確認ACK,ack: 3633446995

這種封包就是同時發FIN,沒有先收到ACK,而是收到對方的FIN,雙方進入CLOSING狀態的封包,不過隨着對方很快發送ACK,因此雙方進入TIME_WAIT狀態

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章