TCP协议在网络分层模型中处于传输层,许多的应用层协议(例如HTTP,SMTP等)是基于传输层TCP协议工作的,TCP协议提供了面向连接的可靠的传输服务,理解TCP协议的工作原理对于工作和面试是非常有帮助的。
为了简便,使用nc命令来模拟服务器和客户端来建立连接的三次握手过程。
首先在linux上打开一个终端,键入nc -v -l 127.0.0.1 22222命令,该命令的作用是让该主机监听本机的22222端口,充当服务器功能。
接下来再打开一个终端使用tcpdump命令来监控三次握手过程
tcpdump -i any port '22222' -XX -nn -vv
然后再打开一个终端,键入nc -v 127.0.0.1 2222来模拟客户端功能,与服务器建立连接。
观察tcpdump窗口的变化
客户端和服务器进行了三次握手过程,建立了连接。
下面来分析协议细节。
分析一下第一次握手的细节
0x0000: 0000 0304 0006 0000 0000 0000 0000 0800 ................
0x0010: 4500 003c 8aa5 4000 4006 b214 7f00 0001 E..<..@.@.......
0x0020: 7f00 0001 b5c8 56ce 246e 2eea 0000 0000 ......V.$n......
0x0030: a002 aaaa fe30 0000 0204 ffd7 0402 080a .....0..........
0x0040: b675 a44f 0000 0000 0103 0307 0000 0000 .u.O............
0x0050: 0000 0000 0000 0000 0000 0000 ............
前32个字节是其他层协议的部分,我们暂时不研究,从第33个字节开始就是TCP的头部信息了
十六进制 | 十进制 | TCP头部信息 |
0xb5c8 | 46536 | 16位源端口号 |
0x56ce | 22222 | 16位目的端口号 |
0x246e2eea | 611200746 | 32位序号 |
0x00000000 | 0 | 32位确认号 |
0xa | 10 | 4位头部长度,10个4字节,最多60字节 |
0x002 | 2 | 前6位保留位,后六位标志位,设置了SYN标志 |
0xaaaa | 43690 | 16位接受窗口大小 |
0xfe30 | 头部校验和 | |
0x0000 | 16位紧急指针 | |
0x0204 | 最大报文段长度选项的kind值和length值 | |
0xffd7 | 65495 | 最大报文段长度 |
0x0402 | 允许SACK选项 | |
0x080a | 时间戳选项的kind值length值 | |
0xb675a44f | 时间戳 | |
0x00000000 | 回显应答时间戳 | |
0x01 | 空操作选项 | |
0x0303 | 窗口扩大因子选项的kind值length值 | |
0x06 | 6 | 窗口扩大因子为6 |
SYN置1,随机生成序列号,这样40个字节长度的TCP头部就列出来了。接下来看第二次握手的细节。
0x0000: 0000 0304 0006 0000 0000 0000 0000 0800 ................
0x0010: 4500 003c 0000 4000 4006 3cba 7f00 0001 E..<..@.@.<.....
0x0020: 7f00 0001 56ce b5c8 20f5 1549 246e 2eeb ....V......I$n..
0x0030: a012 aaaa fe30 0000 0204 ffd7 0402 080a .....0..........
0x0040: b675 a44f b675 a44f 0103 0307 0000 0000 .u.O.u.O........
0x0050: 0000 0000 0000 0000 0000 0000
SYN、ACK置1,seq随机生成,ack=上一次握手的seq+1
十六进制 | 十进制 | TCP头部信息 |
0x56ce | 22222 | 16位源端口号 |
0xb5c8 | 46536 | 16位目的端口号 |
0x20f51549 | 552932681 | 32位序号 |
0x246e2eeb | 611200747 | 32位确认号 |
0xa | 10 | 4位头部长度,10个4字节,最多60字节 |
0x012 | 前6位保留位,后六位标志位,设置了SYN和ACK标志 | |
0xaaaa | 43690 | 16位接受窗口大小 |
0xfe30 | 头部校验和 | |
0x0000 | 16位紧急指针 | |
0x0204 | 最大报文段长度选项的kind值和length值 | |
0xffd7 | 65495 | 最大报文段长度 |
0x0402 | 允许SACK选项 | |
0x080a | 时间戳选项的kind值length值 | |
0xb675a44f | 时间戳 | |
0xb675a44f | 回显应答时间戳 | |
0x01 | 空操作选项 | |
0x0303 | 窗口扩大因子选项的kind值length值 | |
0x06 | 6 | 窗口扩大因子为6 |
接下来观察第三次握手。
16:42:40.336310 IP (tos 0x0, ttl 64, id 35494, offset 0, flags [DF], proto TCP (6), length 52)
127.0.0.1.46536 > 127.0.0.1.22222: Flags [.], cksum 0xfe28 (incorrect -> 0x2bab), seq 1, ack 1, win 342, options [nop,nop,TS val 3061163087 ecr 3061163087], length 0
0x0000: 0000 0304 0006 0000 0000 0000 0000 0800 ................
0x0010: 4500 0034 8aa6 4000 4006 b21b 7f00 0001 E..4..@.@.......
0x0020: 7f00 0001 b5c8 56ce 246e 2eeb 20f5 154a ......V.$n.....J
0x0030: 8010 0156 fe28 0000 0101 080a b675 a44f ...V.(.......u.O
0x0040: b675 a44f 0000 0000 0000 0000 0000 0000 .u.O............
0x0050: 0000 0000
十六进制 | 十进制 | TCP头部信息 |
0xb5c8 | 46536 | 16位源端口号 |
0x56ce | 22222 | 16位目的端口号 |
0x246e2eeb | 611200747 | 32位序号 |
0x20f5154a | 552932682 | 32位确认号 |
0x8 | 8 | 4位头部长度,8个4字节,最多60字节 |
0x010 | 2 | 前6位保留位,后六位标志位,设置了ACK标志 |
0x0156 | 342 | 16位接受窗口大小 |
0xfe28 | 头部校验和 | |
0x0000 | 16位紧急指针 | |
0x0101 | 两个填充位 | |
0xffd7 | 65495 | 最大报文段长度 |
0x080a | 时间戳选项的kind值length值 | |
0xb675a44f | 时间戳 | |
0xb675a44f | 回显应答时间戳 |
ACK置1,seq = 上一次的ack,ack=上一次的序列号+1
接下来看看,数据发送阶段的细节。
客户端给服务器发送hello,server信息,服务器上显示出来客户端发送的信息。
tcpdump抓包如下:
下面来看细节
客户端发送给服务器的包为:
0x0000: 0000 0304 0006 0000 0000 0000 0000 0800 ................
0x0010: 4500 0041 3884 4000 4006 0431 7f00 0001 E..A8.@[email protected]....
0x0020: 7f00 0001 b660 56ce 27a0 4f7f db48 65b5 .....`V.'.O..He.
0x0030: 8018 0156 fe35 0000 0101 080a b6b8 bdcc ...V.5..........
0x0040: b6b8 77fc 6865 6c6c 6f2c 7365 7276 6572 ..w.hello,server
0x0050: 0a00 0000 0000 0000 0000 0000 0000 0000 ................
0x0060: 00
照上例,前36个字节暂时不研究
十六进制 | 十进制 | TCP头部信息 |
0xb660 | 46688 | 16位源端口号 |
0x56ce | 22222 | 16位目的端口号 |
0x27a04f7f | 664817535 | 32位序号 |
0xdb4865b5 | 3678954933 | 32位确认号 |
0x8 | 8 | 4位头部长度,8个4字节,最多60字节 |
0x018 | 前6位保留位,后六位标志位,设置了ACK和PUSH标志 | |
0x0156 | 342 | 16位接受窗口大小 |
0xfe35 | 头部校验和 | |
0x0000 | 16位紧急指针 | |
0x0101 | 两个填充空操作,确保TCP头部长度为4字节的整数倍 | |
0x80a | 时间戳选项的kind值length值 | |
0xb6b8bdcc | 时间戳 | |
0xb6b877fc | 回显应答时间戳 | |
0x6865 6c6c 6f2c 7365 7276 6572 | hello,server | |
0x0a | \n |
0x0000: 0000 0304 0006 0000 0000 0000 0000 0800 ................
0x0010: 4500 0034 1249 4000 4006 2a79 7f00 0001 E..4.I@.@.*y....
0x0020: 7f00 0001 56ce b660 db48 65b5 27a0 4f8c ....V..`.He.'.O.
0x0030: 8010 0156 fe28 0000 0101 080a b6b8 bdcc ...V.(..........
0x0040: b6b8 bdcc 0000 0000 0000 0000 0000 0000 ................
0x0050: 0000 0000
十六进制 | 十进制 | TCP头部信息 |
0x56ce | 22222 | 16位源端口号 |
0xb660 | 46688 | 16位目的端口号 |
0xdb4865b5 | 367895493 | 32位序号 |
0x27a04f8c | 664817548 | 32位确认号 |
0x8 | 8 | 4位头部长度,8个4字节,最多60字节 |
0x010 | 前6位保留位,后六位标志位,设置了ACK标志 | |
0x0156 | 342 | 16位接受窗口大小 |
0xfe28 | 头部校验和 | |
0x0000 | 16位紧急指针 | |
0x0101 | 两个填充空操作,确保TCP头部长度为4字节的整数倍 | |
0x80a | 时间戳选项的kind值length值 | |
0xb6b8bdcc | 时间戳 | |
0xb6b8bdcc | 回显应答时间戳 |
四次挥手过程:
0x0000: 0000 0304 0006 0000 0000 0000 0000 0800 ................
0x0010: 4500 0034 3885 4000 4006 043d 7f00 0001 E..48.@.@..=....
0x0020: 7f00 0001 b660 56ce 27a0 4f8c db48 65b5 .....`V.'.O..He.
0x0030: 8011 0156 fe28 0000 0101 080a b6d2 c714 ...V.(..........
0x0040: b6b8 bdcc 0000 0000 0000 0000 0000 0000 ................
0x0050: 0000 0000
十六进制 | 十进制 | TCP头部信息 |
0xb660 | 46688 | 16位源端口号 |
0x56ce | 22222 | 16位目的端口号 |
0x27a04f8c | 664817548 | 32位序号 |
0xdb4865b5 | 3678954933 | 32位确认号 |
0x8 | 8 | 4位头部长度,8个4字节,最多60字节 |
0x011 | 2 | 前6位保留位,后六位标志位,设置了ACK和FIN标志 |
0xaaaa | 43690 | 16位接受窗口大小 |
0xfe28 | 头部校验和 | |
0x0000 | 16位紧急指针 | |
0x0101 | 两个空操作填充位 | |
0x080a | 时间戳选项的kind值length值 | |
0xb6d2c714 | 时间戳 | |
0xb6b8bdcc | 回显应答时间戳 |
0x0000: 0000 0304 0006 0000 0000 0000 0000 0800 ................
0x0010: 4500 0034 124a 4000 4006 2a78 7f00 0001 E..4.J@.@.*x....
0x0020: 7f00 0001 56ce b660 db48 65b5 27a0 4f8d ....V..`.He.'.O.
0x0030: 8011 0156 fe28 0000 0101 080a b6d2 c714 ...V.(..........
0x0040: b6d2 c714 0000 0000 0000 0000 0000 0000 ................
0x0050: 0000 0000
十六进制 | 十进制 | TCP头部信息 |
0x56ce | 22222 | 16位源端口号 |
0xb660 | 46688 | 16位目的端口号 |
0xdb4865b5 | 3678954933 | 32位序号 |
0xdb4865b5 | 664817549 | 32位确认号 |
0x8 | 8 | 4位头部长度,8个4字节,最多60字节 |
0x011 | 2 | 前6位保留位,后六位标志位,设置了ACK和FIN标志 |
0xaaaa | 43690 | 16位接受窗口大小 |
0xfe28 | 头部校验和 | |
0x0000 | 16位紧急指针 | |
0x0101 | 两个空操作填充位 | |
0x080a | 时间戳选项的kind值length值 | |
0xb6d2c714 | 时间戳 | |
0xb6d2c714 | 回显应答时间戳 |
0x0000: 0000 0304 0006 0000 0000 0000 0000 0800 ................
0x0010: 4500 0034 3886 4000 4006 043c 7f00 0001 E..48.@.@..<....
0x0020: 7f00 0001 b660 56ce 27a0 4f8d db48 65b6 .....`V.'.O..He.
0x0030: 8010 0156 fe28 0000 0101 080a b6d2 c714 ...V.(..........
0x0040: b6d2 c714 0000 0000 0103 0307 0000 0000 ................
0x0050: 0000 0000
十六进制 | 十进制 | TCP头部信息 |
0xb660 | 46688 | 16位源端口号 |
0x56ce | 22222 | 16位目的端口号 |
0x27a04f8d | 664817549 | 32位序号 |
0xdb4865b6 | 3678954934 | 32位确认号 |
0x8 | 8 | 4位头部长度,8个4字节,最多60字节 |
0x010 | 2 | 前6位保留位,后六位标志位,设置了ACK标志 |
0xaaaa | 43690 | 16位接受窗口大小 |
0xfe28 | 头部校验和 | |
0x0000 | 16位紧急指针 | |
0x0101 | 两个空操作填充位 | |
0x080a | 时间戳选项的kind值length值 | |
0xb6d2c714 | 时间戳 | |
0xb6d2c714 | 回显应答时间戳 |