实践一次抓包看到TCP的三次握手与四次挥手及其他
# 1,前言
直到现在,我甚至都没有真正地去实际操作过抓包这个事儿,可能对一个运维工作者来说,这是不可想象的,然而事实就是这样。
我从来没打算逃避自己不会抓包这事儿,这一点在同事们经常脱口而出抓 A 抓 B,而我往往都默不作声即可验证。当然,另一方面,我也从来没打算完全放弃学习抓包,当工作内容越往网络与协议等的深入,我就越觉得这是一个不可回避的事情了。
前几天一个同事分享了《wireshark 网络分析的艺术》这本书给我,让我一下子燃起了对抓包以及网络分析的热情,于是就有了这篇文章。
TCP 协议的相关内容非常多非常深,不过面试时三次握手四次挥手则是经常出现的问题,工作中我们在面对以及处理一些 TCP 相关问题时,也都需要用到这些知识,我始终都不敢说自己掌握的多么熟练,今天,借助于第一次抓包的经历,来分享一下 TCP 的三次握手以及四次挥手。
# 2,抓包
通过在主机上使用tcpdump
进行抓包,将抓包内容保存到文件中,然后再用wireshark
进行分析。
localhost —-> http://eryajf.net/1040.html
以本地作为客户端,然后请求远程网站。
先在本机起一个监听程序:
tcpdump -i ens33 -s 0 -n -S host eryajf.net -w eryajf.cap
然后在本机请求远程主机:
curl http://eryajf.net/1040.html
接着停掉抓包程序,将抓包文件 down 下来,使用 wireshark 打开。
图中凭借着个人目前对 TCP 知识的理解,用红框划分了三个阶段,这三个阶段展示了完整的 TCP 请求的流程。
1–3:是建联时的 TCP 三次握手。
4–7:进入到 HTTP 请求与响应的数据交互过程。
8–11:是结束连接的四次挥手流程。
# 3,见图知意
接下来用大白话浅显的针对每条数据包进行一下简单分析,分析内容中将会依据如上三个阶段进行讲解,并且,因为在这整个过程中,TCP 的状态是在不断变化的,往常我们碰到主机 TIME_WAIT 或者 CLOSE_WAIT 过多的时候,经常头疼于这些名词的含义,因此争取在这次讲解当中也能够将 TCP 的状态对应上,以帮助我们理解那些名词。
讲解之前,先引用两张超级厉害的动图来进行一下概括,首先说明,图来自于 https://blog.csdn.net/qzcsu/article/details/72861891 ,人家已经画的足够好,自己就不必在这上头浪费精力了。
三次握手:
通过三次握手成功建立连接,两端进入数据传输过程。
四次挥手:
# 4,流程浅析
详细说明如下,为了便于对比抓包数据,再次把 wireshark 的图搬过来:
本文基于个人目前对 TCP 相关知识的理解而写,可能会有错漏的地方,如果有人发现,欢迎指出交流。
# 5,思维扩展
关于上边内容的与实际工作的关联,我能想到的大概有如下几点。
# 1,端口
以往对这块儿的理解不够深入,以为server
就启动一个80
的服务,然后 client 直接请求 server 的这个端口就好了,没想过本机也要启动一个端口。不过话说回来,在理解了之后,就想到端对端通信肯定是要基于两个端口来的,不可能对方起一个 80 端口,自己就硬生生去请求数据了。
基于此,再扩展一下来看,我们可以通过如下命令查看到 CentOS 中默认情况下的临时端口分配范围:
[root@eryajf ~]$cat /proc/sys/net/ipv4/ip_local_port_range
32768 60999
2
可以看到默认给出的范围是32768-60999
,而面对一些实际生产环境,这个范围的端口可能是不够用的,如果不够用,那么超过这个范围的请求就会受到影响。于是,我们可以通过调整内核参数来进行修改:
# 添加如下配置
echo "net.ipv4.ip_local_port_range=10240 65000" >> /etc/sysctl.conf
# 重载生效
sysctl -p
2
3
4
正是基于如上知识的了解以及理解,这里才能够体会此处内核参数调优
(特意把这个标红,是为了把这个高大上的词汇平凡化)的意义所在。
# 2,关注 TCP 状态
正如前边提到的,以往在我听到TIME_WAIT
之类的词汇,常常是有一些迷糊的,并不能准确的定位这个状态是发生在整个请求流程的哪一步了,包括CLOST_WAIT
,ESTABLISHED
等名词。于是,这次在整理本文时,我特地将各个状态在整个流程中标明,以帮助理解。
基于如上理解,也可以扩展一下,实际生产业务当中,有哪些状态是需要我们重点关注的呢?这些状态的数值究竟达到多少才是我们应该去处理的呢?处理的时候应该怎样操作配置才能对症下药呢?
事实上在过去半年多的工作当中,我们曾多次以TCP
在Prometheus
中的对应状态的波动,来倒推开发回头审视自己的代码中的 bug 的,以及我们自己对一些配置项的合理度。
这里举几个实际生产中的例子来进行说明,某一天,在进行监控巡检的时候,忽的看到有机器的 TCP 状态如下图所示:
最开始看到的是当前的数值相当大,接着把时间跨度拉大,发现这一现象是从某一刻开始的,而并非一直这么大,后来开发一查代码,果然是在调用连接池的时候,忘记关闭了,如此一来,连接数自然就会越堆越多了。
还有一个例子是我针对一组服务器的 TIME_WAIT 状态过多地探析与研究,具体可以参考一下 CentOS 系统里 TCP 状态中 TIME_WAIT 超过 3 万的分析与建议 (opens new window)这篇文章。
再有一次就是某组 web 服务的机器ESTABLISHED
状态相当的多,高峰时几乎接近四万
,如果不进行处理,如果某一天突然一大波流量进来,可能直接就占满了,从而系统无法处理超出的连接。
其实连接数过多无非也就那么几种情况,要么是真实连接的确多,要么是没有及时将连接关闭导致,因为是 web 服务,极有可能配置在 NGINX 那里控制着,果不其然,我看到了配置中的 keepalive_timeout
定义的是300
(5 分钟),尽管这可能不算很长,但是针对请求量本身就很大的主机来说,显然也是不合理的。
于是我将这个情况与开发进行沟通,表明这个数值需要调小,是否会影响对应的实际业务 (针对一些特殊长链的场景,如果猛然调小超时时间,可能会带来其他不可知问题),得到的回应是不会影响,于是果断将超时时间改为60
(1 分钟),没过多久,就在监控中看到了相应的效果。
很多内容是在我们不经意之间串联着的,当我们一直奔忙在实际工作的任务时,可能有时候反而容易忽略一些简单的东西。
好了,这篇文字东扯葫芦西扯瓢地已经说了不少,该去做点饭填补一下空虚的肚皮了。
# 5,参考

