12

实践一次抓包看到TCP的三次握手与四次挥手及其他

 4 years ago
source link: http://www.eryajf.net/4424.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
实践一次抓包看到TCP的三次握手与四次挥手及其他 |坐而言不如起而行! 二丫讲梵
> 术业专攻 > Linux基础 > 系统与优化 > 实践一次抓包看到TCP的三次握手与四次挥手及其他
本文预计阅读时间 11 分钟

直到现在,我甚至都没有真正地去实际操作过抓包这个事儿,可能对一个运维工作者来说,这是不可想象的,然而事实就是这样。

我从来没打算逃避自己不会抓包这事儿,这一点在同事们经常脱口而出抓A抓B,而我往往都默不作声即可验证。当然,另一方面,我也从来没打算完全放弃学习抓包,当工作内容越往网络与协议等的深入,我就越觉得这是一个不可回避的事情了。

前几天一个同事分享了《wireshark网络分析的艺术》这本书给我,让我一下子燃起了对抓包以及网络分析的热情,于是就有了这篇文章。

TCP协议的相关内容非常多非常深,不过面试时三次握手四次挥手则是经常出现的问题,工作中我们在面对以及处理一些TCP相关问题时,也都需要用到这些知识,我始终都不敢说自己掌握的多么熟练,今天,借助于第一次抓包的经历,来分享一下TCP的三次握手以及四次挥手。

通过在主机上使用tcpdump进行抓包,将抓包内容保存到文件中,然后再用wireshark进行分析。

localhost —-> http://eryajf.net/1040.html

以本地作为客户端,然后请求远程网站。

先在本机起一个监听程序:

  1. tcpdump -i ens33 -s 0 -n -S host eryajf.net -w eryajf.cap

然后在本机请求远程主机:

  1. curl http://eryajf.net/1040.html

接着停掉抓包程序,将抓包文件down下来,使用wireshark打开。

image-20200406171706472

图中凭借着个人目前对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 ,人家已经画的足够好,自己就不必在这上头浪费精力了。

三次握手:

format,png

通过三次握手成功建立连接,两端进入数据传输过程。

四次挥手:

format,png-20200406175022701

4,流程浅析

详细说明如下,为了便于对比抓包数据,再次把wireshark的图搬过来:

image-20200406171706472

  • 1.client发起TCP建联请求,通过本机的临时端口34362与远程server80端口通信。标志位为SYN,序列号为seq=x(0), 此处SYN表示客户端请求建立连接。然后,客户端进入SYN_SEND(同步已发送状态)状态,等待服务器的确认。
  • 2.server收到建联请求,通过web端口80client34362端口通信。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1(1),同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN_RCVD(同步收到)状态。
  • 3.TCP客户进程收到确认后,再次向服务器发出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。
    此时可看下图帮助理解
    三次挥手
    图源网络
  • 4.握手完毕,两端都进入ESTABLISHED状态,可以看到client向server端发起了一个HTTP协议(HTTP建联是基于TCP协议的)的GET请求。
  • 5.从info中我们看到了ACK的标志,说明这个包是server回应给client上一个包的请求。
  • 6.这个包同样是从server流向client的,我们在info中看到了,HTTP 301 Moved Permanently,301是一个重定向的状态码,Moved Permanently表明server将请求的资源反馈给client端。前后许多动作都是为了这一步,我们也可以看到这个包的长度为363,是整个请求流程中最大的,表明这次的真正的数据传输。
  • 7.从info中我们再次看到了ACK的标志,说明这个包是client回应给server端表明自己收到了上一个包。整个你来我往的流程就是这样客气。
    注意:当数据传输完毕,客户端不再发起请求,就会进入四次分手阶段。注意分手的话不一定都是客户端先说,因此下边将双方用主机A和主机B来表示。
  • 8.主机A设置Seq和Ack,向主机B发送一个FIN报文段,FIN是关闭连接的标志。此时,主机A进入到FIN_WAIT_1状态,这表示主机A没有数据要发送给主机B了。
    注意:到这个地方需要注意一个细节,因为一些请求的发生时机并非完全顺序执行的,因此可能会有包的记录时间先后顺序不规范的情况。这个地方9与10两个包就应该换一下位置才符合正常分手的程序,不然就成了两个人同时说分手,然后一拍两散了。
  • 9.主机B收到了主机A发送的FIN报文段,向主机A回一个ACK报文段,Ack为Seq都加1,此时主机B进入到CLOSE_WAIT状态,表示我“同意”你的关闭请求。
  • 10.主机A收到B的确认之后,进入FIN_WAIT_2状态,是半关闭状态,即主机A失去发送能力,但是主机B却还能向A发送数据,并且A可以接收数据。此时主机B占主导位置了,如果需要继续关闭则需要主机B来操作了,于是,它操作了,它向主机A发送FIN报文段,请求关闭连接,同时主机B进入LAST_ACK状态。
  • 11.主机A接收到请求后发送ACK确认,然后进入TIME_WAIT状态,等待2MSL之后进入CLOSED状态,而主机B则在接受到确认后即进入CLOSED状态。
    此时可看下图帮助理解
    四次分手
    图源网络

本文基于个人目前对TCP相关知识的理解而写,可能会有错漏的地方,如果有人发现,欢迎指出交流。

5,思维扩展

关于上边内容的与实际工作的关联,我能想到的大概有如下几点。

以往对这块儿的理解不够深入,以为server就启动一个80的服务,然后client直接请求server的这个端口就好了,没想过本机也要启动一个端口。不过话说回来,在理解了之后,就想到端对端通信肯定是要基于两个端口来的,不可能对方起一个80端口,自己就硬生生去请求数据了。

基于此,再扩展一下来看,我们可以通过如下命令查看到CentOS中默认情况下的临时端口分配范围:

  1. [root@eryajf ~]$cat /proc/sys/net/ipv4/ip_local_port_range
  2. 32768 60999

可以看到默认给出的范围是32768-60999,而面对一些实际生产环境,这个范围的端口可能是不够用的,如果不够用,那么超过这个范围的请求就会受到影响。于是,我们可以通过调整内核参数来进行修改:

  1. # 添加如下配置
  2. echo "net.ipv4.ip_local_port_range=10240 65000" >> /etc/sysctl.conf
  3. # 重载生效
  4. sysctl -p

正是基于如上知识的了解以及理解,这里才能够体会此处内核参数调优(特意把这个标红,是为了把这个高大上的词汇平凡化)的意义所在。

2,关注TCP状态

正如前边提到的,以往在我听到TIME_WAIT之类的词汇,常常是有一些迷糊的,并不能准确的定位这个状态是发生在整个请求流程的哪一步了,包括CLOST_WAITESTABLISHED等名词。于是,这次在整理本文时,我特地将各个状态在整个流程中标明,以帮助理解。

基于如上理解,也可以扩展一下,实际生产业务当中,有哪些状态是需要我们重点关注的呢?这些状态的数值究竟达到多少才是我们应该去处理的呢?处理的时候应该怎样操作配置才能对症下药呢?

事实上在过去半年多的工作当中,我们曾多次以TCPPrometheus中的对应状态的波动,来倒推开发回头审视自己的代码中的bug的,以及我们自己对一些配置项的合理度。

这里举几个实际生产中的例子来进行说明,某一天,在进行监控巡检的时候,忽的看到有机器的TCP状态如下图所示:

image-20200406201008485

最开始看到的是当前的数值相当大,接着把时间跨度拉大,发现这一现象是从某一刻开始的,而并非一直这么大,后来开发一查代码,果然是在调用连接池的时候,忘记关闭了,如此一来,连接数自然就会越堆越多了。

还有一个例子是我针对一组服务器的TIME_WAIT状态过多地探析与研究,具体可以参考一下CentOS系统里TCP状态中TIME_WAIT超过3万的分析与建议这篇文章。

再有一次就是某组web服务的机器ESTABLISHED状态相当的多,高峰时几乎接近四万,如果不进行处理,如果某一天突然一大波流量进来,可能直接就占满了,从而系统无法处理超出的连接。

其实连接数过多无非也就那么几种情况,要么是真实连接的确多,要么是没有及时将连接关闭导致,因为是web服务,极有可能配置在NGINX那里控制着,果不其然,我看到了配置中的 keepalive_timeout定义的是300(5分钟),尽管这可能不算很长,但是针对请求量本身就很大的主机来说,显然也是不合理的。

于是我将这个情况与开发进行沟通,表明这个数值需要调小,是否会影响对应的实际业务(针对一些特殊长链的场景,如果猛然调小超时时间,可能会带来其他不可知问题),得到的回应是不会影响,于是果断将超时时间改为60(1分钟),没过多久,就在监控中看到了相应的效果。

image-20200406202728030

很多内容是在我们不经意之间串联着的,当我们一直奔忙在实际工作的任务时,可能有时候反而容易忽略一些简单的东西。

好了,这篇文字东扯葫芦西扯瓢地已经说了不少,该去做点饭填补一下空虚的肚皮了。


weinxin

二丫讲梵 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明实践一次抓包看到TCP的三次握手与四次挥手及其他

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK