以下内容来自于小码哥"网络协议从入门到底层原理"
分析一下自己抓包的结果
上图是一个典型的三次握手
我们来分析一下第一次握手
-
第一次握手
第一次握手的数据部分的长度是0
个字节, TCP
首部占44
个字节, 其中固定部分长度是20
个字节, 选项部分长度是24
个字节
TCP
首部存储的是Sequence
的raw
值,而不是相对值 见下图
我们把这个值称为s1
, 这个s1
记录的是客户端->服务器发送的每一个segment
的第一个字节的编号
-
我们来分析一下第二次握手
-
我们来分析一下第三次握手
三次握手后, TCP
连接就成功建立
建立了发数据的通道, 这是一个全双工的通道, 即客户端也可以发给服务器, 服务器也可以发给客户端
-
然后客户端发给服务器
HTTP
请求
-
服务端收到客户端发过来的
HTTP
请求之后, 就会发给客户端一个收到的ACK
响应
-
然后服务端就开始发数据给客户端了
看上图可以知道: 上图的ACK
全部是719
, 这是为什么呢? 719
是ACK
的相对值, 即718 + 1 = 719
, 718
是客户端发给服务器的HTTP
请求的长度, 即TCP
的数据部分, 所以ACK
的raw
值应该是期望客户端发给服务器的下一个包的第一个字节的值, 即s1 + b1 + 1
, 因为在这个过程中, 并没有收到客户端发给服务器的任何数据包, 那么这个值就始终是s1 + b1 + 1
, 相对值就始终是719
.
但是会变化的是序号seq
值, seq
值是上一个包的seq
值, 加上长度Len
生成的, 比如上图中最后一个包的seq
是4771
, 而4771 = 4755 + 16
, 4755
就是上一个服务器发给客户端包的seq
值. 如果把上图这7
个包的长度分别记为k1, k2, k3, k4, k5, k6, k7
的话, 那么最后一个包的seq
相对值就是1 + k1 + k2 + k3 + k4 + k5 + k6
, raw
值就是s2 + 1 + k1 + k2 + k3 + k4 + k5 + k6
,
让我们看看是不是这样, 我们点开最后一个包:
s2
的值果然就是这样
- 接下来就是客户端对服务端的
ACK
回应, 告诉客户端, 我收到包了:
注意看最后一个包的ACK
是6223
, 这就是服务器发给客户端的最后一个包的seq
+ Len
:
- 就这样反反复复, 直到服务端这边已经发完了全部数据, 那么就会给客户端返回一个
200
:
从上面的图可以看到, 客户端这边发的好多包的seq
都为719
, 这是因为客户端发的包都只是一个ACK
回应, 表示: 哦, 我收到了, 这样的包的长度Len
都为0
, 所以seq
都为s1 + b1 + 1
, 就是相对值为719(718 + 1)
, raw
值为2751725119(718 + 1 + s1(2751724400))
到此, 传输层的三次握手就分析完了.
TCP
-建立连接-状态解读
-
CLOSED
:client
处于关闭状态 -
LISTEN
:server
处于监听状态, 等待client
连接 -
SYN-RCVD
: 表示server
接受到了SYN
报文, 当收到client
的ACK
报文后, 它会进入到ESTABLISHED
状态 -
SYN-SENT
:表示client
已发送SYN
报文, 等待server
的第2
次握手
TCP
-建立连接-前2
次握手的特点
SYN
都设置为1
数据部分的长度都为
0
TCP
头部的长度一般是32
字节
-- 固定头部:20
字节
-- 选项部分:12
字节双方会交换确认一些信息
-- 比如MSS
, 是否支持SACK
,Window scale
(窗口缩放系数)等
TCP
-建立连接-疑问
为什么建立连接的时候, 要进行
3
次握手,2
次不行吗?
-- 主要目的: 防止server
端一直等待, 浪费资源如果建立连接只需要
2
次握手, 可能会出现的情况
-- 假设client
发出的第一个连接请求报文段, 因为网络延迟, 在连接释放以后的某个时间才到达server
-- 本来这是一个早已失效的请求, 但server
收到此失效的请求后, 误认为是client
再次发出的一个新的连接请求
-- 于是server
就向client
发出确认报文段, 同意建立连接
-- 如果不采用"3
次握手", 那么只要server
发出确认, 新的连接就建立了
-- 由于现在client
并没有真正想连接服务器的意愿, 因此不会理睬server
的确认, 也不会向server
发送数据
-- 但在server
却以为新的连接已经建立, 并一直等待client
发来数据, 这样,server
的很多资源就白白浪费了采用"三次握手"的办法可以防止上述现象发生
-- 例如上述情况,client
没有向server
的确认发出确认,server
由于收不到确认, 就知道client
并没有要求建立连接第
3
次握手失败了, 会怎么处理?
-- 此时server
的状态为SYN-RCVD
, 若等不到client
的ACK
,server
会重新发送SYN+ACK
包
-- 如果server
多次重发SYN+ACK
都等不到client
的ACK
, 就会发送RST
包, 强制关闭连接