为什么要用udp而不直接用ip(http协议属于哪一层)
- 游戏资讯
- 用户投稿
- 2024-11-07 11:32:58
QUIC协议为什么要基于UDP协议而不是直接基于IP协议?
QUIC协议选择基于UDP而不是直接基于IP协议主要有两个原因:网络路径上的中间盒支持问题和终端上内核支持问题。
网络路径上的中间盒支持问题:纯IP协议在网络上不好传播,因为网络中的许多组件(如NAT、防火墙、LB等)都是基于TCP和UDP的。如果不使用UDP,这些组件就无法识别或正常工作。因此,为了保持网络的兼容性和功能性,QUIC选择了基于UDP的实现方式。
终端上内核支持问题:终端设备(尤其是用户终端)的内核普遍支持UDP,但纯IP协议的派发给用户进程的方式未知。如果使用connectionID来派发,那么内核需要进行相应的调整。这涉及到操作系统内核的重新设计,无论是微软还是用户都不太可能接受这样的改动。因此,为了减少对现有系统和终端设备的改动需求,QUIC选择了基于UDP的实现方式。
QUIC协议选择基于UDP而不是直接基于IP协议,主要是出于对网络兼容性、功能保持以及对现有系统和终端设备影响最小的考虑
现在市面上已经有基于UDP协议实现的可靠传输协议的成熟方案了,那就是QUIC协议,已经应用在了HTTP/3。
这次,聊聊QUIC是如何实现可靠传输的?又是如何解决上面TCP协议四个方面的缺陷?
QUIC是如何实现可靠传输的?
要基于UDP实现的可靠传输协议,那么就要在应用层下功夫,也就是要设计好协议的头部字段。
拿HTTP/3举例子,在UDP报文头部与HTTP消息之间,共有3层头部:
PacketHeader首次建立连接时和日常传输数据时使用的Header是不同的
QUIC也是需要三次握手来建立连接的,主要目的是为了协商连接ID。协商出连接ID后,后续传输时,双方只需要固定住连接ID,从而实现连接迁移功能。所以,你可以看到日常传输数据的ShortPacketHeader不需要在传输SourceConnectionID字段了,只需要传输DestinationConnectionID。
ShortPacketHeader中的PacketNumber是每个报文独一无二的编号,它是严格递增的,也就是说就算PacketN丢失了,重传的PacketN的PacketNumber已经不是N,而是一个比N大的值。
TCP在重传报文时的序列号和原始报文的序列号是一样的,也正是由于这个特性,引入了TCP重传的歧义问题。当TCP发生超时重传后,客户端发起重传,然后接收到了服务端确认ACK。由于客户端原始报文和重传报文序列号都是一样的,那么服务端针对这两个报文回复的都是相同的ACK。
这样的话,客户端就无法判断出是「原始报文的响应」还是「重传报文的响应」,这样在计算RTT(往返时间)时应该选择从发送原始报文开始计算,还是重传原始报文开始计算呢?
RTO(超时时间)是基于RTT来计算的,那么如果RTT计算不精准,那么RTO(超时时间)也会不精确,这样可能导致重传的概率事件增大。
QUIC报文中的PakcetNumber是严格递增的,即使是重传报文,它的PakcetNumber也是递增的,这样就能更加精确计算出报文的RTT。
还有一个好处,单调递增的设计,可以让数据包不再像TCP那样必须有序确认,支持乱序确认,当数据包PacketN丢失后,只要有新的已接收数据包确认,当前窗口就会继续向右滑动,这样就不会因为丢包重传将当前窗口阻塞在原地,从而解决了队头阻塞问题。
每一个Frame都有明确的类型,针对类型的不同,功能也不同,自然格式也不同。
我这里只举例Stream类型的Frame格式,Stream可以认为就是一条HTTP请求,它长这样:
QUIC是如何解决TCP队头阻塞问题的?
在一条QUIC连接上可以并发发送多个HTTP请求(Stream)。
但是QUIC给每一个Stream都分配了一个独立的滑动窗口,这样使得一个连接上的多个Stream之间没有依赖关系,都是相互独立的,各自控制的滑动窗口。
假如Stream2丢了一个UDP包,也只会影响Stream2的处理,不会影响其他Stream,与HTTP/2不同,HTTP/2只要某个流中的数据包丢失了,其他流也会因此受影响。
QUIC是基于UDP传输的,而UDP没有流量控制,因此QUIC实现了自己的流量控制机制,QUIC的滑动窗口滑动的条件跟TCP有一点差别,但是同一个Stream的数据也是要保证顺序的,不然无法实现可靠传输,因此同一个Stream的数据包丢失了,也会造成窗口无法滑动。
QUIC的每个Stream都有各自的滑动窗口,不同Stream互相独立,队头的StreamA被阻塞后,不妨碍StreamB、C的读取。而对于HTTP/2而言,所有的Stream都跑在一条TCP连接上,而这些Stream共享一个滑动窗口,因此同一个Connection内,StreamA被阻塞后,StreamB、C必须等待。
QUIC实现了两种级别的流量控制,分别为Stream和Connection两种级别:
Stream级别的流量控制:Stream可以认为就是一条HTTP请求,每个Stream都有独立的滑动窗口,所以每个Stream都可以做流量控制,防止单个Stream消耗连接(Connection)的全部接收缓冲。
Connection流量控制:限制连接中所有Stream相加起来的总字节数,防止发送方超过连接的缓冲容量。
对于HTTP/1和HTTP/2协议,TCP和TLS是分层的,分别属于内核实现的传输层、openssl库实现的表示层,因此它们难以合并在一起,需要分批次来握手,先TCP握手(1RTT),再TLS握手(2RTT),所以需要3RTT的延迟才能传输数据,就算Session会话服用,也需要至少2个RTT。
HTTP/3在传输数据前虽然需要QUIC协议握手,这个握手过程只需要1RTT,握手的目的是为确认双方的「连接ID」,连接迁移就是基于连接ID实现的。
但是HTTP/3的QUIC协议并不是与TLS分层,而是QUIC内部包含了TLS,它在自己的帧会携带TLS里的“记录”,再加上QUIC使用的是TLS1.3,因此仅需1个RTT就可以「同时」完成建立连接与密钥协商,甚至在第二次连接的时候,应用数据包可以和QUIC握手信息(连接信息+TLS信息)一起发送,达到0-RTT的效果。
如下图右边部分,HTTP/3当会话恢复时,有效负载数据与第一个数据包一起发送,可以做到0-RTT(下图的右下角):
基于TCP传输协议的HTTP协议,由于是通过四元组(源IP、源端口、目的IP、目的端口)确定一条TCP连接。
那么当移动设备的网络从4G切换到WIFI时,意味着IP地址变化了,那么就必须要断开连接,然后重新建立TCP连接。
而建立连接的过程包含TCP三次握手和TLS四次握手的时延,以及TCP慢启动的减速过程,给用户的感觉就是网络突然卡顿了一下,因此连接的迁移成本是很高的。
QUIC协议没有用四元组的方式来“绑定”连接,而是通过连接ID来标记通信的两个端点,客户端和服务器可以各自选择一组ID来标记自己,因此即使移动设备的网络变化后,导致IP地址变化了,只要仍保有上下文信息(比如连接ID、TLS密钥等),就可以“无缝”地复用原连接,消除重连的成本,达到了连接迁移的功能。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 1919100645@qq.com 举报,一经查实,本站将立刻删除。