# TCP 协议
# TCP 协议头部
TCP 头部信息出现在每一个 TCP 报文段中,用于指定通信的源端端口,目的端口,管理 tcp 连接等;如下图所示
- 16 位的端口:告诉主机该报文来源于哪个端口以及传给上一层协议的目的端口
- 32 位序列号:在一次 TCP 通信过程中某一方向上的每个字节编号;在最开始建立连接时候序列号将被初始化为一个随机值,后续的数据传输字节大小将在这个初始值上往后偏移
- 32 位应答号:在收到 32 位序列号之后,将 32 位应答号设置为收到的序列号 + 1
- 4 位头部长度:标识该 TCP 头部有多少个 32bit (4 字节),4 位能表示的最大值为 15,所以 TCP 头部最大为 60 字节
- 标记位
- URG:表示紧急指针是否有效;它 使 一 端 可 以 告 诉 另 一 端 有 些 具 有 某 种 方 式 的 “紧急数据” 已经放置在普通的数据流中。另一端被通知这个紧急数据已被放置在普通数据流中,由接收方决定如何处理
- ACK:表示确认号是否有效
- PSH:表示接受方应尽快将数据提交给下一层,为后续数据腾出空间
- RST:重新建立连接并发送复位报文段
- SYN:请求建立连接;携带 SYN 标志位的同步报文段
- FIN:表示通知对方将要关闭连接标志位
- 六位保留标志位:方便日后扩展补充需要进行保留
- 16 位校验和:由发送端填充,接收端对 TCP 报文段执行 CRC 算法校验 TCP 报文段在传输过程中是否损坏
- 16 位紧急指针:和序列号类似的,对紧急数据的字节大小偏移量,其值为当前序列号的大小加上紧急数据的偏移量
- 选项:该字段是一个可变长的可选字段,主要是携带一些数据,这部分的长度最多为 40 字节
# 三次握手
- 一开始,客户端和服务端都是关闭状态,然后服务端监听某一个端口
- 客户端随机初始化一个序列号值 X,将此序列号值填入 TCP 头部的序列号位置,并且将 SYN 标志位为 1,然后向服务端发起连接,该连接不包含应用层数据;客户端状态转换为 SYN-SENT 状态
- 服务端收到客户端发来的 SYN 报文之后,也初始化一个随机的序列号值 Y,并填入到 TCP 头部的序列号位置,然后把客户端发来的序列号值 X+1 填入到应答号位置,接着把 ACK 和 SYN 标志位为 1;最后把该报文发送给客户端;该报文也不包含应用层数据,服务器状态转换为 SYN-RCVD 状态
- 客户端收到服务端发来的报文之后,首先把 ACK 标志为 1 表示应答,然后把确认应答号位值填入服务端发来的值 Y+1,然后把此次报文发送给服务端,这次报文可以携带数据。然后客户端的状态更改为 ESTABLISHED
- 服务端收到报文之后也进入 ESTABLISED 状态
tip:之所以收到之后ACK需要+1是因为发送方包含了SYN或者FIN标志位时候占用了一个序号;并且带有SYN或者FIN标志位的报文段是不允许携带数据
为什么需要握手三次
避免历史连接
客户端连续发送多次 SYN 建立连接,在网络拥堵的情况下,一个旧的报文比新的报文先到达服务端,这时服务端会回一个 SYN+ACK 的报文给客户端,客户端收到该报文之后和自己之前发送的 SYN 值进行对比即可知道是否是超时连接;
同步双方的序列号
TCP 通信的过程中,双方都需要维护一个序列号进行可靠传输
避免资源浪费
如果只有两次连接的话,当客户端发起 SYN 连接之后发生了网络拥堵,迟迟没有收到服务端的 ACK 报文那么就会继续发送 SYN 报文;由于没有第三次握手服务端不知道客户端是否收到了自己的 ACK 建立连接请求,只能让每一个 SYN 连接都进行建立连接,这样子就会造成很多的无效连接
# 四次挥手
- 当客户端准备关闭连接时候,会发送一个 TCP 标志位 FIN 为 1 的报文,携带序列号 (握手时初始序号 + 发送的字节数据数量 + 1) u 给服务端,然后进入 FIN_WAIT1
- 服务端收到该报文之后,就向客户端发送一个 TCP 标志位 ACK 为 1 的应答报文,已经服务端的序列号给客户端,然后进入 CLOSE_WAIT 状态
- 客户端收到 ACK 应答报文之后进入 FIN_WAIT2 状态
- 然后服务端处理完数据后,继续向客户端发送一个标志位为 FIN 的报文,初始化一个随机的序列号 w 给客户端,然后进入 LASR_ACK 状态
- 客户端收到来自服务端的 FIN 报文之后,向服务端发送 ACK 标志位为 1 的应答报文,然后进入 TIME_WAIT 状态,等待 2MSL 之后进入 CLOSE 状态
- 服务端收到客户端的 ACK 报文之后就直接进入 CLOSE 状态
为什么需要等待 2MSL
只有主动发起关闭的一方才有 TIME_WAIT 状态
需要 TIME_WAIT 状态的主要原因是:
- 为了保证客户端发送的最后一个 ACK 报文段能够顺利到达服务端;因为这个报文段可能会丢失,因此客户端就无法收到服务端发送的第三次挥手的 FIN 报文段;这时候服务端会进行超时重新发送;客户端就可以在 2MSL 之内收到这个重传的报文段,然后重启 2MSL 计时器
- 防止已失效的连接请求报文段出现在本连接中。客户端发送完最后一个 ACK 报文段之后,再经过 2MSL 之后就可以使得在本次连接当中产生的所有报文段消失,在下一个连接当中就不会出现本次的旧报文子段
TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 FIN 报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。
2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。