[tcp]セグメントsocketコードシミュレーション分析tcpプロトコル


簡単なpythonコードを2つ書いて、パッケージをつかんでtcpプロトコルを分析します.
サービス端末IP:172.16.196.145クライアントIP:172.16.96.142
TCP 3回握手、4回手を振る
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'

クライアントエンドコード
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

ちくじぶんせき
1行目:クライアント172.16.196.142、ポート41336はサービス側172.16.196.145ポート60000にSYNアクティブ要求を開始し、seq:3254958564 2行目:サービス側172.16.96.145.6000はクライアント172.16.196.142.441336にACK ackが3255498564+1=3255498565であることを確認し、同時にSYN同期3行目:クライアントはサービス側のSYN確認を返信し、3回の握手で接続4行目を確立する:クライアントはs.send('get')を呼び出し、データを送信するので、Pタグ(PST)、seq:355498565:355498568:355498568 5行目:サービス側はクライアントACK確認タグに返信し、ack:355498568 6行目:サービス側はconn.send('200 ok')を呼び出してクライアントにデータを返信し、Pタグ(PST)、seq:4195434199:4195434205第7行:サービス側が返信データを送信した後、接続を閉じ、FINを起動して自発的に閉じる、seq:4195434205第8行:クライアントがサービス側データ(第6行)の確認ACKを返信し、ack:4195434205第9行:クライアントがFINを送信して接続を閉じる、seq:325498568 ack:4195434206第10行:サービス側がクライアントFINの確認を送信することにより、接続全体を閉じるack:325495569
分析から見ると、これは正常な3回の握手で接続を確立した後にデータを転送しますが、4回手を振ると約束しましたか?7行目、9行目、10行目、実は3回しか手を振っていません
どうして三度手を振ったの
上の最後に接続を切ったバッグを下ろしてみます
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接続を閉じて捕まえたパッケージを見てみましょう.なぜ上のパッケージは3回しか手を振っていませんか.サービス側はconn.send('200 ok')の後、すぐにcnnを呼び出すからです.close()は接続を閉じることを開始し、クライアントは受信した後にs.recv(1024)を呼び出さなければならず、パケットを受信した確認をまだ送っていないうちにすぐにサービス側のFINを受信し、クライアントはまずサービス側[P.]のACK確認を返信し、その後、クライアントはすでにサービス側が接続を閉じることを知っているので、いっそFINとACKを結合してパケットを返信し、一歩省く.通信を減らす
正しい4回手を振る
サービス側がすぐに接続を閉じたため、サービス側コードを変更したことを知っています.
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:317165012:31781650187クライアントはパッケージの確認を受け取り、ack:31781650188このとき以前とは異なることに気づき、サービス側は0.1秒を一時停止したため、FINタグの送信をアクティブにオフにしてクライアントになった(172.16.196.142)、seq:3952908879サービス側はクライアントのFIN確認ACKを返信し、ack:39359088810サービス側はFINクローズを開始し、seq:317816501811行のクライアントはサービス側のFIN確認ACKに返信し、ack:3178165019
そのため、以上のバッグはTCPの完全な4回の手を振った.
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:36334469942 10.25.1137.230 FIN、seq:176442747133 10.29.64.142確認ACK、ack:176442747144 10.25.1137.230確認ACK、ack:3633446995
このようなパッケージは同時にFINを送るもので、先にACKを受け取るのではなく、相手のFINを受け取り、双方はCLOSING状態のパッケージに入るが、相手がすぐにACKを送信するにつれて、双方はTIME_に入るWAIT状態