1. 三次握手建立连接
TCP是TCP/IP的传输层控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接。
TCP 3-Way Handshake (SYN,SYN-ACK,ACK),事例图示很多,拿来:
准备一个简单的socket 代码段
- client.py
from socket import *
import time
addr = ('127.0.0.1',8888)
client = socket(AF_INET, SOCK_STREAM)
client.connect(addr)
time.sleep(5)
print "client closed"
- server.py
import socket
import sys
import os
addr = ('127.0.0.1', 8888)
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 端会在连接5秒后,关闭连接,用于查看挥手的过程,暂时我们先分析下建立连接的过程。
- 抓包:
[root@ip-*-*-*-* ~]# tcpdump -nn -vvv -XX -i lo -S tcp
04:33:24.476992 IP (tos 0x0, ttl 64, id 40015, offset 0, flags [DF], proto TCP (6), length 60)
127.0.0.1.54800 > 127.0.0.1.8888: Flags [S], cksum 0xfe30 (incorrect -> 0x4f2d), seq 2359231437, win 43690, options [mss 65495,sackOK,TS val 759865218 ecr 0,nop,wscale 7], length 0
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E.
0x0010: 003c 9c4f 4000 4006 a06a 7f00 0001 7f00 .<.O@[email protected]......
0x0020: 0001 d610 22b8 8c9f 03cd 0000 0000 a002 ...."...........
0x0030: aaaa fe30 0000 0204 ffd7 0402 080a 2d4a ...0..........-J
0x0040: 9f82 0000 0000 0103 0307 ..........
04:33:24.477010 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
127.0.0.1.8888 > 127.0.0.1.54800: Flags [S.], cksum 0xfe30 (incorrect -> 0x6480), seq 4014354056, ack 2359231438, win 43690, options [mss 65495,sackOK,TS val 759865218 ecr 759865218,nop,wscale 7], length 0
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E.
0x0010: 003c 0000 4000 4006 3cba 7f00 0001 7f00 .<..@.@.<.......
0x0020: 0001 22b8 d610 ef46 2e88 8c9f 03ce a012 .."....F........
0x0030: aaaa fe30 0000 0204 ffd7 0402 080a 2d4a ...0..........-J
0x0040: 9f82 2d4a 9f82 0103 0307 ..-J......
04:33:24.477023 IP (tos 0x0, ttl 64, id 40016, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.54800 > 127.0.0.1.8888: Flags [.], cksum 0xfe28 (incorrect -> 0x36c5), seq 2359231438, ack 4014354057, win 342, options [nop,nop,TS val 759865218 ecr 759865218], length 0
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E.
0x0010: 0034 9c50 4000 4006 a071 7f00 0001 7f00 .4.P@[email protected]......
0x0020: 0001 d610 22b8 8c9f 03ce ef46 2e89 8010 ...."......F....
0x0030: 0156 fe28 0000 0101 080a 2d4a 9f82 2d4a .V.(......-J..-J
0x0040: 9f82 ..
分析:
1. 第一条client给server发送了一个SYN包,TCP 协议的报文从0x0020 + 2 的位置开始,TCP-FLAGS是:a002;对应TCP的首部。
只有SYN位为1, 序号是2359231437。
2. 第二条,server给client发送了一个 ACK包,ack的序号是2359231438(2359231437 + 1)。同样的TCP-FLAGS:a012,ACK=1,SYN=1。
3. 第三条,client给server发送了一个ACK包TCP-FLAGS:8010,ACK=1,其余位是0。
注意:tcp在收到第一条数据包之后,后续的数据包,是使用之前数据包的偏移来显示的,所以抓包加-S选项,会打印绝对编号。
2. 四次挥手
示意图:
连接5秒后,client端会close掉当前连接。
此时抓包会接着打印:
04:33:29.484395 IP (tos 0x0, ttl 64, id 40017, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.54800 > 127.0.0.1.8888: Flags [F.], cksum 0xfe28 (incorrect -> 0x2335), seq 2359231438, ack 4014354057, win 342, options [nop,nop,TS val 759870225 ecr 759865218], length 0
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E.
0x0010: 0034 9c51 4000 4006 a070 7f00 0001 7f00 .4.Q@[email protected]......
0x0020: 0001 d610 22b8 8c9f 03ce ef46 2e89 8011 ...."......F....
0x0030: 0156 fe28 0000 0101 080a 2d4a b311 2d4a .V.(......-J..-J
0x0040: 9f82 ..
04:33:29.484840 IP (tos 0x0, ttl 64, id 45323, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.8888 > 127.0.0.1.54800: Flags [.], cksum 0xfe28 (incorrect -> 0x0fa5), seq 4014354057, ack 2359231439, win 342, options [nop,nop,TS val 759870226 ecr 759870225], length 0
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E.
0x0010: 0034 b10b 4000 4006 8bb6 7f00 0001 7f00 .4..@.@.........
0x0020: 0001 22b8 d610 ef46 2e89 8c9f 03cf 8010 .."....F........
0x0030: 0156 fe28 0000 0101 080a 2d4a b312 2d4a .V.(......-J..-J
0x0040: b311
分析:
- client 给server端发了一个[F],分析TCP-FLAGS:8011,对应就是:ACK=1,FIN=1。
- server 马上回复8010, ACK = 1,这个时候,你也可以看下server的TCP 状态,CLOSE_WAIT
[ec2-user@ip-172-31-28-112 ~]$ netstat -natp
(No info could be read for "-p": geteuid()=1000 but you should be root.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8888 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN -
tcp 1 0 127.0.0.1:8888 127.0.0.1:54876 CLOSE_WAIT -
tcp 0 36 172.31.28.112:22 123.121.20.194:53087 ESTABLISHED -
tcp 0 0 172.31.28.112:22 123.121.20.194:62244 ESTABLISHED -
tcp 0 0 172.31.28.112:22 123.121.20.194:62758 ESTABLISHED -
tcp 0 0 127.0.0.1:54876 127.0.0.1:8888 FIN_WAIT2 -
tcp 0 0 172.31.28.112:40916 173.194.175.188:443 ESTABLISHED -
tcp 0 0 172.31.28.112:22 123.121.20.194:62785 ESTABLISHED -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 ::1:25 :::* LISTEN -
- 这时候,ctl + c 关掉server 进程,会自动关闭打开的socket。抓包会继续打印:
06:15:56.777552 IP (tos 0x0, ttl 64, id 34340, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.8888 > 127.0.0.1.54874: Flags [F.], cksum 0xfe28 (incorrect -> 0xfe34), seq 1558878509, ack 4191715919, win 342, options [nop,nop,TS val 766017518 ecr 765997725], length 0
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E.
0x0010: 0034 8624 4000 4006 b69d 7f00 0001 7f00 .4.$@.@.........
0x0020: 0001 22b8 d65a 5cea 992d f9d8 824f 8011 .."..Z\..-...O..
0x0030: 0156 fe28 0000 0101 080a 2da8 7fee 2da8 .V.(......-...-.
0x0040: 329d 2.
06:15:56.777568 IP (tos 0x0, ttl 64, id 61211, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.54874 > 127.0.0.1.8888: Flags [.], cksum 0xb0e3 (correct), seq 4191715919, ack 1558878510, win 342, options [nop,nop,TS val 766017518 ecr 766017518], length 0
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E.
0x0010: 0034 ef1b 4000 4006 4da6 7f00 0001 7f00 .4..@[email protected].......
0x0020: 0001 d65a 22b8 f9d8 824f 5cea 992e 8010 ...Z"....O\.....
0x0030: 0156 b0e3 0000 0101 080a 2da8 7fee 2da8 .V........-...-.
0x0040: 7fee
其中,第一条:8011,其中ACK=1,FIN=1。
- 第2条8010,其中ACK=1。
和原理完全匹配。只是不清楚,为什么close 的时候,ACK为什么要发。