利器tcpdump

准备工作

Web服务器IP(阿里云的内网IP:172.17.51.219:443)  
客户端浏览器 IP:114.242.250.59

标志位(常用)

P push,立刻刷新输出buffer
. 确认应答ACK
F 确认结束
S 请求同步    

步骤:

1 服务端shell中执行tcpdump启动监听
tcpdump tcp -i eth0 '((host 172.17.51.219 or 114.242.250.59) and port 443)' -nn -S

参数解释(更多参数 man 一下)

tcp     : 只监听tcp协议数据包
-i eth0 : 监听网卡 eth0 上的流量(我的主机通过 eth0 跟外网通信)
-nn     : 以ip:port的形式代替助记符显示形式  否则会显示类似于 www.example.com:https 形式
-S      : 以绝对值形式显示 发送序列号,确认序列号,便于直观观察 SYN 与 ACK 之间的关系,默认情况下是以相对值显示
'((host 172.17.51.219 or 114.242.250.59) and port 443)' : 明确指定监控 host:172.17.51.219 与 host:114.242.250.59 之间通过 443 接收或发送的数据包
2 客户端浏览器访问web服务器
    https://www.example.com?foo=foo
3 观察shell端输出后

监测到握手数据后,ctrl + c 终止监听

[root@ ~]$>tcpdump -i eth0 '((host 172.17.51.219 or 114.242.250.59) and port 443)' -nn -S
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

13:51:40.965707 IP 114.242.250.59.41032 > 172.17.51.219.443: Flags [F.], seq 3249586170, ack 137328987, win 2061, options [nop,nop,TS val 555445081 ecr 2928319486], length 0
13:51:40.965838 IP 172.17.51.219.443 > 114.242.250.59.41032: Flags [F.], seq 137328987, ack 3249586171, win 235, options [nop,nop,TS val 2928333454 ecr 555445081], length 0
13:51:40.966228 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [S], seq 1328522649, win 65535, options [mss 1360,nop,wscale 6,nop,nop,TS val 555445081 ecr 0,sackOK,eol], length 0
13:51:40.966266 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [S.], seq 466300160, ack 1328522650, win 28960, options [mss 1460,sackOK,TS val 2928333455 ecr 555445081,nop,wscale 7], length 0
13:51:40.966630 IP 114.242.250.59.41034 > 172.17.51.219.443: Flags [S], seq 3150370905, win 65535, options [mss 1360,nop,wscale 6,nop,nop,TS val 555445081 ecr 0,sackOK,eol], length 0
13:51:40.966650 IP 172.17.51.219.443 > 114.242.250.59.41034: Flags [S.], seq 3538757503, ack 3150370906, win 28960, options [mss 1460,sackOK,TS val 2928333455 ecr 555445081,nop,wscale 7], length 0
13:51:40.986348 IP 114.242.250.59.41034 > 172.17.51.219.443: Flags [.], ack 3538757504, win 2064, options [nop,nop,TS val 555445117 ecr 2928333455], length 0
13:51:40.986539 IP 114.242.250.59.41032 > 172.17.51.219.443: Flags [.], ack 137328988, win 2061, options [nop,nop,TS val 555445117 ecr 2928333454], length 0
13:51:40.991849 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [.], ack 466300161, win 2064, options [nop,nop,TS val 555445118 ecr 2928333455], length 0
13:51:40.994419 IP 114.242.250.59.41034 > 172.17.51.219.443: Flags [P.], seq 3150370906:3150371429, ack 3538757504, win 2064, options [nop,nop,TS val 555445118 ecr 2928333455], length 523
13:51:40.994460 IP 172.17.51.219.443 > 114.242.250.59.41034: Flags [.], ack 3150371429, win 235, options [nop,nop,TS val 2928333483 ecr 555445118], length 0
13:51:40.995615 IP 172.17.51.219.443 > 114.242.250.59.41034: Flags [.], seq 3538757504:3538760200, ack 3150371429, win 235, options [nop,nop,TS val 2928333484 ecr 555445118], length 2696
13:51:40.995621 IP 172.17.51.219.443 > 114.242.250.59.41034: Flags [P.], seq 3538760200:3538760565, ack 3150371429, win 235, options [nop,nop,TS val 2928333484 ecr 555445118], length 365
13:51:40.996812 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [P.], seq 1328522650:1328523173, ack 466300161, win 2064, options [nop,nop,TS val 555445118 ecr 2928333455], length 523
13:51:40.996850 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [.], ack 1328523173, win 235, options [nop,nop,TS val 2928333485 ecr 555445118], length 0
13:51:40.997984 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [.], seq 466300161:466302857, ack 1328523173, win 235, options [nop,nop,TS val 2928333487 ecr 555445118], length 2696
13:51:40.997991 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [P.], seq 466302857:466303222, ack 1328523173, win 235, options [nop,nop,TS val 2928333487 ecr 555445118], length 365
13:51:41.024807 IP 114.242.250.59.41034 > 172.17.51.219.443: Flags [.], ack 3538760200, win 2026, options [nop,nop,TS val 555445154 ecr 2928333484], length 0
13:51:41.024809 IP 114.242.250.59.41034 > 172.17.51.219.443: Flags [.], ack 3538760565, win 2021, options [nop,nop,TS val 555445154 ecr 2928333484], length 0
13:51:41.029955 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [.], ack 466302857, win 2022, options [nop,nop,TS val 555445154 ecr 2928333487], length 0
13:51:41.029957 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [.], ack 466303222, win 2016, options [nop,nop,TS val 555445154 ecr 2928333487], length 0
13:51:41.032718 IP 114.242.250.59.41034 > 172.17.51.219.443: Flags [P.], seq 3150371429:3150371522, ack 3538760565, win 2048, options [nop,nop,TS val 555445155 ecr 2928333484], length 93
13:51:41.033098 IP 172.17.51.219.443 > 114.242.250.59.41034: Flags [P.], seq 3538760565:3538760839, ack 3150371522, win 235, options [nop,nop,TS val 2928333522 ecr 555445155], length 274
13:51:41.033172 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [P.], seq 1328523173:1328523266, ack 466303222, win 2048, options [nop,nop,TS val 555445156 ecr 2928333487], length 93
13:51:41.033374 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [P.], seq 466303222:466303496, ack 1328523266, win 235, options [nop,nop,TS val 2928333522 ecr 555445156], length 274
13:51:41.035404 IP 114.242.250.59.41034 > 172.17.51.219.443: Flags [P.], seq 3150371522:3150372299, ack 3538760565, win 2048, options [nop,nop,TS val 555445156 ecr 2928333484], length 777
13:51:41.035512 IP 172.17.51.219.443 > 114.242.250.59.41034: Flags [P.], seq 3538760839:3538761043, ack 3150372299, win 247, options [nop,nop,TS val 2928333524 ecr 555445156], length 204
13:51:41.065435 IP 114.242.250.59.41034 > 172.17.51.219.443: Flags [.], ack 3538760839, win 2043, options [nop,nop,TS val 555445192 ecr 2928333522], length 0
13:51:41.065920 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [.], ack 466303496, win 2043, options [nop,nop,TS val 555445192 ecr 2928333522], length 0
13:51:41.076415 IP 114.242.250.59.41034 > 172.17.51.219.443: Flags [.], ack 3538761043, win 2044, options [nop,nop,TS val 555445200 ecr 2928333524], length 0
^C
30 packets captured
30 packets received by filter
0 packets dropped by kernel
4 清洗数据

一个TCP链接是由双端定义的 tcp := {sourceHost:port , dstHost:port },所以从上面的输出中过滤出 属于同一个tcp链接的,比较完整的往来数据,比如选择 {172.17.51.219:443 , 114.242.250.59:41033}
同时,首部中的 win部分、options部分 对于验证三次握手无关,同样可以屏蔽.屏蔽完次要信息之后通信流程变的明朗.

[root@ ~]$>tcpdump -i eth0 '((host 172.17.51.219 or 114.242.250.59) and port 443)' -nn -S
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

时间戳                发送端IP:port              接收端IP:port           标志位         发送序列,接收序列 ...   
13:51:40.966228  IP  114.242.250.59.41033   >  172.17.51.219.443:      Flags [S],    seq 1328522649,  length 0
13:51:40.966266  IP  172.17.51.219.443      >  114.242.250.59.41033:   Flags [S.],   seq 466300160, ack 1328522650, length 0
13:51:40.991849  IP  114.242.250.59.41033   >  172.17.51.219.443:      Flags [.],    ack 466300161,  length 0
13:51:40.996812  IP  114.242.250.59.41033   >  172.17.51.219.443:      Flags [P.],   seq 1328522650:1328523173, ack 466300161,  length 523  
13:51:40.996850  IP  172.17.51.219.443      >  114.242.250.59.41033:   Flags [.],    ack 1328523173, length 0
13:51:40.997984  IP  172.17.51.219.443      >  114.242.250.59.41033:   Flags [.],    seq 466300161:466302857, ack 1328523173,  length 2696
13:51:40.997991  IP  172.17.51.219.443      >  114.242.250.59.41033:   Flags [P.],   seq 466302857:466303222, ack 1328523173,  length 365
13:51:41.029955  IP  114.242.250.59.41033   >  172.17.51.219.443:      Flags [.],    ack 466302857,  length 0
13:51:41.029957  IP  114.242.250.59.41033   >  172.17.51.219.443:      Flags [.],    ack 466303222,  length 0
13:51:41.033172  IP  114.242.250.59.41033   >  172.17.51.219.443:      Flags [P.],   seq 1328523173:1328523266, ack 466303222,  length 93
13:51:41.033374  IP  172.17.51.219.443      >  114.242.250.59.41033:   Flags [P.],   seq 466303222:466303496, ack 1328523266, length 274
13:51:41.065920  IP  114.242.250.59.41033   >  172.17.51.219.443:      Flags [.],    ack 466303496, length 0
...
5 分析三次握手

按时间顺序逐条解释报文

13:51:40.966228 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [S], seq 1328522649,  length 0

浏览器向服务器发送创建TCP链接请求, 发送序列号seq=1328522649(逻辑上为1328522649:1328522649代表从第1328522649个字节开始到第1328522649截止),
因为本包的目的为请求创建TCP链接,所以并未在数据部分填充内容.所以 tcpdump解析出实体数据部分 length=0
并且此时将首部Flags中的SYN位设置为1,其它五位(包括ACK位)设为0 ,
当标志位中SYN=1 && ACK=0 时,代表本包是一个连接请求报文, tcpdump工具只会把标志位不为0的部分解析展示在[]中,
比如本次的标志位[S].然后客户端进入SYN-SENT 同步已发送 状态

13:51:40.966266 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [S.], seq 466300160, ack 1328522650, length 0

服务器收到客户端连接请求后,同意建立连接 返回响应报文,同时将标志位中 SYN位、ACK位置为1(ACk标志位用符号点表示,SYNACK组合起来就是[S.]),其它位置为0
SYN=1 && ACK=1 时,表示这个连接接收报文,此时握手还未完成 所以数据长度 length=0
此时服务器要发送确认序列号ack(注意这个ack不是标志位中的ACK),这个ack代表的意思为,服务器期望浏览器下一个报文的发送序列号从ack开始,而这个ack等于上一条浏览器发送服务器的报文中的seq+1.与此同时,服务器也要向浏览器发送数据,所以服务器的发送序列号seq=y(这里是466300160)
代表,服务器要从自己的输出缓存中的第y个字节开始发送.此时服务器进程进入SYN-RCVD 同步收到 状态

13:51:40.991849 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [.], ack 466300161,  length 0

浏览器接收到服务器的响应报文后,验证服务器的确认号ack是否相等(1328522649 + 1 = 1328522650) 通过验证后,
浏览器知晓服务器同意了自己的连接请求,此时浏览器还要向服务器发送应答号 ack=服务器.seq + 1 这一步是为避免第一个请求连接出现异常发生意外情况,
此时TCP连接创建完成.客户端进程进入ESTABLISHED``连接已建立状态.当服务端收到浏览器的确认应答后,也进入ESTABLISHED状态

13:51:40.996812 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [P.], seq 1328522650:1328523173, ack 466300161,  length 523

浏览器发送完连接请求接受的确认ack之后,马上开始发送带有数据的报文,
标志位P设为1 代表,立刻将输出buffer中的数据发送到服务器,不要等待buffer中的数据累积到一定规模再发送,
同时本报文同样要携带ack=服务器.seq + 1
此时浏览器发送的数据长度为 length = seq边界1328522650:1328523173之差:523

13:51:40.996850 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [.], ack 1328523173, length 0
13:51:40.997984 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [.], seq 466300161:466302857, ack 1328523173,  length 2696
13:51:40.997991 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [P.], seq 466302857:466303222, ack 1328523173,  length 365
13:51:41.029955 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [.], ack 466302857,  length 0
13:51:41.029957 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [.], ack 466303222,  length 0
13:51:41.033172 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [P.], seq 1328523173:1328523266, ack 466303222,  length 93
13:51:41.033374 IP 172.17.51.219.443 > 114.242.250.59.41033: Flags [P.], seq 466303222:466303496, ack 1328523266, length 274
13:51:41.065920 IP 114.242.250.59.41033 > 172.17.51.219.443: Flags [.], ack 466303496, length 0
...

这一部分属于常规数据发送接收的通信了,总体来说就是 服务器向浏览器发送一个报文,里面携带数据,浏览器返回一个报文,表示已经收到你刚才发送的数据,
如此往复循环有时 发送跟确认报文可能不是紧挨着的,这是因为TCP的滑动窗口协议.TCP使用确认和重传机制,在不可靠的传输网络上实现可靠的通信

对于IM应用,一个服务端口可以同时与N个客户端口进行通讯,因为服务器端进程维护着一个TCB(Transmission Control Block),这里面存储了每一个连接的
的一些重要信息,比如TCP连接表,指向发送和接收缓存的指针等等…,而不用为每一个来自客户端的TCP连接分配一个端口.

TCP连接的释放

TCP连接的释放比连接创建要稍微复杂一点,通常一个完整的释放过程要交换四组报文.

客户端tcpdump监测结果
[root@ ~]sudo tcpdump -i en0 '((host 39.106.58.35 or 114.242.249.148) and port 443)' -nn -S    
20:18:58.450967 IP 192.168.43.226.54739 > 39.106.58.35.443: Flags [F.], seq 1024508534, ack 2329885927, length 0
20:18:58.520988 IP 39.106.58.35.443 > 192.168.43.226.54739: Flags [F.], seq 2329885927, ack 1024508535,  length 0
20:18:58.521051 IP 192.168.43.226.54739 > 39.106.58.35.443: Flags [.], ack 2329885928, length 0

39.106.58.35是服务端的外网IP ,可以视为172.17.51.219.接下来分析一下报文

20:18:58.450967 IP 192.168.43.226.54739 > 39.106.58.35.443: Flags [F.], seq 1024508534, ack 2329885927, length 0

首先,客户端主动向服务端发送连接释放报文,标志位F设为1,seq1024508534,发送报文后,客户端从ESTABLISHED进入FIN-WAIT-1状态,(这一步可以捎带数据的哈).

20:18:58.520988 IP 39.106.58.35.443 > 192.168.43.226.54739: Flags [.],  seq 2329885927, ack 1024508535,  length 0

服务端接收到释放报文后,向客户端返回确认ack=1024508534+1,同时消耗一个seq=2329885927,发送确认报文后,服务端从ESTABLISHED状态进入CLOSE-WAIT状态
此时 从客户端到服务端这个方向的TCPL连接就单向释放了(TCP是全双工通信,信息发送是双向的).也就是说,客户端已经没有数据要发送到服务端(注意是不能发数据,但是需要发送ACK),
此时TCP连接处于半关闭状态(half-closed),但是服务端向客户端方向的TCP依然的连通的,仍然可以发送数据.

20:18:58.520999 IP 39.106.58.35.443 > 192.168.43.226.54739: Flags [F.], seq 2329885927, ack 1024508535,  length 0

此时,服务端的上层服务进程(比如说Nginx)确定可以没有其他数据需要发送,按协议栈向下传递通知TCP通知对端(也就是客户端浏览器),此时将标志位F置为1,ack跟上一条服务端发送给客户端的
ACK报文中ack值保持一致.此时服务端进入LAST-ACK(最后确认)状态.等待客户端的确认.!!!注意!!!:紧挨着的这两条共享的ack值的报文可以合并为一条报文,F可以捎带上面的ACK.
当收到客户端的确认后,服务端进入CLOSED状态,服务端进入CLOSED状态要早于客户端

20:18:58.521051 IP 192.168.43.226.54739 > 39.106.58.35.443: Flags [.], ack 2329885928, length 0

客户端收到服务端的确认后,客户端从FIN-WAIT-1进入FIN-WAIT-2状态,同时发出确认报文,必须将ACK置为1,然后进入到TIME-WAIT状态,等待2MSL(2 * MSL)后,客户端才进入CLOSED状态.
当客户端撤销了本次的TCB(传输控制块)后,本次TCP连接才真正结束.

这是对应服务端的监测结果,以供对照
[root@ ~]$>tcpdump -i eth0 '((host 172.17.51.219 or 114.242.250.59) and port 443)' -nn -S
20:18:58.495313 IP 114.242.249.148.58188 > 172.17.51.219.443: Flags [F.], seq 1024508534, ack 2329885927,length 0
20:18:58.495476 IP 172.17.51.219.443 > 114.242.249.148.58188: Flags [F.], seq 2329885927, ack 1024508535,length 0
20:18:58.527252 IP 114.242.249.148.58188 > 172.17.51.219.443: Flags [.], ack 2329885928, length 0

有的时候,客户端会因为某种故障突然失去联系,比如断电断网等等,此时服务器还在等待来自客户端的报文,这就要用到TCP的保活计时器(keepalive timer),服务器每一次收到客户端的数据,
就重置timer,通常为两小时,若两小时没有收到来自客户的数据,则发送探测报文段,每隔75秒发送一次,若10个报文段之后仍无响应,服务器就关闭这个连接.
还有一点需要注意,不论客户端还是服务端都可以执行主动关闭,但是通常都为客户端,由服务端主动关闭的例子如:HTTP1.0

重点

重点

  • tcp的每一个报文发送后都需要确认(停止-等待协议).
  • 按照我的理解:一个完整的TCP连接创建释放过程至少需要6个包(这是错误的,看下文解释).
  • 一个好的文章读了以后会给人一种茅塞顿开感觉,但是你要先塞住,才能顿开.

解释上文:6个包意味着挥手需要3个包,而不是4个包,我认为需要三个包是因为我认为挥手时F+ACK是可以合并的.但是事实并非如此.

为什么挥手时需要四次呢?

关闭连接时,当收到对方的FIN报文通知时,他仅仅表示对方没有数据发送给你了,但未必你的所有数据都全部发送给对方了.
所以你可以不是马上回关闭socket,即你可能还会发送一些数据给对方之后,在发送FIN报文给对方来表示你同意现在可以关闭连接了,
所以挥手时的ACK和FIN报文多情况下都是分开发送的.

参考资料:

  • 计算机网络(第7版)
  • Unix网络编程卷1

猜你喜欢

转载自blog.csdn.net/qq_30549099/article/details/104350045
今日推荐