「客户端」学习网络编程,不了解TCP协议?难怪面试被刷下去,还不来学习!( 六 )


而两次握手只保证了一方的初始序列号能被对方成功接收 , 没办法保证双方的初始序列号都能被确认接收 。
原因三:避免资源浪费
如果只有「两次握手」 , 当客户端的 SYN 请求连接在网络中阻塞 , 客户端没有接收到 ACK 报文 , 就会重新发送 SYN  , 由于没有第三次握手 , 服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号 , 所以每收到一个 SYN 就只能先主动建立一个连接 , 这会造成什么情况呢?
如果客户端的 SYN 阻塞了 , 重复发送多次 SYN 报文 , 那么服务器在收到请求后就会建立多个冗余的无效链接 , 造成不必要的资源浪费 。

两次握手会造成资源浪费

即两次握手会造成消息滞留情况下 , 服务器重复接受无用的连接请求 SYN 报文 , 而造成重复分配资源 。
小结
TCP 建立连接时 , 通过三次握手能防止历史连接的建立 , 能减少双方不必要的资源开销 , 能帮助双方同步初始化序列号 。 序列号能够保证数据包不重复、不丢弃和按序传输 。
不使用「两次握手」和「四次握手」的原因:
  • 「两次握手」:无法防止历史连接的建立 , 会造成双方资源的浪费 , 也无法可靠的同步双方序列号;
  • 「四次握手」:三次握手就已经理论上最少可靠连接建立 , 所以不需要使用更多的通信次数 。
为什么客户端和服务端的初始序列号 ISN 是不相同的?
因为网络中的报文会延迟、会复制重发、也有可能丢失 , 这样会造成的不同连接之间产生互相影响 , 所以为了避免互相影响 , 客户端和服务端的初始序列号是随机且不同的 。
初始序列号 ISN 是如何随机产生的?
起始 ISN 是基于时钟的 , 每 4 毫秒 + 1 , 转一圈要 4.55 个小时 。
RFC1948 中提出了一个较好的初始化序列号 ISN 随机生成算法 。
ISN = M + F (localhost localport remotehost remoteport)
  • M 是一个计时器 , 这个计时器每隔 4 毫秒加 1 。
  • F 是一个 Hash 算法 , 根据源 IP、目的 IP、源端口、目的端口生成一个随机数值 。 要保证 Hash 算法不能被外部轻易推算得出 , 用 MD5 算法是一个比较好的选择 。
既然 IP 层会分片 , 为什么 TCP 层还需要 MSS 呢?
我们先来认识下 MTU 和 MSS
MTU 与 MSS

  • MTU:一个网络包的最大长度 , 以太网中一般为 1500 字节;
  • MSS:除去 IP 和 TCP 头部之后 , 一个网络包所能容纳的 TCP 数据的最大长度;
如果TCP 的整个报文(头部 + 数据)交给 IP 层进行分片 , 会有什么异常呢?
当 IP 层有一个超过 MTU 大小的数据(TCP 头部 + TCP 数据)要发送 , 那么 IP 层就要进行分片 , 把数据分片成若干片 , 保证每一个分片都小于 MTU 。 把一份 IP 数据报进行分片以后 , 由目标主机的 IP 层来进行重新组装后 , 在交给上一层 TCP 传输层 。
这看起来井然有序 , 但这存在隐患的 , 那么当如果一个 IP 分片丢失 , 整个 IP 报文的所有分片都得重传 。
因为 IP 层本身没有超时重传机制 , 它由传输层的 TCP 来负责超时和重传 。
当接收方发现 TCP 报文(头部 + 数据)的某一片丢失后 , 则不会响应 ACK 给对方 , 那么发送方的 TCP 在超时后 , 就会重发「整个 TCP 报文(头部 + 数据)」 。


推荐阅读