如果您真正想知道的是“如何检测丢失的数据包”?那么一般的技术是让接收方对发送的每个数据包发送确认。如果发送方没有收到确认,则必须重新发送数据包。如果接收方收到重复的数据包,那么它应该丢弃重复的数据包。
基本方案是这样的:
TX RX
╷ data
╰──────────────────────────────▶
ack ╷
◄──────────────────────────────╯
╷ data
╰────────────────────── - - - loss of data packet
.
.
. timeout
.
╷ data retransmit
╰──────────────────────────────▶
ack ╷
◄──────────────────────────────╯
╷ data
╰──────────────────────────────▶
ack ╷
- - - ────────────────────╯ loss of ack packet
.
. timeout
.
╷ data retransmit
╰──────────────────────────────▶
ack ╷
◄──────────────────────────────╯
这本质上是所有形式的数据包丢失检测的基础。可以进行一些改进来改进该技术,但基本原理通常是相同的:如果接收器没有告诉您数据包已到达,则数据包将丢失。
通常对算法进行的第一个改进之一是检查确认确实是适当的确认,而不仅仅是路由器或信号交叉干扰或软件错误发送的一些回声。解决方案是实现一个切换位。数据包在将切换位设置为某个值的情况下进行传输,并且 ack 数据包需要使用适当的值(通常是相同的值)进行回复。如果切换位错误,则意味着 ack 数据包与最后一个数据包不匹配,这意味着它与前一个数据包匹配。这意味着最后一个数据包尚未得到确认,这意味着出现了严重错误,应该重新传输数据包,直到收到正确的确认。
TX RX
╷ data[0]
╰──────────────────────────────▶
ack[0] ╷
◄──────────────────────────────╯
╷ data[1]
╰──────────────────────────────▶
ack[0] ╷
◄──────────────────────────────╯ ack mismatch!
╷ data[1] retransmit
╰──────────────────────────────▶
一些现实世界的协议使用这种技术,包括大多数用于控制工业设备和机器人的协议。
下一步实际上是对上述想法的扩展。与其只发送一点,为什么不发送一个数字呢?这样您就可以更明确地将 ack 与数据包匹配,从而更准确地检测哪个数据包丢失并需要重传。这种技术通常被称为滑动窗口技术,因为在某个点上数字会翻转并滑回零。因此,在无法检测到数据包丢失之前可以传输的最大数据包数量就是滑动窗口大小。
滑动窗口技术的一大优点是您可以发送大量数据包而无需等待确认。这显着提高了吞吐量:
TX RX
╷ data[1]
╰──────────────────────────────▶
╷ data[2]
╰──────────────────────────────▶
╷ data[3]
╰──────────────────────────────▶
ack[1] ╷
◄──────────────────────────────╯
ack[2] ╷
◄──────────────────────────────╯
╷ data[4]
╰──────────────────────────────▶
╷ data[5]
╰──────────────────────────────▶
ack[3] ╷
◄──────────────────────────────╯
ack[5] ╷
◄──────────────────────────────╯ ack[4] missing!
.
. timeout
.
╷ data[4] retransmit
╰──────────────────────────────▶
以上是检测丢包的基本技术的简短总结。如果您希望所有 UDP 数据包都到达目的地,这就是您需要实现的。
你应该知道 TCP 已经实现了这一点,所以如果你不想重新发明轮子,你应该真正使用 TCP。创建 UDP 是因为在某些情况下丢包是可以接受的(例如音频/视频流)。