我开发了一个基于 Beancount 的账本托管服务 HostedBeans,欢迎大家来了解纯文本复式记账并试用我的服务。
查看源代码

笔记:对比TCP与UDP

TCP和UDP是传输层最常用的两种协议,几乎每个应用层协议都要在它们中间选择一个。同样,本文不详述其协议本身,而是着重对比。

显然如果我要是说,TCP是面向连接的可靠传输协议,而UDP无连接且不可靠,那么我这半个月的书算是白读了。

UDP仅仅是在IP的基础上提供了多路分解和多路复用,以及差错校验,十分简单——从UDP报头就能看出。

而TCP提供的功能则要复杂得多,首先为了保证数据能够正确、有序地到达,TCP使用了“流水线”“积累确认”的模型。

TCP会对每个数据报进行编号,同时在每个数据报中,都包含一个“确认序列号”的字段,用来指明上一个从对方正确接收的数据报。一旦接收方在一定时间内没有发送相应的确认序列号,那么发送方就会重传这些数据报。

数据报的发送行为是“流水线”(与之对应的是“逐一确认”)的,即发送方不逐一等待确认,而是一次发送若干个数据报,换句话说,发送和确认的行为是异步的。

而在这里,确认行为是“积累”(与之对应的是“选择重传”)的,也就是说,一旦一个数据报丢失了,那么在这之后的所有数据报都会重传。

TCP还提供有流量控制和拥塞控制的功能。

流量控制即在每个数据报中,都会指明自己的缓冲区还能容纳多少数据,发送方将根据这个值来协调同时存在于“流水线”上的数据量。流量控制保证了,应用层程序不会因为缓冲区不足而无法接收到新数据。当缓冲区满了以后,发送方会暂停发送。

拥塞控制即TCP会根据网络情况,自动调整发送速率。典型策略是每发送成功一个包,即提高一些发送速率,每出现一次重传,即降低一些发送速率。

同时TCP还使用了“慢启动”模型,即建立连接后默认速率是最低的(1MSS/RTT, MSS即最小TCP分段, MTU减去TCP报头), 然后逐渐提升发送速率。

这样可以保证重传行为始终是受控的,不会因为网络不稳定而出现大量重传的数据报而堵塞线路。

TCP往往用于要求数据分毫不差的应用中,比如网页(HTTP), 文件传输(FTP), 电子邮件(SMTP/POP3), 终端(Telnet)等等。

而UDP往往用于可以容忍数据丢失的协议,如流媒体应用, 语音和视频通话等,这些应用即使出现数据丢失,影响也是瞬间的,可以容忍的。

那么为什么这些流媒体应用要选择UDP呢?换言之,TCP在保证可靠传输的同时又牺牲了什么呢?

对于流媒体应用,临时的数据丢失是可以容忍的,但绝不能容忍的是延迟。

为了建立一个TCP连接,TCP需要进行“三次握手”,只有当三次握手完成后,才会正式传输数据。这样一来,建立连接并传输一份数据,至少需要2个RTT(往返延迟), 如果考虑发送端确认的话,那么需要2.5个RTT.

在断开连接时,TCP还需要“四次挥手”,又会消耗掉至少1.5个RTT.

UDP则没有这个顾虑,直接发送数据,只需0.5个RTT.

UDP协议本身保证了,当应用层通过Socket将数据传递给传输层时,UDP会立刻将数据报发送出去。而TCP不提供这个担保,TCP因为提供有流量控制和拥塞控制服务,TCP会自行选择在“合适”的时候再发送数据报。

流媒体应用在需要低延迟的情况下,还需要尽可能的带宽保证。

尤其是因为TCP的拥塞控制服务,注定了TCP不能够尽可能利用带宽,当网络出现堵塞时,TCP的拥塞控制会降低发送速率,让出带宽,直到网络恢复。

而UDP只会简单粗暴地继续发送数据报,很多情况下,TCP都在为UDP让出带宽,这也算是劣币驱逐良币的一种表现,据说有大牛在研究给UDP加上拥塞控制。

总结:那些宁可在UDP上自行实现TCP的部分功能的应用,主要原因在于希望避开TCP的拥塞控制,以便于尽可能地利用带宽。

撰写评论

如希望撰写评论,请发邮件至 jysperm@gmail.com 并注明文章标题,我会挑选对读者有价值的评论附加到文章末尾。

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过 Telegram Channel 订阅我的博客日志、产品和项目的动态:

王子亭的博客 @ Telegram


通过邮件订阅订阅我的博客日志、产品和项目的动态(历史邮件):

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2023-12-20.