使用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为通 ...
随机推荐
- Cocos2dx打包apk时变更NDK引发问题及解决
现在官方的Cocos Studio已经支持打包apk文件,写该随笔的时候还没试过官方的打包功能,所以就按自己的学习顺序先把打包的心得写下. 问题及最终解决方案: 其中耗时最长的问题就是ndk-r10改 ...
- [转]C#打造一个开源webgis(一)系统架构
搭建一个GIS系统,为了能同时适应C/S和B/S架构,建议是做成自己的地图服务api方式,这样,一个或多个系统,就能通过统一的地图服务接口提供,而通信可以采用http的resful方式,而一个webG ...
- 报错Caused by: org.hibernate.AnnotationException: No identifier specified for entity:
Caused by: org.hibernate.AnnotationException: No identifier specified for entity:. 原因: 1.没有给实体类ID 解决 ...
- 前端面试题(copy)
前端开发面试知识点大纲: HTML&CSS: 对Web标准的理解.浏览器内核差异.兼容性.hack.CSS基本功:布局.盒子模型.选择器优先级及使用.HTML5.CSS3.移动端适应. Jav ...
- 2018年暑假ACM个人训练题6 解题报告
A:水题 https://www.cnblogs.com/yinbiao/p/9311834.html B:考察进制的转化 https://www.cnblogs.com/yinbiao/p/9311 ...
- 微信小程序禁止刷新之后苹果端还可以下拉的问题
一.问题描述 最近在做一个小程序项目,需要禁止下拉刷新,于是在page.json里面添加了这段话 "enablePullDownRefresh":false 全局关闭下拉刷新,这段 ...
- 大数据框架-Mapreduce过程
1.Shuffle [从mapTask到reduceTask: Mapper -> Partitioner ->Combiner -> Sort ->Reducer] mapp ...
- 【oracle】关于创建表时用default指定默认值的坑
刚开始学create table的时候没注意,学到后面发现可以指定默认值.于是写了如下语句: 当我查询的时候发现,查出来的结果是这样的.. 很纳闷有没有,我明明指定默认值了呀,为什么创建出来的表还是空 ...
- kendo UI 倒如css 和 js 后 窗口控件上的工具栏图标不显示如何解决
examples 文档中找到window的例子打开一个 查看其中文件引入 <head> <title>API</title> <meta char ...
- 我的前端工具集(六)Ajax封装token
我的前端工具集(六)Ajax封装token liuyuhang原创,未经允许禁止转载 在单点登陆中,或登陆验证后,不应该每次都验证用户名和密码, 也不应该将用户名密码存入cookie中(虽然很多都 ...