程序猿阵线联盟-汇总各类技术干货

QUIC探索(二):编译第一个QUIC工程

2019-06-17  本文已影响0人  码农叔叔

前言

前篇文章简单介绍了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的版本号,其各个数字的意义如下:

注意注意敲黑板,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!

上一篇下一篇

猜你喜欢

热点阅读