内容开始前,我们摘抄TCP三次握手的内容,抢救一下死去的回忆
- 一次握手 :客户端发送带有 SYN(SEQ=x) 标志的数据包 -> 服务端,然后客户端进入 SYN_SEND 状态,等待服务端的确认;
- 二次握手 :服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1) 标志的数据包 --> 客户端,然后服务端进入 SYN_RECV 状态;
- 三次握手 :客户端发送带有 ACK(ACK=y+1) 标志的数据包 --> 服务端,然后客户端和服务端都进入ESTABLISHED 状态,完成 TCP 三次握手。
当建立了 3 次握手之后,客户端和服务端就可以传输数据啦!
我们知其然了,接下来,我们看看如何知其所以然。
从一个Get请求来探索
我们先从一个最简单的http请求开始说起。我们找一个网络的静态文件,比如
bash
http://h5-sdk.tinytool.top/public/index.css
我们使用浏览器新开一个标签页,请求一下

这就是我们平时的文件请求了,我们可以看到请求的入参,返回的结果。很方便,但浏览器NetWork选项卡是不能直接看到TCP的连接信息的,我们想继续深入,我们得借助一个网卡抓包工具:WireShark。
具体的下载地址和使用方法请参考这个博客:wireshark抓包新手使用教程,这里不额外展开。
Wireshark抓包
我们安装完wireshark后,和一般应用打开,选择我们的使用的对外网卡,我这里是一个wifixxx的标识,选择之后,我们就能看到一堆数据,太多数据了,我们无从下手,得通过方法先筛选一下。
我们可以看到上面是一个域名请求,wireshark的过滤器一般是通过ip来进行过滤的,我们先取一下这个域名解析一下
python
ping h5-sdk.tinytool.top
PING h5-sdk.tinytool.top (43.139.59.79): 56 data bytes
64 bytes from 43.139.59.79: icmp_seq=0 ttl=55 time=10.521 ms
64 bytes from 43.139.59.79: icmp_seq=1 ttl=55 time=15.268 ms
64 bytes from 43.139.59.79: icmp_seq=2 ttl=55 time=8.404 ms
我们得到了ip为: 43.139.59.79 的服务器地址,我们wireshark过滤条件设置为: ip.addr == 43.139.59.79, 然后我们就可以抓包了。
在网页执行一次刷新,我们就可以看到wireshark的抓包界面有具体的信息了。

我们可以看到,在HTTP请求前,存在三个TCP的连接信息,在我框住红色的部分的右边,有相关SYN, SYN+ACK, ACK三条对应的信息,可以很容易和TCP的三次握手对应起来。
翻译一下:
-
172.16.80.14 这个地址是第一个包的原地址(Source), 对应我们握手流程的客户端
-
43.139.59.79 这个地址是第一个包的目标地址(Destination), 对应我们握手流程的服务端
-
SYN: SYNchronize(同步)
-
SYN+ACK: SYNchronize-Acknowledgement(同步-确认)
-
ACK: ACKnowledege(确认)
继续深入分析
好了,我们已经知道怎么去抓三次握手过程中的数据包了,我们拿上面引用内的第一个握手流程继续理解
一次握手 :客户端发送带有 SYN(SEQ=x) 标志的数据包 -> 服务端,然后客户端进入 SYN_SEND 状态,等待服务端的确认;
Okk, 我们继续,第一条数据我们看到,172.16.80.14 先给 43.139.59.79 发了一个TCP的包,这个包内部会携带SYN标识,代表他是握手的第一步。那么他如何携带的呢?我们得先了解一下TCP的报文结构,参考TCP规范文档 RFC793 我们了解到TCP的报文头部结构
css
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
报文的来源:我们都知道数据在底层是以二进制进行传输的,那么一个数据包假如需要携带多个信息的话,应该怎么做呢?很明显,我们需要制定一个规则,让他们按照特定的规则(比如顺序)排列起来,这样通信双方,只要事先只要这个规则,就可以按照这个规则,去将数据隔离开来,翻译一下,就得到了想要的数据,nice~
我们看到上方的的TCP报文有一个SYN的加粗,当报文的这个标志为1的时候,他就代表他属于一个SYN包,用来做握手使用的。Sequence Number代表包的序号。每个数据包都有这个序号。好了我们要数据来实际分析了,我们点击第一条握手信息,在下方会出现好几个数据分析的区域。我们对他先有一个初步的认识

这里的红字是根据网络七层协议标注的他们对应的数据,他们在这里的关系是一层包裹一层,我们只需要关注最后一层数据就行,因为TCP是传输层协议,他的报文信息在这里就可以进行分析,我们对他进行展开。

这里看到的内容比较多,其实和上面的报文基本一一对应,wireshark贴心帮我们标注了他们了的对应的信息,这里我们只需要关注Sequence 和Syn这两个值就可以。 他的SYN标志设置了为1,和我们的理解差不多,这里的Seq的值在这里是2876456852, 按照上方的说法
二次握手 :服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1) 标志的数据包 --> 客户端,然后服务端进入 SYN_RECV 状态;
当二次握手,ACK = x + 1, ACK的值,应该是 2876456852 + 1 也就是 2876456853才对。我们验证一下,我们点击一下第二条握手信息看看数据

可以,所料不错。那么第三次握手的信息也应该如此,数据报文的Acknowledgment报文应该是当前报文的Seq + 1.我们看看
三次握手 :客户端发送带有 ACK(ACK=y+1) 标志的数据包 --> 服务端,然后客户端和服务端都进入ESTABLISHED 状态,完成 TCP 三次握手。
这里的y是第二个握手的Seq的值,这样我们ACK = y + 1 应该为3320095631 + 1 = 3320095632, 我们看一下是不是

这样,我们就了解这个握手协议在数据层是怎么联系起来的了。
网络异常情况
我们可以通过网上资料查询到TCP握手异常情况机制

他在wireshark中是怎么样的呢?我们简单模拟一下第一种场景,我们通过终端简单模拟一下请求一个ipv4地址
bash
curl 185.26.48.56
# curl: (28) Failed to connect to 185.26.48.56 port 80 after 75002 ms: Couldn't connect to server
通过wireshark抓包我们可以看到

我们通过wireshark可以看到抓包数据,我们客户端在一直超时发送SYN包, 符合理论的结果。
结
希望对你理解有所帮助