我们都知道TCP的优点是什么,稳定可考虑全双工等等,记住著名哲学家宇智波鼬的一句话,任何术都有其副作用,也就是说任何优点的实现正好也是其缺点的原因。TCP的稳定可靠依赖的是三次握手、四次挥手、超时重传、拥塞控制、滑动窗口等等逻辑,我们一个个来看:

    1. 三次握手:握手过程消耗时间,慢!半连接设计,导致容易存在洪泛攻击。连接建立后,需要操作系统分配线程、内存等资源进行管理。
    2. 四次挥手:握手过程消耗时间,慢!
    3. 超时重传:TCP针对每一个数据段都有ACK机制,一定时间内未收到ack会再次发送,在弱网环境下,这种现象会非常普遍,导致大量重传数据占用带宽。
    4. 滑动窗口:根据上面描述,TCP根据是否发送、对方是否允许发送、是否ACK隔离出4段状态的数据,分别是未发送且对方不允许发送->未发送对方允许发送->已发送对方未ACK->已发送对方已ACK。可以知道数据应该是不断在这四个状态段中前进的,其中绿色两段称为滑动窗口。
    5. 拥塞控制:网络中由于信息量太大,导致在路由器端有可能存在数据丢包,而由于重传机制,TCP会继续发送导致流量拥塞。类似于交通堵塞,一般这种情况下我们就要考虑,如何减少或降低TCP发送数据到网络的量或频率,所以有两点比较重要,一是如何知道当前网络拥塞了,二是如何控制发送频率。针对第一个问题,一般有两种一个基于发送端自己判断,一个是基于网络层硬件告知,前者可以根据丢包频率判断,后者虽然更准确但需要网络层支持。针对第二个问题,会有一些控制算法不深入描述。 :::info TCP的数据重传 ::: TCP 传输的可靠性是通过序列号和接收方的 ACK 来保证的,当 TCP 传输一个数据段时,它会将该数据段的副本放到重传队列上并开启计时器[14]
    • 如果发送方收到了该数据段对应的 ACK 响应,当前数据段就会从重传队列中删除;
    • 如果发送方在计时器到期之间都没有收到该数据段对应的 ACK,就会重新发送当前数据段;

    TCP 的 ACK 机制可能会导致发送方重新传输接收方已经收到了数据段。TCP 中的 ACK 消息表示该消息之前的全部消息都已经被成功接收和处理,例如:

    1. 发送方向接收方发送了序号为 1-10 的消息;
    2. 接收方向发送方发送 ACK 8 响应;
    3. 发送方认为序号为 1-8 的消息已经被成功接收;

    这种 ACK 的方式在实现上比较简单,更容易保证消息的顺序性,但是在以下情况可能会导致发送方重传已经接收的数据:
    如上图所示,接收方已经收到了序号为 2-5 的数据,但是由于 TCP ACK 的语义是当前数据段前的全部数据段都已经被接收和处理,所以接收方无法发送 ACK 消息,由于发送方没有收到 ACK,所有数据段对应的计时器就会超时并重新传输数据。在丢包较为严重的网络下,这种重传机制会造成大量的带宽浪费。

    :::info TCP的滑动窗口 ::: 对于TCP会话的发送方,任何时候在其发送缓存内的数据都可以分为4类:

    • “已经发送并得到对端ACK的”,数据流中最早的字节已经发送并得到确认。这些数据是站在发送设备的角度来看的。如下图:31个字节已经发送并确认。
    • “已经发送但还未收到对端ACK的”,已发送但尚未得到确认的字节。发送方在确认之前,不认为这些数据已经被处理。如下图:浅蓝色部分。
    • “未发送但对端允许发送的”,如下图,绿色部分。
    • “未发送且对端不允许发送”,如下图,橙色部分。

    接收方允许发送方一次能容纳的未确认的字节数。这称为发送窗口,有时也称为窗口。该窗口决定了发送方允许传送的字节数,也是“已经发送但还未收到对端ACK的”和“未发送但对端允许发送的”这两部之和(中间两部分)。
    当收到接收方新的ACK对于发送窗口中后续字节的确认时,窗口滑动,滑动原理如下图。
    每一次确认接收以后,这一过程都会发生,从而让窗口滑动过整个数据流以供传输。
    2)对于TCP的接收方,在某一时刻在它的接收缓存内存在3种。“已接收”,“未接收准备接收”,“未接收并未准备接收”(由于ACK直接由TCP协议栈回复,默认无应用延迟,不存在“已接收未回复ACK”)。其中“未接收准备接收”称之为接收窗口。