使用QUIC
QUIC是Google新开发的一个基于UDP的协议,它提供了像TCP一样的传输可靠性保证,可以实现数据传输的0-RTT延迟,灵活的设计使我们可以对它的拥塞控制及流量控制做更多的定制,它还提供了传输的安全性保障,以及像HTTP/2一样的应用数据二进制分帧传输。
而QUIC协议最最吸引人的特性有两点,一是对队首阻塞问题的解决更为彻底。基于TCP的HTTP/2,尽管从逻辑上来说,不同的流之间相互独立,不会相互影响,但在实际传输方面,数据还是要一帧一帧的发送和接收,一旦某一个流的数据有丢包,则同样会阻塞在它之后传输的其它与它毫不相干的流的数据的传输。而基于UDP的QUIC协议则可以更为彻底地解决这样的问题,让不同的流之间真正的实现相互独立传输,互不干扰。
另一个特性切换网络时的连接保持。当前移动端的应用环境,用户的网络可能会经常切换,比如从办公室或家里出门,WiFi断开,网络切换为3G或4G。基于TCP的协议,由于切换网络之后,IP会改变,因而之前的连接不可能继续保持。而基于UDP的QUIC协议,则可以内建与TCP中不同的连接标识方法,从而在网络完成切换之后,恢复之前与服务器的连接。
由于这些良好的特性,QUIC协议已经有在gmail中得到了大量的应用。打开Wireshark,随便对某个网卡抓包,都能看到大量的QUIC协议包:
这里我们来跑一下QUIC。
选择一份QUIC代码
下面的说明是用来基于chromium代码库编译QUIC代码。在Chrome支持的任何平台上,这里的说明都能保证是有效的,遇到问题时可以查看一些扩展的故障排查的文档。如果不想下载整个chromium代码库,则可以尝试github上快速而干净的proto-quic库。这是chromium中的代码的一份克隆,但剔除了大多数不必要的依赖,因而下载它要快得多,编译也更快,但不一定在所有的平台上都能用。事实上,github上的proto-quic库,当前只支持在Ubuntu linux上编译。
编译QUIC客户端和服务器
Chromium中提供了一个示例客户端和服务器实现。要使用这些东西,你首先应该已经下载Chromium的源代码,然后构建二进制文件:
ninja -C out/Debug quic_server quic_client
这就像编译chromium的任何模块一样。可以参考 懒人chromium net android移植指南 来对Chromium的构建系统做更多了解。
如果条件允许,github上的proto-quic库,编译起来也很简单快捷。首先需要下载代码及构建依赖的整个工具链:
git clone https://github.com/google/proto-quic.gitcd proto-quicexport PATH=$PATH:`pwd`/depot_tools
./proto_quic_tools/sync.sh
然后编译就是了,与普通的chromium模块编译一样:
cd src
gn gen out/Default && ninja -C out/Default quic_client quic_server net_unittests
从www.example.org准备测试数据
下载一份www.example.org的拷贝,它主要是给quic_server二进制可执行文件用来提供本地服务的:
mkdir ~/quic-data
cd ~/quic-data
wget -p --save-headers https://www.example.org
这里主要是要下载一个html文件,且保存文件的所有的HTTP header,当然也可以从其它的站点下载这个文件。
手动地编辑index.html,并调整如下的headers:
移除(如果存在的话):"Transfer-Encoding: chunked"
移除(如果存在的话):"Alternate-Protocol: ..."
添加:X-Original-Url: https://www.example.org/
生成证书
为了运行服务器,需要一个有效的证书,及一个pkcs8格式的私有key。如果没有,则可以使用脚本来产生它们:
cd net/tools/quic/certs
./generate-certs.shcd -
除了服务器的证书及public key,这个脚本也会产生一个CA证书 (net/tools/quic/certs/out/2048-sha256-root.pem),需要把它添加到操作系统的根证书商店以便于在证书验证期间它被信任。
一种比较简单的管理证书的方法是使用chrome浏览器。在地址栏中输入 chrome://settings/search#ssl
,然后点击“管理证书”:
在弹出的窗口中选择“导入...”,然后按照提示一步步将证书导入即可。
在linux上管理证书相关的更多信息,请参考 这些说明 。
如果遗漏了这里的添加CA证书的步骤的话,后面在执行quic_client的时候会报出如下的证书验证错误:
$ ./out/Default/quic_client --host=127.0.0.1 --port=80 https://www.example.org/[1008/164047:ERROR:cert_verify_proc_nss.cc(942)] CERT_PKIXVerifyCert for www.example.org failed err=-8179[1008/164047:WARNING:proof_verifier_chromium.cc(466)] Failed to verify certificate chain: net::ERR_CERT_AUTHORITY_INVALID
Failed to connect to 127.0.0.1:80. Error: QUIC_PROOF_INVALID
运行QUIC服务器和客户端
运行quic_server:
./out/Default/quic_server \
--quic_in_memory_cache_dir=~/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
还可以通过--port参数指定quic_server监听的端口,及--v参数指定输出更多信息,如:
./out/Default/quic_server --certificate_file=~/proto-quic/src/net/tools/quic/certs/out/leaf_cert.pem --key_file=~/proto-quic/src/net/tools/quic/certs/out/leaf_cert.pkcs8 --quic_in_memory_cache_dir=~/quic-data/www.example.com --port=32457 --v=1
quic_in_memory_cache_dir参数指定存放资源文件的目录路径。如我们前面看到的,从www.example.org下载到页面之后,需要调整headers,其中为X-Original-Url
设置的是相应资源的url。quic_server起来的时候,会加载该目录下的文件。要为quic_server添加其它的测试资源,也要注意正确的设置其headers。如json接口:
# cat testApi1
HTTP/1.1 200 OK
Cache-Control: privateContent-Length: 3214Content-Type: application/json; charset=utf-8Server: Microsoft-IIS/7.5X-AspNet-Version: 4.0.30319X-Powered-By: ASP.NETDate: Thu, 13 Oct 2016 02:12:28 GMT
X-Original-Url: https://www.wolfcstech.com:6121/testApi1{"res":[{"ctime":"2016-10-11","title":"温州","des":"国内","url":"http://news.163.com/16/1011/11/C33GFLIP0001124J.html"},{"ime":"2016","tit":"b","des":"a"}, {"ime":"2016","tit":"b","des":"a"}],"rea":"Su"}
然后就可以使用quic_client以QUIC协议请求文件了:
./out/Default/quic_client --host=127.0.0.1 --port=32457 https://www.example.org/
注意,如果要让服务器运行于端口32457上,则必须为客户端指定端口,因为它默认是80。
此外,如果本地机器有多个loopback地址 (由于它同时使用IPv4 和 IPv6),则不得不选定一个地址。
目前还不确定后面的缺点是不是一个bug。
注意:client和server都主要是为了做集成测试的:它们都不能大规模使用。
要使用chrome来测试相同的下载过程,可以执行:
chromium-browser \ --user-data-dir=/tmp/chrome-profile \
--no-proxy-server \
--enable-quic \
--origin-to-force-quic-on=www.example.org:443 \
--host-resolver-rules='MAP www.example.org:443 127.0.0.1:32457' \
https://www.example.org
使用Cronet访问QUIC服务
除了使用quic_client和Chrome浏览器之外,还可以在移动端使用Cronet访问QUIC服务。常规的使用Cronet访问网络服务的流程为:
使用CronetEngine.Builder创建CronetEngine,也及URL请求上下文,设置是否启用缓存,是否启用HTTP/2等。
实现UrlRequest.Callback,用以接收请求执行的结果。
使用UrlRequest.Builder,以前面创建的CronetEngine和UrlRequest.Callback创建UrlRequest。
启动UrlRquest的执行。
请求执行完成之后,在UrlRequest.Callback中处理请求执行结果。
访问QUIC服务相对于常规的使用Cronet访问网络服务的流程的差别主要有以下几点:
在上面的第1部,创建CronetEngine时,要启用QUIC,同时要为CronetEngine添加Quic Hint。如:
CronetEngine.Builder builder = new CronetEngine.Builder(context);
builder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024)
.enableHttp2(true)
.enableQuic(true)
.enableSDCH(true)
.setLibraryName("cronet")
.addQuicHint("www.example.org", 32457, 32457)
.addQuicHint("www.wolfcstech.com", 443, 6121)
.addQuicHint("www.wolfcstech.cn", 443, 6121)
.addQuicHint("www.wolfcstech.com", 6121, 6121)
.addQuicHint("www.wolfcstech.cn", 6121, 6121);
mCronetEngine = builder.build();用以https为scheme的URL访问QUIC服务。如:
String url = "https://www.wolfcstech.com:6121/testApi1";
UrlRequest.Builder builder = new UrlRequest.Builder(url, callback, mExecutor, mCronetEngine);
applyPostDataToUrlRequestBuilder(builder, mExecutor, postData);
builder.build().start();
QuicHint是 (主机名, 端口号, 备选端口号) 的三元组。为CronetEngine添加Quic Hint指示其在执行请求时,在以TCP协议连接 (主机名, 端口号) 并向其发送常规http或https请求的同时,以QUIC协议向 (主机名, 备选端口号) 请求相同的服务。
为CronetEngine添加加Quic Hint,是将Quic Hint添加进了构造中的URLRequestContextConfig:
static void AddQuicHint(JNIEnv* env, const JavaParamRef<jclass>& jcaller,
jlong jurl_request_context_config, const JavaParamRef<jstring>& jhost,
jint jport,
jint jalternate_port) {
URLRequestContextConfig* config = reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config);
config->quic_hints.push_back(
base::WrapUnique(new URLRequestContextConfig::QuicHint(
base::android::ConvertJavaStringToUTF8(env, jhost), jport,
jalternate_port)));
}
在构造net::URLRequestContext时,相关的这些信息会被保存起来,以备后续访问QUIC服务之用:
if (config->enable_quic) { for (auto hint = config->quic_hints.begin();
hint != config->quic_hints.end(); ++hint) { const URLRequestContextConfig::QuicHint& quic_hint = **hint; if (quic_hint.host.empty()) { continue;
} url::CanonHostInfo host_info;
std::string canon_host(net::CanonicalizeHost(quic_hint.host, &host_info)); if (!host_info.IsIPAddress() &&
!net::IsCanonicalizedHostCompliant(canon_host)) { continue;
} if (quic_hint.port <= std::numeric_limits<uint16_t>::min() ||
quic_hint.port > std::numeric_limits<uint16_t>::max()) { continue;
} if (quic_hint.alternate_port <= std::numeric_limits<uint16_t>::min() ||
quic_hint.alternate_port > std::numeric_limits<uint16_t>::max()) { continue;
} url::SchemeHostPort quic_server("https", canon_host, quic_hint.port);
net::AlternativeService alternative_service(
net::AlternateProtocol::QUIC, "", static_cast<uint16_t>(quic_hint.alternate_port));
context_->http_server_properties()->SetAlternativeService(
quic_server, alternative_service, base::Time::Max());
}
}
这种访问QUIC服务的奇怪方式,要求终端事先知道访问一个特定网站所用的协议及服务的端口。这样是非常不灵活的,因而它主要用于缺乏适当的协议协商机制的情况下,用于QUIC协议的实验阶段。不久前,有一个称为 替代服务(Alternative Services) 的HTTP机制标准化了,其标准规范文档为 RFC7838。这种机制允许对一个HTTP资源的访问,被重定向到另外的一个网络位置,甚至是以一种不同的协议配置来访问。具体而言,这种机制为HTTP新增了一个头部字段Alt-Svc
,HTTP服务器可以通过这个头部字段,告知客户端服务器被重定向到的服务的信息,包括协议,端口号等,如:
Alt-Svc: h2="new.example.org:80"
故障排查
如果你在运行时遇到了问题,则可以以--v=1参数运行服务器或客户端。它将提升日志的verbosity,更多的日志常常可以帮助暴露底层的问题。
参考文档: Playing with QUIC
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易云社区,经作者韩鹏飞授权发布。
使用QUIC的更多相关文章
- Google将向IETF标准提交QUIC协议提案
Google近期宣布,他们将向IETF提交实验性传输层网络协议QUIC的提案.此外,Google已经给出了QUIC协议优化页面加载时间的第一手数据. 自从2013年引入QUIC以来,Google一直在 ...
- QUIC简单介绍
QUIC,即Quick UDP Internet Connection,类似于SPDY,相同也是由Google公司在现有已存协议之上进行了扩展设计,而旨在降低网络延迟.之前我曾介绍过SPDY的相关信息 ...
- QUIC简要
QUIC.即Quick UDP Internet Connection,类似于SPDY,相同也是由Google公司在现有已存协议之上进行了扩展设计,而旨在降低网络延迟.之前我曾介绍过SPDY的相关信息 ...
- HTTP2.0和QUIC
最近看到腾讯云支持QUIC的文章,突然意识到还没有好好认识HTTP2.QUIC,而要认识HTTP2,就需要从HTTP1.0开始讲起,才能清楚HTTP的发展历程. HTTP1.x HTTP(HyperT ...
- 让互联网更快:新一代QUIC协议在腾讯的技术实践分享
本文来自腾讯资深研发工程师罗成在InfoQ的技术分享. 1.前言 如果:你的 App,在不需要任何修改的情况下就能提升 15% 以上的访问速度,特别是弱网络的时候能够提升 20% 以上的访问速度. 如 ...
- QUIC协议的分析,性能测试以及在QQ会员实践
WeTest 导读 你听过HTTPS.HTTP2.0.SPDY,但是这些应用层协议都是基于可靠的传输层协议TCP来实现的.那么,基于高效的UDP协议有没有一种相对可靠的应用层协议呢? Why QUIC ...
- Google 的 QUIC 华丽转身成为下一代网络协议: HTTP/3.0
HTTP/2.0 还没有普及,HTTP/3.0 标准就要被制定了. 据 IETF 透露,HTTP-over-QUIC 实验协议将被重命名为 HTTP/3,并成为 HTTP 协议的第三个正式版本. IE ...
- QUIC协议原理分析(转)
之前深入了解了一下HTTP1.1.2.0.SPDY等协议,发现HTTP层怎么优化,始终要面对TCP本身的问题.于是了解到了QUIC,这里分享一篇之前找到的有意义的文章. 原创地址:https://mp ...
- Google Quic协议
0x01 Quic QUIC协议于2012年实现,2015年提交RFC草案,它是Goolge为了解决当今WEB应用常见的传输层和应用层问题而提出的,从分层结构上可以看做是TCP+TLS+HTTP2的集 ...
- RTMP之后,SRT与QUIC
RTMP协议存在累计延迟与加密方面的问题,为适应互联网视频低延时,高质量的要求,以UDP为核心,具有创造性的SRT,QUIC等流媒体视频方式将成为新的选择 RTMP协议最初是由Macromedia为通 ...
随机推荐
- CRT公钥登录
1.实现原理: 通过CRT生成的密钥对,把公钥上传到Linux服务器指定用户下的.ssh目录中,在客户端上只需输入秘钥的密码即可登陆,而且验证一次以后可以免密码登陆 2.具体过程: 转自:http:/ ...
- 2014年百度之星资格赛第一题Energy Conversion
Problem Description 魔法师百小度也有遇到难题的时候-- 如今.百小度正在一个古老的石门面前,石门上有一段古老的魔法文字,读懂这样的魔法文字须要耗费大量的能量和大量的脑力. 过了许久 ...
- UVa 658 - It's not a Bug, it's a Feature!(Dijkstra + 隐式图搜索)
链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...
- UVa 1609 - Foul Play
链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...
- 【luogu P1073 最优贸易】 题解
题目链接:https://www.luogu.org/problemnew/show/P1073 对于状态量相互影响的题目,分层图是个不错的想法. 考虑在题目中分为: 不交易: 直接从1到n出去,为0 ...
- Andorid进阶7—— Ant自动编译打包&发布 android项目
http://www.cnblogs.com/tt_mc/p/3891546.html Eclipse用起来虽然方便,但是编译打包android项目还是比较慢,尤其将应用打包发布到各个渠道时,用Ecl ...
- 时钟晶振32.768KHz为什么是15分频?
实时时钟晶振为什么选择是32768Hz的晶振,在百度上搜索的话大部分的答案都是说2的15次方是32768,使用这个频率的晶振,人们可以很容易的通过分频电路得到1Hz的计时脉冲.但是话有说回来了,2的整 ...
- Notes 20180306 : 变量与常量
1.1 变量与常量 我们在开发中会经常听到常量和变量,那么常量和变量指的又是什么呢?顾名思义,在程序执行过程中,其值不能被改变的量称为常量,其值能被改变的量称为变量.变量与常量的命名都必须使用合法的标 ...
- iOS | 解决中文乱码
在iOS开发中,多多少少的朋友在开发的过程中,测试数据的时候可能会碰到后台打印的时候不能正确的打印出正常的汉字,打印出一些影响判断的字符,经常需要查看数组中得元素是否是自己想要的,但是苹果并没有对直接 ...
- iOS之webview加载网页、文件、html的方法
UIWebView 是用来加载加载网页数据的一个框.UIWebView可以用来加载pdf.word.doc 等等文件 生成webview 有两种方法,1.通过storyboard 拖拽 2.通过a ...