JS `WebTransport` (HTTP/3) 协议:低延迟、多路复用的实时通信

嘿,大家好!今天咱们来聊聊一个听起来很酷炫,用起来更带劲儿的技术——JS WebTransport,也就是基于HTTP/3的低延迟、多路复用的实时通信协议。准备好了吗?咱们这就开讲!

开场白:WebSockets 的那些年,那些事儿…

话说当年,WebSockets 一统天下,解决了Web的实时通信问题。客户端和服务器之间建立一个长连接,数据biu biu biu地就飞过去了,效率杠杠的。但是!人生嘛,总有but。WebSockets 基于 TCP,TCP的那些个问题,它也躲不掉,比如队头阻塞,一个包堵住了,后面的都得等着。而且,HTTP/1.1升级到WebSockets,中间还得握个手,开销也不小。

时代在发展,技术在进步,人们对实时性的要求越来越高,WebSockets 有点hold不住了。于是乎,WebTransport 闪亮登场!

WebTransport:HTTP/3 加持,性能飞起!

WebTransport 是一个基于 HTTP/3 的 API,它允许客户端和服务器之间进行低延迟、双向、多路复用的数据传输。简单来说,它就是 WebSockets 的升级版,但底层换成了 HTTP/3。HTTP/3 又是什么鬼?它基于 UDP 协议,引入了 QUIC 协议。QUIC 解决了 TCP 的队头阻塞问题,而且自带加密,安全性也更高。

WebTransport 的优势:

  • 低延迟: QUIC 协议避免了 TCP 的队头阻塞,延迟更低。
  • 多路复用: 可以在单个连接上创建多个独立的流,互不干扰。
  • 可靠性和不可靠性: WebTransport 既支持可靠的传输,也支持不可靠的传输,可以根据不同的场景选择不同的模式。
  • 双向通信: 客户端和服务器可以同时发送和接收数据。

WebTransport 的两种模式:Datagrams 和 Streams

WebTransport 提供了两种数据传输模式:

  • Datagrams (数据报): 类似于 UDP,不可靠的传输,每个数据报都是独立的,可能会丢失,也可能会乱序。适用于对延迟要求非常高,但可以容忍少量数据丢失的场景,比如游戏。
  • Streams (流): 类似于 TCP,可靠的传输,保证数据按顺序到达,不会丢失。适用于对数据完整性要求高的场景,比如文件传输。

代码实战:WebTransport 的简单使用

光说不练假把式,咱们直接上代码!

服务器端 (Node.js):

首先,你需要一个支持 HTTP/3 的服务器。这里我们使用 quictransport 这个库。

const { QuicListener } = require('quictransport');
const fs = require('fs');

async function main() {
    const options = {
        port: 4433,
        host: '0.0.0.0',
        cert: fs.readFileSync('cert.pem'), // 你的证书文件
        key: fs.readFileSync('key.pem'),   // 你的私钥文件
        alpn: 'wt-03', // 协议名称, 必须是 'wt-03'
    };

    const listener = new QuicListener(options);

    listener.on('session', async (session) => {
        console.log('New session:', session.id);

        session.on('datagram', (datagram) => {
            console.log('Received datagram:', new TextDecoder().decode(datagram));
            session.sendDatagram(datagram); // Echo back
        });

        session.on('stream', async (stream) => {
            console.log('New stream:', stream.id);
            let data = '';
            for await (const chunk of stream) {
                data += new TextDecoder().decode(chunk);
            }
            console.log('Received stream data:', data);
            stream.write(new TextEncoder().encode('Server received: ' + data));
            stream.close();
        });

        session.on('close', () => {
            console.log('Session closed:', session.id);
        });

        session.on('error', (error) => {
            console.error('Session error:', error);
        });
    });

    listener.on('close', () => {
        console.log('Listener closed');
    });

    listener.on('error', (error) => {
        console.error('Listener error:', error);
    });

    await listener.listen();
    console.log('WebTransport server listening on port 4433');
}

main().catch(console.error);

客户端 (浏览器):

async function connect() {
    const url = 'https://localhost:4433/'; // 你的服务器地址
    try {
        const transport = new WebTransport(url, {
            allowInsecure: true // 允许不安全的连接 (仅用于测试!)
        });

        await transport.ready;
        console.log('WebTransport connected!');

        // Datagrams
        transport.datagrams.readable.pipeTo(new WritableStream({
            write(chunk) {
                console.log('Received datagram:', new TextDecoder().decode(chunk));
            }
        }));

        transport.datagrams.writable.getWriter().then(writer => {
            writer.write(new TextEncoder().encode('Hello from client (datagram)!'));
            // writer.close();  datagrams 不需要 close
        });

        // Streams
        const stream = await transport.createUnidirectionalStream();
        const writer = stream.getWriter();
        await writer.write(new TextEncoder().encode('Hello from client (stream)!'));
        await writer.close();

        const reader = transport.incomingUnidirectionalStreams.getReader();
        const { value, done } = await reader.read();
        if (!done) {
            let data = '';
            const streamReader = value.getReader();
            let readResult;
            while (!(readResult = await streamReader.read()).done) {
                data += new TextDecoder().decode(readResult.value);
            }

            console.log('Received stream data:', data); // Output: Server received: Hello from client (stream)!
        }

        // Close connection after 5 seconds
        setTimeout(() => {
            transport.close();
            console.log('WebTransport connection closed');
        }, 5000);

    } catch (error) {
        console.error('WebTransport error:', error);
    }
}

connect();

代码解释:

  • 服务器端:
    • QuicListener 用于监听 HTTP/3 连接。
    • session 代表一个 WebTransport 会话。
    • session.on('datagram', ...) 处理接收到的数据报。
    • session.on('stream', ...) 处理接收到的流。
    • session.sendDatagram(datagram) 向客户端发送数据报(这里是简单的回显)。
    • stream.write(data) 向客户端的流写入数据。
    • stream.close() 关闭流。
  • 客户端:
    • new WebTransport(url) 创建一个 WebTransport 连接。
    • transport.ready 是一个 Promise,当连接建立成功后 resolve。
    • transport.datagrams.readable 是一个 ReadableStream,用于读取接收到的数据报。
    • transport.datagrams.writable 是一个 WritableStream,用于发送数据报。
    • transport.createUnidirectionalStream() 创建一个单向流 (客户端 -> 服务器)。 双向流需要服务器端创建并推送给客户端。
    • transport.incomingUnidirectionalStreams 是一个 ReadableStream,用于读取服务器推送过来的单向流。
    • transport.close() 关闭 WebTransport 连接。

运行代码:

  1. 生成证书: 你需要一个有效的 TLS 证书才能运行 HTTP/3 服务器。 你可以使用 openssl 来生成一个自签名证书 (不推荐用于生产环境!):

    openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes

    在生成证书的过程中,会提示你输入一些信息,可以随便填。

  2. 安装依赖: 在服务器端,你需要安装 quictransport 库:

    npm install quictransport
  3. 运行服务器: 运行服务器端的 JavaScript 文件:

    node server.js
  4. 运行客户端: 将客户端的 JavaScript 代码嵌入到一个 HTML 文件中,然后在支持 WebTransport 的浏览器中打开该 HTML 文件。 Chrome Canary 是一个不错的选择,你需要启用 "Experimental QUIC protocol" 和 "WebTransport" 这两个 flag (在 chrome://flags 中搜索)。

    <!DOCTYPE html>
    <html>
    <head>
        <title>WebTransport Example</title>
    </head>
    <body>
        <h1>WebTransport Example</h1>
        <script src="client.js"></script>
    </body>
    </html>

注意事项:

  • 安全性: 在生产环境中,一定要使用有效的 TLS 证书,不要使用自签名证书。
  • 浏览器支持: WebTransport 仍然是一个比较新的技术,浏览器支持还在不断完善中。 目前 Chrome Canary 支持得最好。
  • 防火墙: HTTP/3 使用 UDP 协议,确保你的防火墙允许 UDP 流量通过。
  • allowInsecure: true: 在客户端代码中,我们使用了 allowInsecure: true 选项,这是为了允许连接到使用自签名证书的服务器。 在生产环境中,不要使用这个选项!

WebTransport 的应用场景:

  • 实时游戏: WebTransport 的低延迟和不可靠传输非常适合实时游戏,可以减少延迟,提高游戏体验。
  • 实时视频: WebTransport 可以用于实时视频流的传输,例如直播、视频会议等。
  • 实时数据推送: WebTransport 可以用于实时数据推送,例如股票行情、新闻推送等。
  • 文件传输: WebTransport 的可靠传输可以用于文件传输,例如 P2P 文件共享。

WebTransport vs WebSockets:一场性能的较量

特性 WebTransport (HTTP/3) WebSockets (HTTP/1.1)
底层协议 QUIC (UDP) TCP
队头阻塞
多路复用 支持 不支持 (需要额外的协议)
可靠性 支持可靠和不可靠 仅支持可靠
延迟
加密 内置 需要 TLS
浏览器支持 逐渐完善 广泛支持

总结:WebTransport 的未来

WebTransport 作为下一代实时通信协议,具有低延迟、多路复用、可靠性和不可靠性等优点,在实时游戏、实时视频、实时数据推送等领域具有广泛的应用前景。虽然目前浏览器支持还在不断完善中,但相信随着 HTTP/3 的普及,WebTransport 将会成为Web开发的重要组成部分。

最后,友情提示:

  • WebTransport 的 API 还在不断变化中,请关注最新的规范文档。
  • 在实际开发中,需要根据具体的应用场景选择合适的数据传输模式 (Datagrams 或 Streams)。
  • WebTransport 的学习曲线相对较陡峭,需要对 HTTP/3 和 QUIC 协议有一定的了解。

希望今天的讲座对大家有所帮助! 咱们下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注