前言
前篇文章简单介绍了QUIC协议在业内存在的几种开源实现,但是归根结底主要可以分为Google开源的chromium库自带的QUIC实现方案,还有利用Go语言来重写的QUIC协议实现库。
相对来说,更加建议在chromium上面进行QUIC的研究,从多平台支持、功能的稳定性都有一定的保证,当然chromium浩大繁杂的源码库也是明显拔高了QUIC学习的入门难度。
获取源码
其实对于编译chromium的衍生工程来说,基本上流程都是差不多的,类似的可以参考《Webrtc 研究: Android编译》这篇文章。
限于篇幅,这里就以获取Linux环境下的chromium源码库的步骤进行介绍,对于Android、iOS的介绍后续有空再补上。
安装编译依赖环境
depot_tools是chromium编译必须依赖的编译环境,安装的方式很简单,只要git clone这个depot_tools库到本地即可。
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
需要将depot_tools安装目录添加到环境变量里面。
export PATH=$PATH:/path/depot_tools
其实也可以直接将路径配置到启动脚本里面,这样就不用每次启动终端就重新export
一次好累人。
拉取chromium源码
准备好环境了就可以开始拉取chromium源码了。
创建一个存放源码的目录,当然文件名可以看自己心情。
mkdir ~/chromium
cd ~/chromium
检出源码,然后静静得喝杯茶等待吧~
fetch -nohooks chromium
PS:如果仅仅只是想编译最新版本源码的话,可以添加--no-history
能减少一些下载量,但是缺点就是无法切换到旧版本源码。
安装构建所需的依赖项
进入源码库的src目录,运行install-build-deps.sh进行编译环境的配置。
cd ~/chromium/src
./build/install-build-deps.sh
gclient runhooks
值得注意的是,对于一个编译环境而言install-build-deps.sh仅需要运行一次即可,其会安装下载一些编译所需要的软件环境。
PS:如果在已经运行过install-build-deps.sh的机器重新检出chromium源码的话,可以不输入-nohooks
这个选项,其会在fetch完毕后自动gclient runhooks。
编译第一个QUIC项目
编译quic-sever和quic-client工程
生成编译参数配置脚本文件。
gn args out/Default
这时候一般会新建一个文件out/Default/args.gn
,并进入文本编辑模式;如果不需要特殊配置编译参数的话,直接保存退出即可。
接下来编译系统会根据args.gn的配置自动生成ninja文件。
如果后续需要修改编译参数的话,编辑args.gn文件后,执行
gn gen out/Default
就可以重新生成ninja文件。
接下来可以开始编译quic_server和quic_client模块。
ninja -C out/Debug quic_server quic_client
等待编译完成,进入out/Default
下可以看到quic_server和quic_client可执行文件。
运行quic-sever和quic-client工程
在运行工程前,我们需要先准备测试数据。
mkdir /tmp/quic-data
cd /tmp/quic-data
wget -p --save-headers https://www.example.org
修改测试数据如下
cd www.example.org/
vim index.html
将index.html
按照下面的提示进行修改
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Fri, 14 Jun 2019 06:19:23 GMT
Etag: "1541025663"
Expires: Fri, 21 Jun 2019 06:19:23 GMT
Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
Server: ECS (oxr/830F)
Vary: Accept-Encoding
X-Cache: HIT
Content-Length: 1270
#添加这一行
X-Original-Url: https://www.example.org/
#将后面内容全部删除,改成你想要测试的数据,例如改成json数据
{"code":200,"name":"quic"}
我们需要生成一个加密证书,这是因为这个DEMO包含了SSL加密的逻辑。
cd /<chromium-path>/src/net/tools/quic/certs/
./generate-certs.sh
将证书部署到系统
apt install libnss3-tools
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n quic -i /<chromium-path>/src/net/tools/quic/certs/out/2048-sha256-root.pem
#查看证书是否添加成功
certutil -d sql:$HOME/.pki/nssdb -L
#用完可以删除证书
certutil -d sql:$HOME/.pki/nssdb -D -n quic
如果证书没有添加成功,那么运行客户端会出现这个错误。
[0614/004201.673072:ERROR:cert_verify_proc_nss.cc(981)] CERT_PKIXVerifyCert for www.example.org failed err=-8179
[0614/004201.674716:WARNING:proof_verifier_chromium.cc(502)] Failed to verify certificate chain: net::ERR_CERT_AUTHORITY_INVALID
Failed to connect to 127.0.0.1:12306. Error: QUIC_PROOF_INVALID
运行server服务器,监听本地12306
端口。
cd /<chromium-path>/src
./out/Default/quic_server \
--quic_response_cache_dir=/tmp/quic-data/www.example.org \
--certificate_file=net/tools/quic/certs/out/leaf_cert.pem \
--key_file=net/tools/quic/certs/out/leaf_cert.pkcs8 \
--host=127.0.0.1 \
--port=12306
运行quic-client客户端,向本地12306
端口发送请求,顺便提一句如果没指定端口的话会默认采用80
端口。
cd /<chromium-path>/src
./out/Default/quic_client \
--host=127.0.0.1 \
--port=12306 \
https://www.example.org/
如果想输出更详细的调试信息,可以加上--v=1
。
下面是运行结果的信息输出
Connected to 127.0.0.1:12306
Request:
headers:
{
:method GET
:scheme https
:authority www.example.org
:path /
}
body:
Response:
headers:
{
:status 200
accept-ranges bytes
cache-control max-age=604800
content-type text/html; charset=UTF-8
date Fri, 14 Jun 2019 06:19:23 GMT
etag "1541025663"
expires Fri, 21 Jun 2019 06:19:23 GMT
last-modified Fri, 09 Aug 2013 23:54:35 GMT
server ECS (oxr/830F)
vary Accept-Encoding
x-cache HIT
content-length 1270
x-original-url https://www.example.org/
}
body: {"test":"quic","code":400}
trailers: {}
Request succeeded (200).
编译运行quartc-test工程
如果不想运行基于HTTP封装的QUIC协议,而想测试模拟SOCKET的QUIC数据接发,可以测试一下Quartc相关的测试用例。
PS:Quartc并不需要配置SSL加密证书。
这个模块基于gtest,当然这里并不需要了解那么多。
首先,先编译quartc-test测试模块,由于quartc-test的代码包含在net_unittests这个net模块测试用例集里面
#编译net测试用例集合
ninja -C out/Default/ net_unittests
接下来就可以运行quartc的测试工程
#quic流握手测试用例
./out/Default/net_unittests --single-process-tests --gtest_filter=QuartcSessionTest.StreamConnection
#quic流关闭测试用例
./out/Default/net_unittests --single-process-tests --gtest_filter=QuartcSessionTest.CloseQuartcStream
#quic流数据发送测试用例
./out/Default/net_unittests --single-process-tests --gtest_filter=QuartcSessionTest.BundleWrites
这个是运行QuartcSessionTest.BundleWrites
这个测试用例的结果输出
Note: Google Test filter = QuartcSessionTest.BundleWrites
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from QuartcSessionTest
[ RUN ] QuartcSessionTest.BundleWrites
[59022:59022:0614/032154.391161:53217086974:INFO:quartc_session_test.cc(337)] Crypto handshake complete!
[59022:59022:0614/032154.391991:53217087794:INFO:quartc_session_test.cc(337)] Crypto handshake complete!
[ OK ] QuartcSessionTest.BundleWrites (16 ms)
[----------] 1 test from QuartcSessionTest (16 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (19 ms total)
[ PASSED ] 1 test.
可以参考测试源码进行相对应调试和日志输出,这块后续会有文章介绍。
chromium的net模块
如果我们想要使用chromium里面的QUIC相关功能,最佳的方案就是选择编译chromium的net模块。
至少我们可以知道net模块提供的功能在chromium所有支持的平台上都是经过稳定性测试的,换句话说我们可以通过net在各个平台上面达到使用QUIC协议的目的。
可能你们会说net模块的编译出来的动态库会有点大,一种方案就是用编译脚本进行功能裁剪,另外一种方案就是修改GN脚本进行代码抽离,这个后面有空再解释。
选择QUIC的历史版本
由于quic-go、Caddy、谷歌浏览器等并不能同步支持到QUIC的最新版本,所以建议在一个稳定的版本进行QUIC开发更加靠谱。
现在市面上主流支持是quic44、quic43、quic39之类的,我用shareshark抓包发现其只支持到quic43,所以后续是以quic43展开研究的。
如果要用以前版本的QUIC版本,可以在同步版本时这么指定
#注意这里不能用--no-history
fetch --nohooks chromium
cd src
#切换到你想要的版本,这里是68.0.3440.134
git fetch origin 68.0.3440.134
git checkout -b my_stable_branch FETCH_HEAD
gclient sync --with_branch_heads
build/install-build-deps.sh
gclient runhooks
gn args out/Default
ninja -C out/Default net
这里你会疑惑68.0.3440.134
是什么鬼,这是是chromium的版本号,其各个数字的意义如下:
- 68.0 - 为chrome主版本号,通常它的变化频率很低,通常当它有所变化就意味着chrome本身有了较多的改进。
- 3440 - 它是当前版本的代号,每当它有所增大,就意味着Chrome有新功能出现或者是某些原有功能得到改进。
- 134 - 这个数字代表Chrome在安全性和稳定性方面的更新,它的增加不会带来新功能,仅仅是修补漏洞和改善稳定性。
注意注意敲黑板,QUIC版本号并不跟chromium版本号同步,那么怎么通过chromium的版本号来获得QUIC的版本呢?
那么可以通过这个在线源码网站
https://chromium.googlesource.com/chromium/src.git 中查看 net/third_party/quic/core/quic_versions.cc 这个文件就能清楚看出当前的QUIC版本,例如:
QuicString QuicVersionToString(QuicTransportVersion transport_version) {
switch (transport_version) {
RETURN_STRING_LITERAL(QUIC_VERSION_35);
RETURN_STRING_LITERAL(QUIC_VERSION_37);
RETURN_STRING_LITERAL(QUIC_VERSION_38);
RETURN_STRING_LITERAL(QUIC_VERSION_39);
RETURN_STRING_LITERAL(QUIC_VERSION_41);
RETURN_STRING_LITERAL(QUIC_VERSION_42);
//当前的QUIC版本为43
RETURN_STRING_LITERAL(QUIC_VERSION_43);
//99并不是一个版本号,而已预留给后续的升级兼容对接
RETURN_STRING_LITERAL(QUIC_VERSION_99);
default:
return "QUIC_VERSION_UNSUPPORTED";
}
}
结语
这篇文章简单介绍了怎么拉取chromium源码并且编译出适用于Linux环境的QUIC工程,以及其过程中的注意事项,后续会从源码层面介绍怎么进行QUIC的定制开发。
本文发布于 简书。
End!