面试(一):如何使用 UDP 实现 TCP

898 阅读4分钟

这个问题是在网上偶然看到的,觉得考查角度比较全面。

在回答这个问题前,我们首先要了解 TCP 和 UDP。然后,我们关注于:1. TCP 与 UDP 的差异 2. 如果以 UDP 的条件或基础去实现这些差异。

差异性

我直接从网上找了张参考图。

61T01K.md.jpg

简单归纳下来,我们可以从以下几方面区分差异性:

  1. 面向连接。TCP 在数据传输前需要建立一个可靠的连接,以数据字节流的形式传输数据。UDP 不需要连接,支持多播,广播,以数据报文的形式在网络中传输。
  2. 有序接收数据和重传。TCP 能从机制上保证数据到达的顺序,出现丢包时支持重传。UDP 不支持。
  3. 流量控制和拥塞控制。TCP 在连接的基础上进行点对点通信,发送方可以根据接收方的反馈信息,调整数据流的传输状态。UDP 只管发送。

UDP 实现 TCP

面向连接

TCP 的连接建立是从 “三次握手” 开始,理论上我们也可以模拟这种方式,使用 UDP 发三个包来模拟连接建立。同理,断开连接的 “四次握手”,也可以通过 UDP 发包模拟。

确认与丢包问题

如果接收端收到了数据包,可以做一个确认,发送一个 ACK 给发送端(这个 ACK 参照 TCP)。 如果有的数据包提前到达,接收端可以缓存着。 如果有的数据包丢失,接收方可以设置超时,要求发送端重新发送。超时时间过短(相较RTT),导致过多的重传,超时时间过长,影响传输速度。

顺序问题

所有数据包都有自己的唯一顺序 ID,这部分可能要修改协议头。接收端通过返回 ACK 告诉发送端,“我准备接收某 ID 的数据包”。

流量控制

参照滑动窗口协议,发送端通过 ACK 中夹带的接收端窗口大小,控制自身的发送速率。

会发生死锁吗?

当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。但是如果这个窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。

每当发送者收到一个零窗口的应答后就启动该计时器。时间一到便主动发送报文询问接收者的窗口大小。若接收者仍然返回零窗口,则重置该计时器继续等待;若窗口不为0,则表示应答报文丢失了,此时重置发送窗口后开始发送,这样就避免了死锁的产生。

拥塞控制

拥塞控制应对网络异常的场景,包括网络中出现的丢包和超时。

慢启动算法:一开始使用小流量试探网络质量,指数型增加拥塞窗口,避免拥塞。当窗口大小等于阈值时,窗口改为一个一个增加。 拥塞避免算法:当出现超时(拥塞)时,我们及时将窗口大小调整为当前的一半,然后窗口大小开始慢慢增加,如果再次出现超时(拥塞),那么重复之前的操作。

额外功能: 快重传算法:接收端收到失序的数据包时,立即发出重复确认(不要等到自己的发送数据时稍待确认)。而发送端一脸多次收到重复确认时,就立即重传接收端未收到的数据包,而不必等待重传计时器到期。 快恢复算法:当发送端收到多个重复确认时,此时网络可能没有出现拥塞(因为拥塞的话,可能连重复确认都接收不到),但是为了预防网络拥塞,开始采取拥塞避免算法。