likes
comments
collection
share

TechBits | HTTP 3 中的 UDP

作者站长头像
站长
· 阅读数 21

UDP

UDP(User Datagram Protocol)是一种无连接的传输层协议,它不提供像TCP一样的可靠性和流控制,但它具有更低的开销和更快的传输速度,适合在要求实时性较高,但可靠性要求不高的场景下使用

UDP协议原理:

  1. 无连接:UDP协议是一种无连接的协议,它在发送数据之前不需要先建立连接,因此发送数据的过程非常快。UDP不像TCP那样需要先建立连接,维护连接状态,释放连接等,因此UDP可以快速发送数据,并且具有更低的开销。
  2. 不可靠:UDP协议不提供可靠性保证,也就是说,当数据包发送出去后,UDP无法保证数据包一定会被接收方接收到。如果数据包在传输过程中丢失、重复、乱序,UDP无法检测到并进行重传,因此UDP不适合在对数据可靠性要求比较高的场景中使用。
  3. 简单:UDP协议的头部非常简单,只有8个字节,包含源端口、目标端口、长度和校验和等信息。由于UDP头部非常简单,因此UDP的开销比较小,传输效率比较高。
  4. 支持广播和多播:UDP协议支持向网络中的所有设备广播数据,也支持向一组设备发送数据,即多播。这使得UDP在实现多播和广播等应用时比TCP更具优势。

UDP协议格式:

UDP头部和UDP数据

UDP头部格式如下:

源端口号目标端口号长度校验和
2 字节2 字节2 字节2 字节

其中,各字段的含义如下:

  • 源端口号和目标端口号:每个UDP数据包都包含源端口号和目标端口号。源端口号和目标端口号都是16位的整数,用于标识发送方和接收方的应用程序。
  • 长度:该字段指定了UDP数据包(包括头部和数据)的长度,以字节为单位。因为UDP数据包的长度不能超过65535个字节,所以该字段只需要占用2个字节。
  • 校验和:该字段用于校验UDP数据包是否在传输过程中发生了错误。UDP协议的校验和是可选的,如果不需要校验,则该字段的值为0。

UDP数据部分可以是任意长度的数据,最大长度为65535个字节(即2^16-1字节),没有规定数据的格式和内容。UDP数据包的总长度等于UDP头部长度加数据长度。

UDP数据包的格式简单明了,头部长度只有8个字节,因此UDP协议的数据包大小比TCP协议的数据包要小。这使得UDP协议在网络传输中比TCP协议更加高效。

端口:

  1. 公认端口

公认端口是由IANA(Internet Assigned Numbers Authority)管理的端口号,它们的范围是0到1023。公认端口是被广泛使用的端口,常用于标准的网络服务,例如HTTP(80)、FTP(21)和Telnet(23)等。

  1. 注册端口

注册端口是由IANA分配的端口号,范围从1024到49151。注册端口用于应用程序的特定服务,这些服务是由应用程序开发人员自己定义的。

  1. 动态端口

动态端口的范围是从49152到65535。动态端口是由客户端操作系统自动分配的,用于发送数据包的源端口。当客户端应用程序发送UDP数据包时,它们不需要提前知道自己的源端口,操作系统会自动分配一个未被使用的动态端口。

Java中的UDP编程:

  1. DatagramSocket类:用于建立UDP连接,并进行数据的发送和接收操作。
  2. DatagramPacket类:用于封装数据包,包括数据、目标地址和端口等信息。
import java.net.*;
​
public class UDPSender {
    public static void main(String[] args) {
        try {
            DatagramSocket socket = new DatagramSocket(); // 创建DatagramSocket对象
            String message = "Hello, World!"; // 要发送的消息
            byte[] data = message.getBytes(); // 将消息转换为字节数组
​
            InetAddress address = InetAddress.getByName("127.0.0.1"); // 目标地址
            int port = 8888; // 目标端口号
​
            DatagramPacket packet = new DatagramPacket(data, data.length, address, port); // 创建DatagramPacket对象
​
            socket.send(packet); // 发送数据包
​
            byte[] buffer = new byte[1024]; // 创建缓冲区
​
            DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length); // 创建接收数据包
​
            socket.receive(receivePacket); // 接收响应数据包
​
            String response = new String(receivePacket.getData(), 0, receivePacket.getLength()); // 获取响应消息
​
            System.out.println("Response: " + response); // 输出响应消息
​
            socket.close(); // 关闭DatagramSocket对象
​
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
​

创建了一个DatagramSocket对象,然后将需要发送的消息转换为字节数组,并使用DatagramPacket对象封装需要发送的数据、目标地址和端口等信息。然后使用DatagramSocket的send()方法将数据包发送到目标地址。

创建了一个缓冲区和一个DatagramPacket对象用于接收来自目标地址的响应消息。最后,使用DatagramSocket的receive()方法接收来自目标地址的响应消息,并使用DatagramPacket的getData()方法获取响应消息,然后输出响应消息。最后,关闭了DatagramSocket对象

HTTP 3

HTTP/3是一种基于UDP协议的新一代Web协议,它采用了QUIC协议作为传输层协议。QUIC(Quick UDP Internet Connections)是一种基于UDP协议的快速可靠传输协议。

  1. 可靠传输:尽管UDP本身是一种不可靠的协议,但是QUIC协议在其上面增加了可靠性保证机制,确保了数据的可靠传输。因此,在HTTP/3中,UDP协议作为QUIC协议的底层传输协议,提供了可靠的数据传输保障。
  2. 快速连接建立:QUIC协议使用了0-RTT技术,可以在客户端和服务器之间建立快速连接,避免了TCP连接建立时的握手延迟。这对于HTTP/3的性能提升非常重要,而UDP协议正是QUIC协议的底层传输协议,支持了这一特性。
  3. 低延迟和高带宽利用率:UDP协议相比TCP协议具有更低的延迟和更好的带宽利用率。在HTTP/3中,采用了基于UDP的QUIC协议作为传输层协议,可以大幅度降低网络延迟,提升带宽利用率,进一步提升了HTTP/3的性能。
  4. 兼容性:HTTP/3在设计上兼容了HTTP/2,并且在HTTP/2的基础上做出了一些改进。采用UDP协议作为QUIC协议的底层传输协议,可以保证HTTP/3的兼容性,同时具有更好的性能表现。

QUIC协议核心内容:

QUIC的设计灵感来自于TCP+TLS+HTTP/2的组合,但它将所有这些协议合并为一个单一的协议,从而避免了每个协议之间的不必要的交互。QUIC提供了以下特性:

  1. 加密:所有的QUIC数据都加密传输,防止中间人攻击和数据窃取。

QUIC的加密采用了TLS 1.3协议的加密机制,包括前向安全、完全性保护、抗重放攻击等特性。在QUIC中,每个连接都有一个唯一的连接ID和一个加密密钥。当客户端第一次发送数据包时,它会发送一个未加密的初始数据包,其中包含了一个随机数和客户端的支持的加密套件列表。服务器收到这个数据包后,会根据客户端的支持列表选择一个加密套件,然后发送一个包含服务器的证书、服务器的随机数和加密套件的选择的数据包。

旦双方协商好了加密套件,就会开始进行密钥交换。QUIC采用了一种称为“0-RTT”的特性,可以在建立连接时就发送数据,而无需等待完整的握手过程完成。具体地,客户端可以在未建立连接时就把数据发送给服务器,同时使用一个预共享密钥进行加密,这个预共享密钥是之前已经建立的连接使用的密钥。如果服务器能够正确地解密数据包,并且认为这个连接是合法的,那么它就会发送一个确认数据包,然后这个连接就正式建立了。

一旦连接建立完成,所有的数据都会进行加密传输。QUIC采用的是AEAD(Authenticated Encryption with Associated Data)模式的加密算法,可以同时提供数据完整性保护和机密性保护。QUIC还支持0-RTT数据传输,这意味着客户端可以在建立连接时就发送数据,而无需等待完整的握手过程完成。这可以进一步提高传输性能,但也会带来安全风险,因此需要谨慎使用。

  1. 多路复用:QUIC支持在一个连接上同时传输多个请求和响应,从而避免了TCP的头阻塞问题。

在传统的HTTP/1.1协议中,每个HTTP请求都需要建立一个独立的TCP连接,这会导致TCP头阻塞问题,即每个请求必须等待前面的请求完成,才能继续传输,导致网络资源的低效利用和传输延迟。

而HTTP/2协议引入了多路复用的机制,可以在一个TCP连接上同时传输多个HTTP请求和响应,从而避免了TCP头阻塞问题,提高了网络资源的利用率和传输性能。但HTTP/2仍然使用TCP协议作为传输层协议,会受到TCP头阻塞和队头阻塞的影响。

而QUIC协议则将多路复用和UDP协议结合起来,可以更好地解决TCP头阻塞和队头阻塞问题。QUIC通过为每个数据包添加数据流标识符来支持多路复用。每个数据流都是一个独立的逻辑通道,可以承载一个或多个HTTP请求和响应。

在QUIC中,每个数据流都有一个唯一的标识符,用于标识这个数据流所承载的请求和响应。客户端可以在一个连接上同时打开多个数据流,并且每个数据流都可以独立地传输数据。服务器可以通过标识符来识别每个数据流,并将响应数据发送到对应的数据流上。

  1. 0-RTT:QUIC支持0-RTT连接建立,可以让客户端在不验证服务器身份的情况下建立连接,并立即发送数据,从而进一步提高传输性能。

0-RTT(Zero Round-Trip Time)是QUIC协议的一种特性,可以在建立连接时就发送数据,而无需等待完整的握手过程完成。具体来说,客户端可以在未建立连接时就把数据发送给服务器,同时使用一个预共享密钥进行加密,这个预共享密钥是之前已经建立的连接使用的密钥。

在使用0-RTT特性时,客户端发送的数据会带有一个“0-RTT”标记,服务器会优先处理这些数据,并尽可能快地发送响应。这样可以进一步缩短请求和响应的时间,提高传输性能。

但是0-RTT也存在一些安全风险。因为这些数据是在建立连接之前发送的,所以服务器无法对客户端进行身份验证。如果攻击者拦截了这些数据,并冒充客户端向服务器发送了一些请求,服务器就会误认为这些请求是合法的,从而造成安全漏洞。

因此,使用0-RTT时需要注意安全问题,建议只在对安全性要求不高的场景下使用,或者采用其他安全机制来加强安全保障。同时,服务器也可以通过配置来限制客户端使用0-RTT的次数和发送的数据量,以降低安全风险。

  1. 拥塞控制:QUIC实现了一种基于最小RTT(Round-Trip Time)的拥塞控制算法,可以快速适应网络环境的变化。

  2. 快速重传:QUIC支持快速重传,当数据包丢失时可以快速重传,而不必等待超时重传。

HTTP 3 的生命周期

ClientServer发送HTTP/3请求建立QUIC连接发送QUIC连接请求发送QUIC连接响应发送加密密钥请求发送加密密钥响应发送HTTP/3请求数据 (0-RTT)发送HTTP/3响应数据 (0-RTT)(加密)发送HTTP/3请求数据发送HTTP/3响应数据关闭HTTP/3连接关闭QUIC连接ClientServer

客户端首先向服务器发送HTTP/3请求,然后使用QUIC协议在UDP协议上建立连接。接下来,客户端和服务器之间进行握手,包括QUIC连接请求和响应,以及加密密钥请求和响应。一旦连接建立并且双方都准备好,客户端就会发送HTTP/3请求数据,服务器会响应HTTP/3响应数据。最后,当连接不再需要时,客户端和服务器都会关闭连接,结束通信

支持的操作系统和浏览器

HTTP/3是一项相对较新的技术,它需要较新版本的操作系统和浏览器才能支持。目前,只有最新版本的操作系统和浏览器才支持HTTP/3,例如:

  • Windows 10和Windows Server 2022(使用IIS或Nginx 服务器)
  • Linux(使用Nginx或Caddy服务器)
  • macOS Big Sur或更高版本(使用Safari浏览器)
  • Google Chrome 94或更高版本
  • Mozilla Firefox 91或更高版本