Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >s2n-quic: 终于有一个比较好用的 QUIC 实现了

s2n-quic: 终于有一个比较好用的 QUIC 实现了

作者头像
tyrchen
发布于 2022-03-29 02:14:45
发布于 2022-03-29 02:14:45
2.9K03
代码可运行
举报
文章被收录于专栏:程序人生程序人生
运行总次数:3
代码可运行

QUIC 是一种为性能而设计的加密传输协议,它是尚处在襁褓之中的 HTTP/3(最新是 draft-34)的基础。根据维基百科的资料显示,QUIC 在 2012 年就被部署到 Google 内部,并于 2013 年对外发布。2021 年 5 月,IETF 在 RFC 9000 中对 QUIC 的基本功能进行了标准化,并在 RFC 9001 中标准化了如何使用 TLS 保护 QUIC,以及 RFC 9002 中标准化了 QUIC 的拥塞控制。QUIC 通过使用在 QUIC 传输中承载的通过 TLS 建立的加密和身份验证密钥来保护其 UDP 数据报文。它旨在通过提供改进的首字节延迟,多路复用,以及解决诸如线头阻塞、移动性和数据丢失检测等问题来改进 TCP

我们知道,即便在 TLS 1.3 中改进了握手的效率,一个 HTTPS(TCP + TLS)完整的握手过程需要 2 个 roundtrip,包括 TCP 三次握手头两个包 SYN - SYN/ACK 一个 roundtrip,以及后续的 ACK/HELLO - ACK/HELLO/CERT/Finished 一个 roundtrip:

所以,Web 应用的延迟还是非常可观。如果使用 QUIC,QUIC/TLS 握手可以同步进行,于是可以省掉 TCP 三次握手的一次 roundtrip,一个 roundtrip 就可以建立安全的加密信道:

这就使得 Web 应用程序能够更快地执行,尤其是在网络较差的情况下。

除了更短的延迟外,QUIC 还有一个很重要的特性是传输层的多路复用,也就是在同一个连接中打开多个互不干扰的 stream(流)。以前我们要支持多路复用,需要 TCP + Yamux,现在可以用 QUIC 更高效地在传输层完成这个功能,解决了队头阻塞的问题。

s2n-quic 是什么?

上周 Amazon 开源了 s2n-quic 这个 QUIC 协议的软件包。s2n-quic 用 Rust 撰写,可见 Amazon/AWS 对 Rust 不断投入的决心。s2n 这个名字是Signal to Noise 的缩写,是对我们生活中无处不在的信息加密的致敬 —— 因为加密是一种将有意义的信号(Signal)伪装成看似随机的噪声(Noise)的行为。Amazon 有好几个 s2n 打头的开源软件,包括 s2n-tls 和 s2n-bignum。s2n-tls 是用 C 撰写的 TLS 实现,它被 s2n-quic 默认使用(s2n-quic 也可以用 Rustls)。

别看 Rust 是一门相对年轻的语言,但很多新兴的基础领域的代码,都在用 Rust 撰写。比如哈希算法中比较新的 blake3,就是用 Rust 撰写并移植到其它语言。对于 QUIC 来说,Rust 下已经有了 Cloudflare 的 quiche,社区开发的 quinn,还有今天发布的 s2n-quic。三者中 s2n-quic 的接口最为易用,非常值得一试。

如何使用 s2n-quic?

我们看如何实现一个简单的 echo clien/server。首先是服务器实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let mut listener = Server::builder()
        .with_tls((CERT_PEM, KEY_PEM))?
        .with_io("127.0.0.1:4433")?
        .start()?;

    while let Some(mut conn) = listener.accept().await {
        // spawn a new task for the connection
        tokio::spawn(async move {
            eprintln!("Connection accepted from {:?}", conn.remote_addr());

            while let Ok(Some(mut stream)) =  conn.accept_bidirectional_stream().await {
                // spawn a new task for the stream
                tokio::spawn(async move {
                    eprintln!("Stream opened from {:?}", stream.connection().remote_addr());

                    // echo any data back to the stream
                    while let Ok(Some(data)) = stream.receive().await {
                        stream.send(data).await.expect("stream should be open");
                    }
                });
            }
        });
    }

    Ok(())
}

这和 Tokio TCP 的使用方法几乎一样,比 Tokio TCP + TLS 还要简单一些。值得注意的是,由于 QUIC 支持多路复用,所以 accept 一个 conn 之后,这个 conn 并不是一个 Stream,而是需要进一步调用 conn.accept_bidirectional_stream() 得到一个可以收发数据的 stream。

客户端的实现也非常类似:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let client = Client::builder()
        .with_tls(CERT_PEM)?
        .with_io("0.0.0.0:0")?
        .start()?;

    let addr: SocketAddr = "127.0.0.1:4433".parse()?;
    let connect = Connect::new(addr).with_server_name("localhost");
    let mut conn = client.connect(connect).await?;

    // ensure the connection doesn't time out with inactivity
    conn.keep_alive(true)?;

    // open a new stream and split the receiving and sending sides
    let stream = conn.open_bidirectional_stream().await?;
    let (mut rx, mut tx) = stream.split();

    // spawn a task that copies responses from the server to stdout
    tokio::spawn(async move {
        let mut stdout = tokio::io::stdout();
        let _ = tokio::io::copy(&mut rx, &mut stdout).await;
    });

    // copy data from stdin and send it to the server
    let mut stdin = tokio::io::stdin();
    tokio::io::copy(&mut stdin, &mut tx).await?;

    Ok(())
}

注意这里 client 和 conn 是分开的概念,一个 client 可以建立多个 conn。然后一个 conn 又可以打开多个 stream 进行收发。

在体验了简单的 echo client/server 后,我感觉 s2n-quic 把 QUIC 协议的使用门槛大大降低,我们可以用和处理 TCP client/server 相同结构的代码,来处理 QUIC。

为了进一步体验 QUIC 和已有项目的结合,我尝试着把我之前为极客时间的《Rust 第一课》做的示范性的类似 redis 的 kv server(github 下 tyrchen/simple-kv)添加了 QUIC 的支持。我发现,只消添加两三百行代码,我的 simple-kv 就能很好地在已有支持 TCP + TLS + Yamux 的基础上,扩展支持 QUIC。如果你感兴趣的话,可以去看看那个 repo,代码我已提交。

如果你觉得文字描述代码比较生硬,你也可以去 B 站看我的视频。我做了两个关于 s2n-quic 的视频,周二晚和周四晚会发布在合集 —— Rust crate 大巡礼 中。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Rust之tower如何构建请求中间件
tower是一个请求协议无关的的中间件定义类库,主要定义了Service和Layer两个trait来帮助实现可重用的请求处理中间件。
newbmiao
2024/02/26
7780
Rust之tower如何构建请求中间件
TOKIO ASYNC&AWAIT 初探
rust的async/await终于在万众瞩目之下稳定下来了,一起来尝尝鲜. 这篇文章主要是介绍基于tokio 0.2做一个服务程序员的小工具githubdns.
MikeLoveRust
2019/12/17
1.4K0
Rust中的异步编程实战:使用Tokio构建并发应用
异步编程是一种重要的编程模型,允许我们以非阻塞的方式执行I/O密集型操作,显著提高程序的性能。Rust中的异步编程模型非常强大,特别是与tokio等异步运行时结合使用时,能够让我们高效地构建并发应用。
数字扫地僧
2024/12/14
2650
Quic 协议详解-开篇简介
Quic如此牛逼,一篇肯定是不能做到详解的,所以后面将会写几篇来对quic进行一个阐述。这些文章都是自己对https://www.chromium.org/quic官网的翻译与理解。
榴莲其实还可以
2019/01/21
7.3K0
QUIC协议深度解析:构建HTTP/3高速传输的基石
HTTP/2 是目前最新的网络传输协议(如上图左),主要由 TCP + TLS 1.2 + HTTP 所组成。随着时间的演进,越来越多的网络流量都往移动端移动,手机的无线网络环境会遇到的问题像是 (1) 丢包率较高、(2) 较长的往返时间(RTT)和 (3) 连接迁移问题等等,都让主要是为了有线网络设计的 HTTP/TCP 协议遇到瓶颈。
写bug的高哈哈
2024/05/09
5420
QUIC协议深度解析:构建HTTP/3高速传输的基石
Rust 中的 QUIC 实现 --- quinn
QUIC 是基于 UDP 的多路复用、安全传输协议。可以简单理解为在用户空间将 TCP 里的机制实现了一遍,比如拥塞控制、流量控制等。好处是升级比较方便,TCP 协议栈是内核中实现的,只能随内核升级,而 QUIC 可灵活升级。
谛听
2022/01/30
4.1K0
以 QUIC 为例,聊聊如何学习网络协议
上周发了篇关于 s2n-quic 的文章,有读者问我如何学习像 QUIC 这样的网络协议。对于大部分互联网从业者来说,虽然大家每天都在跟网络打交道,但很少有人会(需要)关心 HTTP 之下的网络协议的细节,大部分时候,了解个大概,知道如何使用就可以了。如果你对 QUIC 一点概念都没有,那么,下面这个图能帮助你很好地了解 QUIC 在 HTTP/3 生态中的地位:
tyrchen
2022/03/29
2.4K0
以 QUIC 为例,聊聊如何学习网络协议
【Rust每周一库】smol - 异步rumtime
smol是一个轻量而高效的异步runtime。它采用了对标准库进行扩展的方式,整个runtime只有大约1500行代码。作者stjepang大神是大名鼎鼎crossbeam的作者。而他之前参与tokio和async-std的开发的经验和思考,产生出了从头开始构建的smol这个库。实际上在达到和tokio以及async-std相似的性能的前提下,smol代码短线精悍,完全没有依赖mio库,API更加简单,并且没有unsafe代码!而且,它还兼容tokio和async-std。让我们看个简单的例子
MikeLoveRust
2020/05/14
1.2K0
透过 Rust 探索系统的本原:网络篇
如今所有的应用程序几乎都离不开网络。从应用开发的角度,绝大多数应用以及其后端系统都工作在应用层:
tyrchen
2021/04/07
9860
透过 Rust 探索系统的本原:网络篇
Rust语法之多线程(Tokio)
该示例代码创建了一个包含 9 个元素的 Vec,然后使用 Arc 和 Mutex 包装了该 Vec。接着,我们创建了 3 个线程,每个线程负责修改 Vec 的三分之一元素的值。在每个线程的执行体中,我们使用 Mutex 来获取 Vec 的写锁,并修改 Vec 中的元素。最后,我们等待所有线程完成,并输出修改后的 Vec。
码客说
2023/04/17
1.9K0
Rust的第二次接触-写个小服务器程序
蛮久前入门了一下 Rust 语言。它的设计模型非常地吸引C/C++的开发者。但是学习语言嘛还是要练习一下,之前也用它给我们项目写了个命令行小工具。这回拿来写个小型的服务器程序吧。
owent
2018/10/09
4.2K2
tokio_rustls 自签名证书
服务端使用自己的域名向 CA(Certificate Authority,证书颁发机构)申请证书。
谛听
2021/11/13
2.7K0
【Rust每周一库】hyper - 底层http库
现在说到写应用,网络框架肯定是必不可少的。今天就给大家简单介绍一下hyper。hyper是一个偏底层的http库,支持HTTP/1和HTTP/2,支持异步Rust,并且同时提供了服务端和客户端的API支持。很多同学可能觉得既然hyper是个偏底层的框架,那是不是就不需要去了解了呢?首先很多上层的框架,比如rocket、iron和reqwest底层都是基于hyper的。(关于Rust中各种网络开发框架,这里有个很全面的综述和比较。)所以如果在使用这些框架的时候遇到了一些问题,对hyper的了解肯定是有一定的帮助的。再者学习Rust的我们都是奔着成为大佬的路线去的,很难说不会有直接操作偏底层框架的需求。
MikeLoveRust
2020/02/12
3.2K0
socks5协议原理分析及实现对比
本文个人博客链接:https://rayepeng.net/socks5-xie-yi-yuan-li-fen-xi-ji-shi-xian-dui-bi
raye
2024/02/04
1.8K0
【Rust投稿】从零实现消息中间件(6)-CLIENT
client实现功能相对比较单一,就是能够向服务器pub消息,然后就会说订阅消息,订阅的主题收到消息以后能够得到通知.因此总结起来就是下面三个功能:
MikeLoveRust
2020/03/11
7700
那些必须要了解的Serverless时代的并发神器-Rust语言Tokio框架基础
今天我们继续高并发的话题,传统的云计算技术,本质上都是基于虚拟机的,云平台可以将一些性能强劲的物理服务器,拆分成若干个虚拟机,提供给用户使用,但在互联网发展到今天,虚拟机还是太重了。即使是飞天集群,新增部署虚拟机的时间也是以分钟来计的。但是对于互联网用户来讲20秒的等等就是就会千万50%以上的用户流失,不能忍受的煎熬,因此Docker秒级启动的速度也不是个完美的解决方案,最终还是要Serverless极速的伸缩才能满足客户需求。
beyondma
2021/09/12
8450
【愚公系列】2022年11月 .NET CORE工具案例-.NET 7中的Quic通信
QUIC(Quick UDP Internet Connection)是谷歌制定的一种基于UDP的低时延的互联网传输层协议。我们知道,TCP/IP协议族是互联网的基础。其中传输层协议包括TCP和UDP协议。与TCP协议相比,UDP更为轻量,但是错误校验也要少得多。这意味着UDP往往效率更高(不经常跟服务器端通信查看数据包是否送达或者按序),但是可靠性比不上TCP。通常游戏、流媒体以及VoIP等应用均采用UDP,而网页、邮件、远程登录等大部分的应用均采用TCP。
愚公搬代码
2022/11/16
4750
【愚公系列】2022年11月 .NET CORE工具案例-.NET 7中的Quic通信
透过 Rust 探索系统的本原:安全篇
安全是我的老本行,隔一段时间不拉出来谈一谈就不舒服。我个人觉得:做应用不谈安全都是在耍流氓。
tyrchen
2021/04/07
1.2K0
透过 Rust 探索系统的本原:安全篇
Rust网络编程框架-Tokio进阶
我们在上文《小朋友也能听懂的Rust网络编程框架知识-Tokio基础篇》对于Tokio的基础知识进行了一下初步的介绍,本文就对于Tokio的用法及原理进行进一步的介绍与说明。
beyondma
2021/09/19
2.6K0
【Rust每周“一”库】async http三剑客
本文是Yoshua Wuyts大佬博客关于async http文章的大部分内容的翻译。原文地址https://blog.yoshuawuyts.com/async-http/
MikeLoveRust
2020/03/10
8780
相关推荐
Rust之tower如何构建请求中间件
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验