我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Node.js中QUIC协议的示例分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Node.js中QUIC协议的示例分析

这篇文章主要介绍了Node.js中QUIC协议的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

在2019年3月,受到 NearForm 和 Protocol Labs 的支持,我开始为 Node.js 实现 QUIC 协议 支持。这个基于 UDP 的新传输协议旨在最终替代所有使用 TCP 的 HTTP 通信。

Node.js中QUIC协议的示例分析

熟悉 UDP 的人可能会产生质疑。众所周知 UDP 是不可靠的,数据包经常会有丢失、乱序、重复等情况。 UDP 不保证高级协议(例如 HTTP)严格要求的 TCP 所支持的可靠性和顺序。那就是 QUIC 进来的地方。

QUIC 协议在 UDP 之上定义了一层,该层为 UDP 引入了错误处理、可靠性、流控制和内置安全性(通过 TLS 1.3)。实际上它在 UDP 之上重新实现了大多数 TCP 的特效,但是有一个关键的区别:与 TCP 不同,仍然可以不按顺序传输数据包。了解这一点对于理解 QUIC 为什么优于 TCP 至关重要。

【相关推荐:《nodejs 教程》】

QUIC 消除了队首阻塞的根源

在 HTTP 1 中,客户端和服务器之间所交换的所有消息都是连续的、不间断的数据块形式。虽然可以通过单个 TCP 连接发送多个请求或响应,但是在发送下一条完整消息之前,必须先等上一条消息完整的传输完毕。这意味着,如果要发送一个 10 兆字节的文件,然后发送一个 2 兆字节的文件,则前者必须完全传输完毕,然后才能启动后者。这就是所谓的队首阻塞,是造成大量延迟和不良使用网络带宽的根源。

HTTP 2 尝试通过引入多路复用来解决此问题。 HTTP 2 不是将请求和响应作为连续的流传输,而是将请求和响应分成了被称为帧的离散块,这些块可以与其他帧交织。一个 TCP 连接理论上可以处理无限数量的并发请求和响应流。尽管从理论上讲这是可行的,但是 HTTP 2 的设计没有考虑 TCP 层出现队首阻塞的可能性。

TCP 本身是严格排序的协议。数据包被序列化并按照固定顺序通过网络发送。如果数据包未能到达其目的地,则会阻止整个数据包流,直到可以重新传输丢失的数据包为止。有效的顺序是:发送数据包1,等待确认,发送数据包2,等待确认,发送数据包3……。使用 HTTP 1,在任何给定时间只能传输一个 HTTP 消息,如果单个 TCP 数据包丢失,那么重传只会影响单个 HTTP 请求/响应流。但是使用 HTTP 2,则会在丢失单个 TCP 数据包的情况下阻止无限数量的并发 HTTP 请求/响应流的传输。在通过高延迟、低可靠性网络进行 HTTP 2 通信时,与 HTTP 1 相比,整体性能和网络吞吐量会急剧下降。

Node.js中QUIC协议的示例分析

在 HTTP 1 中,该请求会被阻塞,因为一次只能发送一条完整的消息。

Node.js中QUIC协议的示例分析

在 HTTP 2 中,当单个 TCP 数据包丢失或损坏时,该请求将被阻塞。

Node.js中QUIC协议的示例分析

在QUIC中,数据包彼此独立,能够以任何顺序发送(或重新发送)。

幸运的是有了 QUIC 情况就不同了。当数据流被打包到离散的 UDP 数据包中传输时,任何单个数据包都能够以任意顺序发送(或重新发送),而不会影响到其他已发送的数据包。换句话说,线路阻塞问题在很大程度上得到解决。

QUIC 引入了灵活性、安全性和低延迟

QUIC 还引入了许多其他重要功能:

  • QUIC 连接的运行独立于网络拓扑结构。在建立了 QUIC 连接后,源 IP 地址和目标 IP 地址和端口都可以更改,而无需重新建立连接。这对于经常进行网络切换(例如 LTE 到 WiFi)的移动设备特别有用。

  • 默认 QUIC 连接是安全的并加密的。 TLS 1.3 支持直接包含在协议中,并且所有 QUIC 通信都经过加密。

  • QUIC 为 UDP 添加了关键的流控制和错误处理,并包括重要的安全机制以防止一系列拒绝服务攻击。

  • QUIC 添加了对零行程 HTTP 请求的支持,这与基于 TCP 的 TLS 之上的 HTTP 不同,后者要求客户端和服务器之间进行多次数据交换来建立 TLS 会话,然后才能传输 HTTP 请求数据,QUIC 允许 HTTP 请求头作为 TLS 握手的一部分发送,从而大大减少了新连接的初始延迟。

Node.js中QUIC协议的示例分析

为 Node.js 内核实现 QUIC

为 Node.js 内核实现 QUIC 的工作从 2019 年 3 月开始,并由 NearForm 和 Protocol Labs 共同赞助。我们利用出色的 ngtcp2 库来提供大量的低层实现。因为 QUIC 是许多 TCP 特性的重新实现,所以对 Node.js 意义重大,并且与 Node.js 中当前的 TCP 和 HTTP 相比能够支持更多特性。同时对用户隐藏了大量的复杂性。

“quic” 模块

在实现新的 QUIC 支持的同时,我们用了新的顶级内置 quic 模块来公开 API。当该功能在 Node.js 核心中落地时,是否仍将使用这个顶级模块,将在以后确定。不过当在开发中使用实验性支持时,你可以通过 require('quic') 使用这个 API。

const { createSocket } = require('quic')

quic 模块公开了一个导出:createSocket 函数。这个函数用来创建 QuicSocket 对象实例,该对象可用于 QUIC 服务器和客户端。

QUIC 的所有工作都在一个单独的 GitHub 存储库 中进行,该库 fork 于 Node.js master 分支并与之并行开发。如果你想使用新模块,或者贡献自己的代码,可以从那里获取源代码,请参阅 Node.js 构建说明。不过它现在仍然是一项尚在进行中的工作,你一定会遇到 bug 的。

创建QUIC服务器

QUIC 服务器是一个 QuicSocket 实例,被配置为等待远程客户端启动新的 QUIC 连接。这是通过绑定到本地 UDP 端口并等待从对等方接收初始 QUIC 数据包来完成的。在收到 QUIC 数据包后,QuicSocket 将会检查是否存在能够用于处理该数据包的服务器 QuicSession 对象,如果不存在将会创建一个新的对象。一旦服务器的 QuicSession 对象可用,则该数据包将被处理,并调用用户提供的回调。这里有一点很重要,处理 QUIC 协议的所有细节都由 Node.js 在其内部处理。

const { createSocket } = require('quic')const { readFileSync } = require('fs')const key = readFileSync('./key.pem')const cert = readFileSync('./cert.pem')const ca = readFileSync('./ca.pem')const requestCert = trueconst alpn = 'echo'const server = createSocket({  // 绑定到本地 UDP 5678 端口  endpoint: { port: 5678 },  // 为新的 QuicServer Session 实例创建默认配置  server: {    key,    cert,    ca,    requestCert    alpn   }})server.listen()server.on('ready', () => {  console.log(`QUIC server is listening on ${server.address.port}`)})server.on('session', (session) => {  session.on('stream', (stream) => {    // Echo server!    stream.pipe(stream)   })  const stream = session.openStream()  stream.end('hello from the server')})

如前所述,QUIC 协议内置并要求支持 TLS 1.3。这意味着每个 QUIC 连接必须有与其关联的 TLS 密钥和证书。与传统的基于 TCP 的 TLS 连接相比,QUIC 的独特之处在于 QUIC 中的 TLS 上下文与 QuicSession 相关联,而不是 QuicSocket。如果你熟悉 Node.js 中 TLSSocket 的用法,那么你一定注意到这里的区别。

QuicSocket(和 QuicSession)的另一个关键区别是,与 Node.js 公开的现有 net.Sockettls.TLSSocket 对象不同,QuicSocketQuicSession 都不是 ReadableWritable的流。即不能用一个对象直接向连接的对等方发送数据或从其接收数据,所以必须使用 QuicStream 对象。

在上面的例子中创建了一个 QuicSocket 并将其绑定到本地 UDP 的 5678 端口。然后告诉这个 QuicSocket 侦听要启动的新 QUIC 连接。一旦 QuicSocket 开始侦听,将会发出 ready 事件。

当启动新的 QUIC 连接并创建了对应服务器的 QuicSession 对象后,将会发出 session 事件。创建的 QuicSession 对象可用于侦听新的客户端服务器端所启动的 QuicStream 实例。

QUIC 协议的更重要特征之一是客户端可以在不打开初始流的情况下启动与服务器的新连接,并且服务器可以在不等待来自客户端的初始流的情况下先启动其自己的流。这个功能提供了许多非常有趣的玩法,而这在当前 Node.js 内核中的 HTTP 1 和 HTTP 2 是不可能提供的。

创建QUIC客户端

QUIC 客户端和服务器之间几乎没有什么区别:

const { createSocket } = require('quic')const fs = require('fs')const key = readFileSync('./key.pem')const cert = readFileSync('./cert.pem')const ca = readFileSync('./ca.pem')const requestCert = trueconst alpn = 'echo'const servername = 'localhost'const socket = createSocket({  endpoint: { port: 8765 },  client: {    key,    cert,    ca,    requestCert    alpn,    servername  }})const req = socket.connect({  address: 'localhost',  port: 5678,})req.on('stream', (stream) => {  stream.on('data', (chunk) => { /.../ })  stream.on('end', () => { /.../ })})req.on('secure', () => {  const stream = req.openStream()  const file = fs.createReadStream(__filename)  file.pipe(stream)  stream.on('data', (chunk) => { /.../ })  stream.on('end', () => { /.../ })  stream.on('close', () => {    // Graceful shutdown    socket.close()  })  stream.on('error', (err) => { /.../ })})

对于服务器和客户端,createSocket() 函数用于创建绑定到本地 UDP 端口的 QuicSocket 实例。对于 QUIC 客户端来说,仅在使用客户端身份验证时才需要提供 TLS 密钥和证书。

QuicSocket 上调用 connect() 方法将新创建一个客户端 QuicSession 对象,并与对应地址和端口的服务器创建新的 QUIC 连接。启动连接后进行 TLS 1.3 握手。握手完成后,客户端 QuicSession 对象会发出 secure 事件,表明现在可以使用了。

与服务器端类似,一旦创建了客户端 QuicSession 对象,就可以用 stream 事件监听服务器启动的新 QuicStream 实例,并可以调用 openStream()  方法来启动新的流。

单向流和双向流

所有的 QuicStream 实例都是双工流对象,这意味着它们都实现了 ReadableWritable 流 Node.js API。但是,在 QUIC 中,每个流都可以是双向的,也可以是单向的。

双向流在两个方向上都是可读写的,而不管该流是由客户端还是由服务器启动的。单向流只能在一个方向上读写。客户端发起的单向流只能由客户端写入,并且只能由服务器读取;客户端上不会发出任何数据事件。服务器发起的单向流只能由服务器写入,并且只能由客户端读取;服务器上不会发出任何数据事件。

// 创建双向流const stream = req.openStream()// 创建单向流const stream = req.openStream({ halfOpen: true })

每当远程对等方启动流时,无论是服务器还是客户端的 QuicSession 对象都会发出提供 QuicStream 对象的 stream 事件。可以用来检查这个对象确定其来源(客户端或服务器)及其方向(单向或双向)

session.on('stream', (stream) => {  if (stream.clientInitiated)    console.log('client initiated stream')  if (stream.serverInitiated)    console.log('server initiated stream')  if (stream.bidirectional)    console.log('bidirectional stream')  if (stream.unidirectional)    console.log(‘’unidirectional stream')})

由本地发起的单向 QuicStreamReadable 端在创建 QuicStream 对象时总会立即关闭,所以永远不会发出数据事件。同样,远程发起的单向 QuicStreamWritable 端将在创建后立即关闭,因此对 write() 的调用也会始终失败。

就是这样

从上面的例子可以清楚地看出,从用户的角度来看,创建和使用 QUIC 是相对简单的。尽管协议本身很复杂,但这种复杂性几乎不会上升到面向用户的 API。实现中包含一些高级功能和配置选项,这些功能和配置项在上面的例子中没有说明,在通常情况下,它们在很大程度上是可选的。

在示例中没有对 HTTP 3 的支持进行说明。在基本 QUIC 协议实现的基础上实现 HTTP 3 语义的工作正在进行中,并将在以后的文章中介绍。

QUIC 协议的实现还远远没有完成。在撰写本文时,IETF 工作组仍在迭代 QUIC 规范,我们在 Node.js 中用于实现大多数 QUIC 的第三方依赖也在不断发展,并且我们的实现还远未完成,缺少测试、基准、文档和案例。但是作为 Node.js v14 中的一项实验性新功能,这项工作正在逐步着手进行。希望 QUIC 和 HTTP 3 支持在 Node.js v15 中能够得到完全支持。我们希望你的帮助!如果你有兴趣参与,请联系 https://www.nearform.com/cont... !

鸣谢

在结束本文时,我要感谢 NearForm 和 Protocol Labs 在财政上提供的赞助,使我能够全身心投入于对 QUIC 的实现。两家公司都对 QUIC 和 HTTP 3 将如何发展对等和传统 Web 应用开发特别感兴趣。一旦实现接近完成,我将会再写一文章来阐述 QUIC 协议的一些奇妙的用例,以及使用 QUIC 与 HTTP 1、HTTP 2、WebSockets 以及其他方法相比的优势。

James Snell( @jasnell)是 NearForm Research 的负责人,该团队致力于研究和开发 Node.js 在性能和安全性方面的主要新功能,以及物联网和机器学习的进步。 James 在软件行业拥有 20 多年的经验,并且是 Node.js 社区中的知名人物。他曾是多个 W3C 语义 web 和 IETF 互联网标准的作者、合著者、撰稿人和编辑。他是 Node.js 项目的核心贡献者,是 Node.js 技术指导委员会(TSC)的成员,并曾作为 TSC 代表在 Node.js Foundation 董事会任职。

感谢你能够认真阅读完这篇文章,希望小编分享的“Node.js中QUIC协议的示例分析”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网行业资讯频道,更多相关知识等着你来学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Node.js中QUIC协议的示例分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Node.js中QUIC协议的示例分析

这篇文章主要介绍了Node.js中QUIC协议的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在2019年3月,受到 NearForm 和 Protocol Labs
2023-06-14

Linux中DHCP协议的示例分析

这篇文章主要为大家展示了“Linux中DHCP协议的用法”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Linux中DHCP协议的用法”这篇文章吧。我特别喜欢Linux启动的时候屏幕上一行一行的提
2023-06-13

OSPF协议的示例分析

这篇文章主要介绍了OSPF协议的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。OSPF(Open Shortest Path First,开放最短路径优先)是IETF
2023-06-27

前端HTTP协议的示例分析

这篇文章将为大家详细讲解有关前端HTTP协议的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。实际上前端功能会在实际开发中经常的接触浏览器网络相关的,一共四个部分。1、原因如图,那么为什么要在前端进
2023-06-05

Linux TCP/IP协议栈的示例分析

这篇文章将为大家详细讲解有关Linux TCP/IP协议栈的示例分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。TCP特点我们都非常清楚TCP协议设计的初衷,就是保证数据传输的快速,有序,
2023-06-05

Node.js中的示例分析

小编给大家分享一下Node.js中的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Node.js 的非阻塞 I/OI/O 即 Input/Output,一
2023-06-15

PHP通过ICMP协议实现ping的示例分析

小编给大家分享一下PHP通过ICMP协议实现ping的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!PHP通过ICMP协议实现ping(原始套接字)最近想
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录