网络是怎样连接的(十九)—— UDP协议的收发操作

341 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

前言

大多数的应用程序都像前面介绍的一样使用 TCP 协议来收发数据,但当然也有例外。有些应用程序不使用 TCP 协议,而是使用 UDP 协议来说法数据。向 DNS 服务器查询 IP 地址的时候我们用的也是 UDP 协议。

下面就简单介绍一下 UDP 协议。

为什么 TCP 设计如此复杂

TCP 设计复杂的原因是我们需要将数据高效且可靠地发送给对方。为了实现可靠性,我们就需要确认对方是否收到了我们发送的数据,如果没有还需要再发一遍。

要实现上面的要求,最简单的方法是数据全部发送完毕之后让接收方返回一个接收确认。这样一来,如果没收到直接全部重新发送一遍就好了,根本不用像 TCP 一样要管理发送和确认的进度。但是,如果漏掉了一个包就要全部重发一遍,怎么看都很低效。为了实现高效的传输,我们要避免重发已经送达的包,而是只重发哪些出错的或者未送达的包。

TCP 之所以复杂,就是因为要实现这一点。

不过,在某种情况下,即便没有 TCP 这样复杂的机制,我们也能够高效的重发数据,这种情况就是 数据很短,用一个包就能装得下

如果只有一个包,就不用考虑哪个包未送达了,因为全部重发也只不过是重发一个包而已,这种情况下我们就不需要 TCP 这样复杂的机制了。而且,如果不使用 TCP,也不需要发送那些用来建立和断开连接的控制包了。此外,我们发送了数据,对方一般都会给出回复,只要将回复的数据当做接收确认就行了,也不需要专门的接收确认包了。

这些情况就十分适用 UDP。

UDP 头部结构

我们先来看下 UDP 头部结构。UDP 头部结构非常简单,共 8 个字节,其结构如下所示:

字段名称长度(比特)含义
发送方端口号16网络包发送方的端口号
接收方端口号16网络包接收方的端口号
数据长度16UDP 头部后面数据的长度
校验和16用于校验错误

UDP 使用场景

控制用的短数据

像 DNS 查询等交换控制信息的操作基本上都可以在一个包的大小范围内解决,这种场景中就可以用 UDP 来代替 TCP。UDP 没有 TCP 的接收确认、窗口等机制,因此在收发数据之前也不需要交换控制信息,也就是说不需要建立和断开连接的步骤,主要在应用程序获取的数据前面加上 UDP 头部,然后交给 IP 进行发送就可以了。

接收也很简单,只要根据 IP 头部中的接收方和发送方 IP 地址,以及 UDP 头部中的接收方和发送方端口号,找到相应的套接字并将数据交给相应的应用程序就可以了。

除此之外,UDP 协议没有其他功能了,遇到错误或者丢包也一概不管。因为 UDP 只负责单纯地发送包而已,并不像 TCP 一样会对包的送达状态进行监控,所以协议栈也不知道有没有发生错误。但这样并不会引发什么问题,因此出错时就收不到来自对方的回复,应用程序会注意到这个问题,并重新发送一遍数据。这样的操作本身并不复杂,也并不会增加应用程序的负担。

音频和视频数据

还有另一个场景会使用 UDP,就是发送音频和视频数据的时候。音频和视频数据必须在规定的时间内送达,一旦送达完了,就会错过播放时机,导致声音和图像卡顿。

如果像 TCP 一样通过接收确认响应来检查错误并重发,重发的过程需要消耗一定的时间,因此重发的数据很可能已经错过了播放的实际。一旦错过了播放实际,重发数据也是没有用的,因为声音和图像已经卡顿了,这是无法挽回的。当然,我们可以用高速线路让重发的数据能够在规定的时间内送达,但是这样一来可能增加几倍的带宽才行(UDP 经常会被防火墙阻止,因此当需要穿越防火墙传输音频和视频数据时,尽管需要消耗额外的带宽,但有时候也只能使用 TCP)。

此外,音频和视频数据中缺少了某些包并不会产生严重的问题,只是会产生一些失真或者卡顿而已,一般都是可以接受的。

在这些无需重发数据,或者重发了也没什么意义的情况下,使用 UDP 发送数据的效率会更高。

总结

  1. 数据包很短,用一个包就可以装的下的网络包通常适用于 UDP 来传出。

  2. 常见的使用 UDP 来传输网络包的场景有如下两种:

    1. 控制用的短数据
    2. 音频和视频数据

参考文档

  • 《网络是怎样连接的》—— 户根勤

往期文章