ip地址错误,无法上网182105...87是什么地方

&p&&i&摘要:&/i& 小程序升级实时音视频录制及播放能力,开放 Wi-Fi、NFC(HCE) 等硬件连接功能。同时提供按需加载、自定义组件和更多访问层级等新特性,增强了第三方平台的能力,以满足日趋丰富的业务需求。&/p&&p&作者: 小柒&/p&&p&&a href=&https://link.zhihu.com/?target=https%3A//yq.aliyun.com/articles/Fspm%3Da2c4e..searchblog.9.54132ec1vAat9a& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&原文&/a& &/p&&h2&&b&写在开始&/b&&/h2&&p&小程序升级实时音视频录制及播放能力,开放 Wi-Fi、NFC(HCE) 等硬件连接功能。同时提供按需加载、自定义组件和更多访问层级等新特性,增强了第三方平台的能力,以满足日趋丰富的业务需求。&/p&&p&为更高效地连接用户与商家,小程序提供了实时音视频录制及播放组件。符合类目要求的小程序自助开通后,可自建或使用云服务,实现单向、双向甚至多向的音视频功能,如在线授课、远程咨询、视频客服,以及多人会议等。&/p&&p&微信小程序推送了这样一条消息,文档,代码也是简单的一笔带过,用户需要自建或使用云服务,实现单向、双向甚至多向的音视频功能。目前仅支持 flv, rtmp 格式。&/p&&p&本篇博客通过介绍使用nginx的rtmp模块来使nginx服务支持rtmp协议,可以完成推流和播放的功能。&/p&&h2&&b&开发环境&/b&&/h2&&p&Linux (centos7),Nginx (openresty),nginx-rtmp-module&/p&&h2&&b&安装&/b&&/h2&&p&由于之前已经安装过了openresty,详见&a href=&https://link.zhihu.com/?target=https%3A//blog.52itstyle.com/archives/1764/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&博文&/a&。这里我们只需要安装nginx-rtmp-module模块即可。&/p&&p&下载并解压模块:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-25b158e9fbee6d9aea8f12_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&695& data-rawheight=&67& class=&origin_image zh-lightbox-thumb& width=&695& data-original=&https://pic3.zhimg.com/v2-25b158e9fbee6d9aea8f12_r.jpg&&&/figure&&p&停止原来的Nginx服务:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-8b32c17f5e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&692& data-rawheight=&51& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic1.zhimg.com/v2-8b32c17f5e_r.jpg&&&/figure&&p&配置并编辑:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-4abc8db8_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&690& data-rawheight=&69& class=&origin_image zh-lightbox-thumb& width=&690& data-original=&https://pic1.zhimg.com/v2-4abc8db8_r.jpg&&&/figure&&p&复制Nginx:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-89a2d78a215f621b1dbb4049_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&694& data-rawheight=&64& class=&origin_image zh-lightbox-thumb& width=&694& data-original=&https://pic1.zhimg.com/v2-89a2d78a215f621b1dbb4049_r.jpg&&&/figure&&p&配置rtmp服务:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-54ec6d22a520d227da2698d9ded295bf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&695& data-rawheight=&258& class=&origin_image zh-lightbox-thumb& width=&695& data-original=&https://pic4.zhimg.com/v2-54ec6d22a520d227da2698d9ded295bf_r.jpg&&&/figure&&p&检查并启动服务:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-b12b1d48d58980eff5f90f2d1ba82f98_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&694& data-rawheight=&66& class=&origin_image zh-lightbox-thumb& width=&694& data-original=&https://pic4.zhimg.com/v2-b12b1d48d58980eff5f90f2d1ba82f98_r.jpg&&&/figure&&h2&&b&播放&/b&&/h2&&p&上传一个视频文件test.flv至/opt/video目录。&/p&&p&下载一个VLC media player,工具栏-媒体-打开网络串流-网络,然后偶输入网络URL(rtmp://ip:port/vod/test.flv),点击播放。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-d45f4fa9ea1e8f27af8c1c8c182105bf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&490& data-rawheight=&285& class=&origin_image zh-lightbox-thumb& width=&490& data-original=&https://pic1.zhimg.com/v2-d45f4fa9ea1e8f27af8c1c8c182105bf_r.jpg&&&/figure&&h2&&b&回看&/b&&/h2&&p&实时回看视频服务器的配置:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-63bfd7f9fa7c4cd0af7c551_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&694& data-rawheight=&506& class=&origin_image zh-lightbox-thumb& width=&694& data-original=&https://pic2.zhimg.com/v2-63bfd7f9fa7c4cd0af7c551_r.jpg&&&/figure&&p&HTTP服务:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-962d7cbbae2b1_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&697& data-rawheight=&639& class=&origin_image zh-lightbox-thumb& width=&697& data-original=&https://pic3.zhimg.com/v2-962d7cbbae2b1_r.jpg&&&/figure&&h2&&b&微信推流&/b&&/h2&&figure&&img src=&https://pic1.zhimg.com/v2-659ae3cf52cae9eaf63de9f39bf6ff30_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&697& data-rawheight=&164& class=&origin_image zh-lightbox-thumb& width=&697& data-original=&https://pic1.zhimg.com/v2-659ae3cf52cae9eaf63de9f39bf6ff30_r.jpg&&&/figure&&p&手机打开小程序页面,进行视频推流服务,稍后我们会在/opt/video/hls/itstyle 目录下发现一些ts文件和index.m3u8文件。&/p&&p&注意 url中的itstyle可以随便定义,相当于一个房间标识。&/p&&h2&&b&PC推流&/b&&/h2&&p&PC 直播是在 PC(windows/mac)上借助安装的推流软件 OBS(推荐)或者 XSplit 向RTMP视频云的推流地址,推送经过压缩编码现场活动、教学、投影或者游戏等画面,同时观众可以通过和推流地址相对应的播放地址收看实时画面。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-27d4a6b32ab02f88b7acf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1709& data-rawheight=&597& class=&origin_image zh-lightbox-thumb& width=&1709& data-original=&https://pic2.zhimg.com/v2-27d4a6b32ab02f88b7acf_r.jpg&&&/figure&&p&PC 直播流程非常简单,主要步骤:&/p&&ul&&li&通过RTMP自定义一个推流地址,解决往哪推流的问题;&/li&&li&采用第三方的推流软件,设置推流音视频源以及编码参数,解决推什么内容的问题;&/li&&li&观众就可以使用我们提供的 RTMP DEMO 通过设置播放地址即可进行观看,解决内容触达到观众那里的问题。&/li&&/ul&&figure&&img src=&https://pic3.zhimg.com/v2-74bdda28e5edb598cde9e0f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1170& data-rawheight=&701& class=&origin_image zh-lightbox-thumb& width=&1170& data-original=&https://pic3.zhimg.com/v2-74bdda28e5edb598cde9e0f_r.jpg&&&/figure&&p&推荐阅读:&a href=&https://link.zhihu.com/?target=https%3A//help.aliyun.com/document_detail/45212.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&第三方推流工具使用指南&/a&&/p&&h2&&b&Web播放&/b&&/h2&&p&xiaozhibo.html:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-30993dbed816bbbb3daefacf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&695& data-rawheight=&560& class=&origin_image zh-lightbox-thumb& width=&695& data-original=&https://pic4.zhimg.com/v2-30993dbed816bbbb3daefacf_r.jpg&&&/figure&&p&跨域问题crossdomain.xml放置在网站根目录:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-52f90c2cce418d500f8fecf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&698& data-rawheight=&89& class=&origin_image zh-lightbox-thumb& width=&698& data-original=&https://pic3.zhimg.com/v2-52f90c2cce418d500f8fecf_r.jpg&&&/figure&&p&测试访问地址:&a href=&https://link.zhihu.com/?target=http%3A//rtmp.52itstyle.com/xiaozhibo.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&rtmp.52itstyle.com/xiao&/span&&span class=&invisible&&zhibo.html&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&参考文档:&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//www.cnblogs.com/zx-admin/p/5783523.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&cnblogs.com/zx-admin/p/&/span&&span class=&invisible&&5783523.html&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&&a href=&https://link.zhihu.com/?target=https%3A//cloud.tencent.com/document/product/267& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&cloud.tencent.com/docum&/span&&span class=&invisible&&ent/product/267&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&&a href=&https://link.zhihu.com/?target=https%3A//github.com/arut/nginx-rtmp-module/wiki/Directives%23rtmp& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/arut/nginx-r&/span&&span class=&invisible&&tmp-module/wiki/Directives#rtmp&/span&&span class=&ellipsis&&&/span&&/a& &/p&&p&出处: &a href=&https://link.zhihu.com/?target=https%3A//blog.52itstyle.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&https://blog.52itstyle.com&/a&&/p&&p&&b&更多技术干货敬请关注云栖社区知乎机构号:&a href=&https://www.zhihu.com/org/a-li-yun-yun-qi-she-qu-48& class=&internal&&阿里云云栖社区 - 知乎&/a&&/b&&/p&&p&&/p&
摘要: 小程序升级实时音视频录制及播放能力,开放 Wi-Fi、NFC(HCE) 等硬件连接功能。同时提供按需加载、自定义组件和更多访问层级等新特性,增强了第三方平台的能力,以满足日趋丰富的业务需求。作者: 小柒 写在开始小程序升级实时音视频录制及播放能…
&h2&导语&/h2&&p&企鹅媒体平台媒体名片页反爬虫技术实践,分布式网页爬虫技术、利用人工智能进行人机识别、图像识别码、频率访问控制、利用无头浏览器PhantomJS、Selenium 进行网页抓取等相关技术不在本文讨论范围内。&/p&&h2&Cookie是什么&/h2&&p&大家都知道http请求是无状态的,为了让http请求从“无状态” to “有状态” , W3C 在 rfc6265 中描述了整个http协议的状态机制,既从客户端(通常是浏览器)到服务器端的流转过程,cookie 的引入使得 服务器在 接收到请求时可以区分用户和状态。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-44aaee32f3cda67e6762e4_b.jpg& data-rawwidth=&700& data-rawheight=&332& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&https://pic1.zhimg.com/v2-44aaee32f3cda67e6762e4_r.jpg&&&/figure&&figure&&img src=&https://pic2.zhimg.com/v2-ce3affbb2f1a2222334daced_b.jpg& data-rawwidth=&700& data-rawheight=&292& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&https://pic2.zhimg.com/v2-ce3affbb2f1a2222334daced_r.jpg&&&/figure&&br&&p&通过上边这张图,我们可以容易的发现,这个过程就好像“上车打票”一样,有普通票(不记名)和 也月票(“记名的票”),有位伟大的程序员曾经说过“如果你的程序逻辑和实际生活中的逻辑反了,就一定是你错了”。&/p&&h2&言归正传,为什么反爬虫&/h2&&p&互联网有很多业务或者说网页,是不需要用户进行登录的(不记名的票),你可以简单的认为这其实是一个“不需要记录http状态的业务场景”(注意这里是简单认为,但其实并不是无状态的),那这些不需要登录的页面,往往又会包含大量的聚合信息,比如新闻门户网站、视频门户网站、搜索引擎,这些信息是公开的,其实是可以可以被爬虫抓取的,站长甚至还要做SEO(搜索引擎优化)让搜索引擎或其他网站更多更经常的去收录自己的整站,以便推广,那既然我们要做SEO优化为什么还要 “反爬虫” ?&/p&&p&因为通过程序进行 URL 请求 去获得数据的成本是很低的,这就造成大量抵质量网页爬虫在网络横行,对业务方的服务器造成不必要的流量浪费和资源消耗。&/p&&h2&网页爬虫到底有多容易&/h2&&p&正常打开的界面内容是这样的&/p&&figure&&img src=&https://pic1.zhimg.com/v2-00041afe8d59d4ca65dcf4_b.png& data-rawwidth=&2238& data-rawheight=&1922& class=&origin_image zh-lightbox-thumb& width=&2238& data-original=&https://pic1.zhimg.com/v2-00041afe8d59d4ca65dcf4_r.jpg&&&/figure&&br&&p&查看网页源代码,看看我们要抓取的目标,这里就不在演示了,然后利用Chrome开发者工具提供的 “Copy as curl”&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&curl 'https://v.qq.com/' -H 'Pragma: no-cache' -H 'DNT: 1' -H 'Accept-Encoding: gzip, deflate, sdch, br' -H 'Accept-Language: zh-CN,q=0.8,en-US;q=0.6,q=0.4' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (M Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/q=0.9,image/webp,*/*;q=0.8' -H 'Cache-Control: no-cache' -H 'Cookie: tvfe_boss_uuid=ad12b5df44c4af49; ad_play_index=34; pgv_info=ssid=s; ts_last=v.qq.com/; pgv_pvid=; ts_uid=; qv_als=778esxpJSKmr5N0bAPnRBKw==' -H 'Connection: keep-alive' --compressed
&/code&&/pre&&/div&&p&然后你会发现,与“查看网页源代码” 没有区别,说明我们已经成功获得数据内容。&/p&&h2&反爬虫效果是什么样的?&/h2&&br&&p&我们通过浏览器直接打开下面这个链接 ,会发现请求到的结果是个JSON&/p&&figure&&img src=&https://pic3.zhimg.com/v2-e9dbc89e34e_b.png& data-rawwidth=&1944& data-rawheight=&1140& class=&origin_image zh-lightbox-thumb& width=&1944& data-original=&https://pic3.zhimg.com/v2-e9dbc89e34e_r.jpg&&&/figure&&br&&p&现在我们重复刚刚的 “Copy as curl” 的过程,看看是否依然能获得正确结果 ? 答案是否定的,我们来验证一下&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&curl 'https://media.om.qq.com/media/5054675/list' -H 'pragma: no-cache' -H 'dnt: 1' -H 'accept-encoding: gzip, deflate, sdch, br' -H 'accept-language: zh-CN,q=0.8,en-US;q=0.6,q=0.4' -H 'upgrade-insecure-requests: 1' -H 'user-agent: Mozilla/5.0 (M Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' -H 'accept: text/html,application/xhtml+xml,application/q=0.9,image/webp,*/*;q=0.8' -H 'cache-control: no-cache' -H 'authority: media.om.qq.com' -H 'cookie: signiture=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjpbXSwiZWkiOjUsInN0IjoiMTQ5MTg5OTczNjQ3NyIsImlhdCI6MTQ5MTg5OTc0M30.fTAsbJZNOTsHk0QoLfZIA-AXhO6ezaCOiCTj8lYCqO4; tvfe_boss_uuid=ad12b5df44c4af49; pgv_info=ssid=s; pgv_pvid=; omuisid=eyJ1aWQiOiI5ZGVjMjRiN2UxM2FiNGUyZWJjNWRhMGU1N2EyYTRkZSJ9; omuisid.sig=5wAr5khpxGvFDp3WpkJ6_QX9iE4' -H 'referer: https://media.om.qq.com/media/5054675/list' --compressed
&/code&&/pre&&/div&&p&我们会发现得到的是一个网页而不是 JSON,命中了反爬虫逻辑&/p&&figure&&img src=&https://pic1.zhimg.com/v2-c5975e9eab5607ccc0150_b.png& data-rawwidth=&2456& data-rawheight=&2154& class=&origin_image zh-lightbox-thumb& width=&2456& data-original=&https://pic1.zhimg.com/v2-c5975e9eab5607ccc0150_r.jpg&&&/figure&&br&&h2&到底发生了什么?&/h2&&p&前面提到了 “不记名票据” 和 因推广需求网站不需要登录的场景,那针对这样的情况,是否我们就真的不需要对请求进行签名呢 ? 答案是否定的,不花钱或花很少的钱就可以免费进入公园游玩了,游客可能本身是感受不到“票据”的存在,但其实我们还是需要对用户进行标记,这里发散一下,其实统计网站在追踪pv/uv时也是进行了类似的“标记”,下面我们通过一张图来描述下上面请求发生的过程&/p&&figure&&img src=&https://pic4.zhimg.com/v2-728d1b8a5862cabc5a566b_b.png& data-rawwidth=&1870& data-rawheight=&1420& class=&origin_image zh-lightbox-thumb& width=&1870& data-original=&https://pic4.zhimg.com/v2-728d1b8a5862cabc5a566b_r.jpg&&&/figure&&br&&p&这里我们有两次签名过程,一次在服务器端进行,一次在客户端进行,因为在这个页面是不需要用户登录,所以在服务器端进行签名,对于爬虫来说是一个几乎没有成本的过程,它可以每次来“园子”里玩的时候,都申请一张新的票,伪装成为一个“新的用户”,为了应对如此低廉的成本,我们增加“客户端进行签名”的流程,有人说“客户端签名是不靠谱的,因为你的代码别人都是可以看到的是可以破解的”,这个地方的客户端签名不是为了数据安全,而是为了增加爬虫进行抓取的成本,因为一般网页爬虫都不具备 js 之行能力,这样就增加了它抓取的成本。&/p&&p&另外一点,签名虽然是由客户端签发的,但是却是由服务器端进行验证,我们这里是利用 JWT(JSON WEB TOKEN) 进行了 encode和decode过程,且通过将服务器时间对客户端进行下发,完成有效期控制。&/p&&br&&h2&起到一定的防DDOS攻击的效果&/h2&&p&通过上图我们可以看到,关键就是两次票据的签发与验证,一次由浏览器签发,一次由后台签发,但是验证真伪都是在服务端进行,这样做的最终效果就是,要对我们的界面进行抓取或攻击的人,必须按照我们设定的套路运转,就会增加他下载js并执行我们js的过程,如果有攻击方利用xss 获的肉机,对我们的域名发起攻击时,由于我们有两次票据验证,而去其中有一个票据的有效期时间很短,基本上不太可能对我们的服务器造成连续伤害了。&/p&&p&如果网页抓取人,通过使用完全模拟浏览器的运行环境的第三方软件(PhantomJS、Selenium,WEBDRIVER)对我们进行抓取,其实效率是很慢的,基本上需要5-6秒完成一次, 基本上比一个真实的用户打开网页还要慢很多,对于这种可以当成是真是用户一样对待,数据本来就是开放的&/p&&p&接入这套反爬、防DDOS逻辑后,从模调系统查看数据后台服务被击穿的现象已经完成消失,异常流量已被隔离。&/p&&p&作者 | 张宁&br&编辑 | 迷鹿&br&&/p&&p&此文已由作者授权腾讯云技术社区发布,转载请注明&a href=&http://link.zhihu.com/?target=https%3A//www.qcloud.com/community& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&文章出处&/a&&/p&
导语企鹅媒体平台媒体名片页反爬虫技术实践,分布式网页爬虫技术、利用人工智能进行人机识别、图像识别码、频率访问控制、利用无头浏览器PhantomJS、Selenium 进行网页抓取等相关技术不在本文讨论范围内。Cookie是什么大家都知道http请求是无状态的,为了让…
&p&&i&&b&摘要:&/b&&/i& WebSocket的出现,使得浏览器具备了实时双向通信的能力。本文由浅入深,介绍了WebSocket如何建立连接、交换数据的细节,以及数据帧的格式。此外,还简要介绍了针对WebSocket的安全攻击,以及协议是如何抵御类似攻击的。&/p&&h2&&b&一、内容概览&/b&&/h2&&p&WebSocket的出现,使得浏览器具备了实时双向通信的能力。本文由浅入深,介绍了WebSocket如何建立连接、交换数据的细节,以及数据帧的格式。此外,还简要介绍了针对WebSocket的安全攻击,以及协议是如何抵御类似攻击的。&/p&&h2&&b&二、什么是WebSocket&/b&&/h2&&p&HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。&/p&&p&对大部分web开发者来说,上面这段描述有点枯燥,其实只要记住几点:&/p&&ol&&li&WebSocket可以在浏览器里使用&/li&&li&支持双向通信&/li&&li&使用很简单&/li&&/ol&&p&&b&1、有哪些优点&/b&&/p&&p&说到优点,这里的对比参照物是HTTP协议,概括地说就是:支持双向通信,更灵活,更高效,可扩展性更好。&/p&&ol&&li&支持双向通信,实时性更强。&/li&&li&更好的二进制支持。&/li&&li&较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头部。&/li&&li&支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)&/li&&/ol&&p&对于后面两点,没有研究过WebSocket协议规范的同学可能理解起来不够直观,但不影响对WebSocket的学习和使用。&/p&&p&&b&2、需要学习哪些东西&/b&&/p&&p&对网络应用层协议的学习来说,最重要的往往就是&b&连接建立过程&/b&、&b&数据交换教程&/b&。当然,数据的格式是逃不掉的,因为它直接决定了协议本身的能力。好的数据格式能让协议更高效、扩展性更好。&/p&&p&下文主要围绕下面几点展开:&/p&&ol&&li&如何建立连接&/li&&li&如何交换数据&/li&&li&数据帧格式&/li&&li&如何维持连接&/li&&/ol&&h2&&b&三、入门例子&/b&&/h2&&p&在正式介绍协议细节前,先来看一个简单的例子,有个直观感受。例子包括了WebSocket服务端、WebSocket客户端(网页端)。完整代码可以在 &a href=&https://link.zhihu.com/?target=https%3A//github.com/chyingp/blog/tree/master/demo/-web-socket/ws& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a& 找到。&/p&&p&这里服务端用了&code&ws&/code&这个库。相比大家熟悉的&code&socket.io&/code&,&code&ws&/code&实现更轻量,更适合学习的目的。&/p&&p&&b&1、服务端&/b&&/p&&p&代码如下,监听8080端口。当有新的连接请求到达时,打印日志,同时向客户端发送消息。当收到到来自客户端的消息时,同样打印日志。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-9a6128bfa580e1b35d3c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&851& data-rawheight=&427& class=&origin_image zh-lightbox-thumb& width=&851& data-original=&https://pic2.zhimg.com/v2-9a6128bfa580e1b35d3c_r.jpg&&&/figure&&p&&b&2、客户端&/b&&/p&&p&代码如下,向8080端口发起WebSocket连接。连接建立后,打印日志,同时向服务端发送消息。接收到来自服务端的消息后,同样打印日志。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-d0a496ffac80f00abe6a23_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&870& data-rawheight=&235& class=&origin_image zh-lightbox-thumb& width=&870& data-original=&https://pic3.zhimg.com/v2-d0a496ffac80f00abe6a23_r.jpg&&&/figure&&p&&b&3、运行结果&/b&&/p&&p&可分别查看服务端、客户端的日志,这里不展开。&/p&&p&服务端输出:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-f23dc6b7fac06a594a3b3_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&867& data-rawheight=&67& class=&origin_image zh-lightbox-thumb& width=&867& data-original=&https://pic4.zhimg.com/v2-f23dc6b7fac06a594a3b3_r.jpg&&&/figure&&p&客户端输出:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-e1b30ec7d269f0d479bff2b82930ffce_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&868& data-rawheight=&64& class=&origin_image zh-lightbox-thumb& width=&868& data-original=&https://pic2.zhimg.com/v2-e1b30ec7d269f0d479bff2b82930ffce_r.jpg&&&/figure&&h2&&b&四、如何建立连接&/b&&/h2&&p&前面提到,WebSocket复用了HTTP的握手通道。具体指的是,客户端通过HTTP请求与WebSocket服务端协商升级协议。协议升级完成后,后续的数据交换则遵照WebSocket的协议。&/p&&p&&b&1、客户端:申请协议升级&/b&&/p&&p&首先,客户端发起协议升级请求。可以看到,采用的是标准的HTTP报文格式,且只支持&code&GET&/code&方法。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-f9ec6bca4e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&865& data-rawheight=&163& class=&origin_image zh-lightbox-thumb& width=&865& data-original=&https://pic1.zhimg.com/v2-f9ec6bca4e_r.jpg&&&/figure&&p&重点请求首部意义如下:&/p&&ul&&li&&code&Connection: Upgrade&/code&:表示要升级协议&/li&&li&&code&Upgrade: websocket&/code&:表示要升级到websocket协议。&/li&&li&&code&Sec-WebSocket-Version: 13&/code&:表示websocket的版本。如果服务端不支持该版本,需要返回一个&code&Sec-WebSocket-Version&/code&header,里面包含服务端支持的版本号。&/li&&li&&code&Sec-WebSocket-Key&/code&:与后面服务端响应首部的&code&Sec-WebSocket-Accept&/code&是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。&/li&&/ul&&blockquote&注意,上面请求省略了部分非重点请求首部。由于是标准的HTTP请求,类似Host、Origin、Cookie等请求首部会照常发送。在握手阶段,可以通过相关请求首部进行 安全限制、权限校验等。&/blockquote&&p&&b&2、服务端:响应协议升级&/b&&/p&&p&服务端返回内容如下,状态代码&code&101&/code&表示协议切换。到此完成协议升级,后续的数据交互都按照新的协议来。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-075baf54eea0ab028e8c042_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&868& data-rawheight=&106& class=&origin_image zh-lightbox-thumb& width=&868& data-original=&https://pic2.zhimg.com/v2-075baf54eea0ab028e8c042_r.jpg&&&/figure&&blockquote&备注:每个header都以&code&\r\n&/code&结尾,并且最后一行加上一个额外的空行&code&\r\n&/code&。此外,服务端回应的HTTP状态码只能在握手阶段使用。过了握手阶段后,就只能采用特定的错误码。&/blockquote&&p&&b&3、Sec-WebSocket-Accept的计算&/b&&/p&&p&&code&Sec-WebSocket-Accept&/code&根据客户端请求首部的&code&Sec-WebSocket-Key&/code&计算出来。&/p&&p&计算公式为:&/p&&ol&&li&将&code&Sec-WebSocket-Key&/code&跟&code&258EAFA5-E914-47DA-95CA-C5AB0DC85B11&/code&拼接。&/li&&li&通过SHA1计算出摘要,并转成base64字符串。&/li&&/ol&&p&伪代码如下:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-ddcfcefbbd7cea79e506e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&861& data-rawheight=&45& class=&origin_image zh-lightbox-thumb& width=&861& data-original=&https://pic1.zhimg.com/v2-ddcfcefbbd7cea79e506e_r.jpg&&&/figure&&p&验证下前面的返回结果:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-ce06baf6deacff_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&866& data-rawheight=&217& class=&origin_image zh-lightbox-thumb& width=&866& data-original=&https://pic4.zhimg.com/v2-ce06baf6deacff_r.jpg&&&/figure&&h2&&b&五、数据帧格式&/b&&/h2&&p&客户端、服务端数据的交换,离不开数据帧格式的定义。因此,在实际讲解数据交换之前,我们先来看下WebSocket的数据帧格式。&/p&&p&WebSocket客户端、服务端通信的最小单位是帧(frame),由1个或多个帧组成一条完整的消息(message)。&/p&&ol&&li&发送端:将消息切割成多个帧,并发送给服务端;&/li&&li&接收端:接收消息帧,并将关联的帧重新组装成完整的消息;&/li&&/ol&&p&本节的重点,就是讲解&b&数据帧&/b&的格式。详细定义可参考 &a href=&https://link.zhihu.com/?target=https%3A//tools.ietf.org/html/rfc6455%23section-5.2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&RFC节&/a& 。&/p&&p&&b&1、数据帧格式概览&/b&&/p&&p&下面给出了WebSocket数据帧的统一格式。熟悉TCP/IP协议的同学对这样的图应该不陌生。&/p&&ol&&li&从左到右,单位是比特。比如&code&FIN&/code&、&code&RSV1&/code&各占据1比特,&code&opcode&/code&占据4比特。&/li&&li&内容包括了标识、操作代码、掩码、数据、数据长度等。(下一小节会展开)&/li&&/ol&&figure&&img src=&https://pic3.zhimg.com/v2-d826bd534aeedbfeaa5b2e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&868& data-rawheight=&370& class=&origin_image zh-lightbox-thumb& width=&868& data-original=&https://pic3.zhimg.com/v2-d826bd534aeedbfeaa5b2e_r.jpg&&&/figure&&p&&b&2、数据帧格式详解&/b&&/p&&p&针对前面的格式概览图,这里逐个字段进行讲解,如有不清楚之处,可参考协议规范,或留言交流。&/p&&p&&b&FIN&/b&:1个比特。&/p&&p&如果是1,表示这是消息(message)的最后一个分片(fragment),如果是0,表示不是是消息(message)的最后一个分片(fragment)。&/p&&p&&b&RSV1, RSV2, RSV3&/b&:各占1个比特。&/p&&p&一般情况下全为0。当客户端、服务端协商采用WebSocket扩展时,这三个标志位可以非0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用WebSocket扩展,连接出错。&/p&&p&&b&Opcode&/b&: 4个比特。&/p&&p&操作代码,Opcode的值决定了应该如何解析后续的数据载荷(data payload)。如果操作代码是不认识的,那么接收端应该断开连接(fail the connection)。可选的操作代码如下:&/p&&ul&&li&%x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。&/li&&li&%x1:表示这是一个文本帧(frame)&/li&&li&%x2:表示这是一个二进制帧(frame)&/li&&li&%x3-7:保留的操作代码,用于后续定义的非控制帧。&/li&&li&%x8:表示连接断开。&/li&&li&%x8:表示这是一个ping操作。&/li&&li&%xA:表示这是一个pong操作。&/li&&li&%xB-F:保留的操作代码,用于后续定义的控制帧。&/li&&/ul&&p&&b&Mask&/b&: 1个比特。&/p&&p&表示是否要对数据载荷进行掩码操作。从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作。&/p&&p&如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接。&/p&&p&如果Mask是1,那么在Masking-key中会定义一个掩码键(masking key),并用这个掩码键来对数据载荷进行反掩码。所有客户端发送到服务端的数据帧,Mask都是1。&/p&&p&掩码的算法、用途在下一小节讲解。&/p&&p&&b&Payload length&/b&:数据载荷的长度,单位是字节。为7位,或7+16位,或1+64位。&/p&&p&假设数Payload length === x,如果&/p&&ul&&li&x为0~126:数据的长度为x字节。&/li&&li&x为126:后续2个字节代表一个16位的无符号整数,该无符号整数的值为数据的长度。&/li&&li&x为127:后续8个字节代表一个64位的无符号整数(最高位为0),该无符号整数的值为数据的长度。&/li&&/ul&&p&此外,如果payload length占用了多个字节的话,payload length的二进制表达采用网络序(big endian,重要的位在前)。&/p&&p&&b&Masking-key&/b&:0或4字节(32位)&/p&&p&所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,Mask为1,且携带了4字节的Masking-key。如果Mask为0,则没有Masking-key。&/p&&p&备注:载荷数据的长度,不包括mask key的长度。&/p&&p&&b&Payload data&/b&:(x+y) 字节&/p&&p&载荷数据:包括了扩展数据、应用数据。其中,扩展数据x字节,应用数据y字节。&/p&&p&扩展数据:如果没有协商使用扩展的话,扩展数据数据为0字节。所有的扩展都必须声明扩展数据的长度,或者可以如何计算出扩展数据的长度。此外,扩展如何使用必须在握手阶段就协商好。如果扩展数据存在,那么载荷数据长度必须将扩展数据的长度包含在内。&/p&&p&应用数据:任意的应用数据,在扩展数据之后(如果存在扩展数据),占据了数据帧剩余的位置。载荷数据长度 减去 扩展数据长度,就得到应用数据的长度。&/p&&p&&b&3、掩码算法&/b&&/p&&p&掩码键(Masking-key)是由客户端挑选出来的32位的随机数。掩码操作不会影响数据载荷的长度。掩码、反掩码操作都采用如下算法:&/p&&p&首先,假设:&/p&&ul&&li&original-octet-i:为原始数据的第i字节。&/li&&li&transformed-octet-i:为转换后的数据的第i字节。&/li&&li&j:为&code&i mod 4&/code&的结果。&/li&&li&masking-key-octet-j:为mask key第j字节。&/li&&/ul&&p&算法描述为: original-octet-i 与 masking-key-octet-j 异或后,得到 transformed-octet-i。&/p&&blockquote&j = i MOD 4&br&transformed-octet-i = original-octet-i XOR masking-key-octet-j&/blockquote&&h2&&b&六、数据传递&/b&&/h2&&p&一旦WebSocket客户端、服务端建立连接后,后续的操作都是基于数据帧的传递。&/p&&p&WebSocket根据&code&opcode&/code&来区分操作的类型。比如&code&0x8&/code&表示断开连接,&code&0x0&/code&-&code&0x2&/code&表示数据交互。&/p&&p&&b&1、数据分片&/b&&/p&&p&WebSocket的每条消息可能被切分成多个数据帧。当WebSocket的接收方收到一个数据帧时,会根据&code&FIN&/code&的值来判断,是否已经收到消息的最后一个数据帧。&/p&&p&FIN=1表示当前数据帧为消息的最后一个数据帧,此时接收方已经收到完整的消息,可以对消息进行处理。FIN=0,则接收方还需要继续监听接收其余的数据帧。&/p&&p&此外,&code&opcode&/code&在数据交换的场景下,表示的是数据的类型。&code&0x01&/code&表示文本,&code&0x02&/code&表示二进制。而&code&0x00&/code&比较特殊,表示延续帧(continuation frame),顾名思义,就是完整消息对应的数据帧还没接收完。&/p&&p&&b&2、数据分片例子&/b&&/p&&p&直接看例子更形象些。下面例子来自&a href=&https://link.zhihu.com/?target=https%3A//developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&MDN&/a&,可以很好地演示数据的分片。客户端向服务端两次发送消息,服务端收到消息后回应客户端,这里主要看客户端往服务端发送的消息。&/p&&p&&b&第一条消息&/b&&/p&&p&FIN=1, 表示是当前消息的最后一个数据帧。服务端收到当前数据帧后,可以处理消息。opcode=0x1,表示客户端发送的是文本类型。&/p&&p&&b&第二条消息&/b&&/p&&ol&&li&FIN=0,opcode=0x1,表示发送的是文本类型,且消息还没发送完成,还有后续的数据帧。&/li&&li&FIN=0,opcode=0x0,表示消息还没发送完成,还有后续的数据帧,当前的数据帧需要接在上一条数据帧之后。&/li&&li&FIN=1,opcode=0x0,表示消息已经发送完成,没有后续的数据帧,当前的数据帧需要接在上一条数据帧之后。服务端可以将关联的数据帧组装成完整的消息。&/li&&/ol&&figure&&img src=&https://pic3.zhimg.com/v2-19bce2a5b57e47d6d006daccfbc943d4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&861& data-rawheight=&180& class=&origin_image zh-lightbox-thumb& width=&861& data-original=&https://pic3.zhimg.com/v2-19bce2a5b57e47d6d006daccfbc943d4_r.jpg&&&/figure&&h2&&b&七、连接保持+心跳&/b&&/h2&&p&WebSocket为了保持客户端、服务端的实时双向通信,需要确保客户端、服务端之间的TCP通道保持连接没有断开。然而,对于长时间没有数据往来的连接,如果依旧长时间保持着,可能会浪费包括的连接资源。&/p&&p&但不排除有些场景,客户端、服务端虽然长时间没有数据往来,但仍需要保持连接。这个时候,可以采用心跳来实现。&/p&&ul&&li&发送方-&接收方:ping&/li&&li&接收方-&发送方:pong&/li&&/ul&&p&ping、pong的操作,对应的是WebSocket的两个控制帧,&code&opcode&/code&分别是&code&0x9&/code&、&code&0xA&/code&。&/p&&p&举例,WebSocket服务端向客户端发送ping,只需要如下代码(采用&code&ws&/code&模块)&/p&&figure&&img src=&https://pic2.zhimg.com/v2-10f469dd3a2bbaf5935281c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&869& data-rawheight=&49& class=&origin_image zh-lightbox-thumb& width=&869& data-original=&https://pic2.zhimg.com/v2-10f469dd3a2bbaf5935281c_r.jpg&&&/figure&&h2&&b&八、Sec-WebSocket-Key/Accept的作用&/b&&/h2&&p&前面提到了,&code&Sec-WebSocket-Key/Sec-WebSocket-Accept&/code&在主要作用在于提供基础的防护,减少恶意连接、意外连接。&/p&&p&作用大致归纳如下:&/p&&ol&&li&避免服务端收到非法的websocket连接(比如http客户端不小心请求连接websocket服务,此时服务端可以直接拒绝连接)&/li&&li&确保服务端理解websocket连接。因为ws握手阶段采用的是http协议,因此可能ws连接是被一个http服务器处理并返回的,此时客户端可以通过Sec-WebSocket-Key来确保服务端认识ws协议。(并非百分百保险,比如总是存在那么些无聊的http服务器,光处理Sec-WebSocket-Key,但并没有实现ws协议。。。)&/li&&li&用浏览器里发起ajax请求,设置header时,Sec-WebSocket-Key以及其他相关的header是被禁止的。这样可以避免客户端发送ajax请求时,意外请求协议升级(websocket upgrade)&/li&&li&可以防止反向代理(不理解ws协议)返回错误的数据。比如反向代理前后收到两次ws连接的升级请求,反向代理把第一次请求的返回给cache住,然后第二次请求到来时直接把cache住的请求给返回(无意义的返回)。&/li&&li&Sec-WebSocket-Key主要目的并不是确保数据的安全性,因为Sec-WebSocket-Key、Sec-WebSocket-Accept的转换计算公式是公开的,而且非常简单,最主要的作用是预防一些常见的意外情况(非故意的)。&/li&&/ol&&blockquote&强调:Sec-WebSocket-Key/Sec-WebSocket-Accept 的换算,只能带来基本的保障,但连接是否安全、数据是否安全、客户端/服务端是否合法的 ws客户端、ws服务端,其实并没有实际性的保证。&/blockquote&&h2&&b&九、数据掩码的作用&/b&&/h2&&p&WebSocket协议中,数据掩码的作用是增强协议的安全性。但数据掩码并不是为了保护数据本身,因为算法本身是公开的,运算也不复杂。除了加密通道本身,似乎没有太多有效的保护通信安全的办法。&/p&&p&那么为什么还要引入掩码计算呢,除了增加计算机器的运算量外似乎并没有太多的收益(这也是不少同学疑惑的点)。&/p&&p&答案还是两个字:&b&安全&/b&。但并不是为了防止数据泄密,而是为了防止早期版本的协议中存在的代理缓存污染攻击(proxy cache poisoning attacks)等问题。&/p&&p&&b&1、代理缓存污染攻击&/b&&/p&&p&下面摘自2010年关于安全的一段讲话。其中提到了代理服务器在协议实现上的缺陷可能导致的安全问题。&a href=&https://link.zhihu.com/?target=http%3A//w2spconf.com/2011/papers/websocket.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&猛击出处&/a&。&/p&&blockquote&“We show, empirically, that the current version of the WebSocket consent mechanism is vulnerable to proxy cache poisoning attacks. Even though the WebSocket handshake is based on HTTP, which should be understood by most network intermediaries, the handshake uses the esoteric “Upgrade” mechanism of HTTP [5]. In our experiment, we find that many proxies do not implement the Upgrade mechanism properly, which causes the handshake to succeed even though subsequent traffic over the socket will be misinterpreted by the proxy.”&br&[TALKING] Huang, L-S., Chen, E., Barth, A., Rescorla, E., and C.&/blockquote&&figure&&img src=&https://pic4.zhimg.com/v2-ace7741b28_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&831& data-rawheight=&49& class=&origin_image zh-lightbox-thumb& width=&831& data-original=&https://pic4.zhimg.com/v2-ace7741b28_r.jpg&&&/figure&&p&在正式描述攻击步骤之前,我们假设有如下参与者:&/p&&ul&&li&攻击者、攻击者自己控制的服务器(简称“邪恶服务器”)、攻击者伪造的资源(简称“邪恶资源”)&/li&&li&受害者、受害者想要访问的资源(简称“正义资源”)&/li&&li&受害者实际想要访问的服务器(简称“正义服务器”)&/li&&li&中间代理服务器&/li&&/ul&&p&攻击步骤一:&/p&&ol&&li&&b&攻击者&/b&浏览器 向 &b&邪恶服务器&/b& 发起WebSocket连接。根据前文,首先是一个协议升级请求。&/li&&li&协议升级请求 实际到达 &b&代理服务器&/b&。&/li&&li&&b&代理服务器&/b& 将协议升级请求转发到 &b&邪恶服务器&/b&。&/li&&li&&b&邪恶服务器&/b& 同意连接,&b&代理服务器&/b& 将响应转发给 &b&攻击者&/b&。&/li&&/ol&&p&由于 upgrade 的实现上有缺陷,&b&代理服务器&/b& 以为之前转发的是普通的HTTP消息。因此,当&b&协议服务器&/b& 同意连接,&b&代理服务器&/b& 以为本次会话已经结束。&/p&&p&攻击步骤二:&/p&&ol&&li&&b&攻击者&/b& 在之前建立的连接上,通过WebSocket的接口向 &b&邪恶服务器&/b& 发送数据,且数据是精心构造的HTTP格式的文本。其中包含了 &b&正义资源&/b& 的地址,以及一个伪造的host(指向&b&正义服务器&/b&)。(见后面报文)&/li&&li&请求到达 &b&代理服务器&/b& 。虽然复用了之前的TCP连接,但 &b&代理服务器&/b& 以为是新的HTTP请求。&/li&&li&&b&代理服务器&/b& 向 &b&邪恶服务器&/b& 请求 &b&邪恶资源&/b&。&/li&&li&&b&邪恶服务器&/b& 返回 &b&邪恶资源&/b&。&b&代理服务器&/b& 缓存住 &b&邪恶资源&/b&(url是对的,但host是 &b&正义服务器&/b& 的地址)。&/li&&/ol&&p&到这里,受害者可以登场了:&/p&&ol&&li&&b&受害者&/b& 通过 &b&代理服务器&/b& 访问 &b&正义服务器&/b& 的 &b&正义资源&/b&。&/li&&li&&b&代理服务器&/b& 检查该资源的url、host,发现本地有一份缓存(伪造的)。&/li&&li&&b&代理服务器&/b& 将 &b&邪恶资源&/b& 返回给 &b&受害者&/b&。&/li&&li&&b&受害者&/b& 卒。&/li&&/ol&&p&附:前面提到的精心构造的“HTTP请求报文”。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-e696f895f51f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&868& data-rawheight=&126& class=&origin_image zh-lightbox-thumb& width=&868& data-original=&https://pic3.zhimg.com/v2-e696f895f51f_r.jpg&&&/figure&&p&&b&2、当前解决方案&/b&&/p&&p&最初的提案是对数据进行加密处理。基于安全、效率的考虑,最终采用了折中的方案:对数据载荷进行掩码处理。&/p&&p&需要注意的是,这里只是限制了浏览器对数据载荷进行掩码处理,但是坏人完全可以实现自己的WebSocket客户端、服务端,不按规则来,攻击可以照常进行。&/p&&p&但是对浏览器加上这个限制后,可以大大增加攻击的难度,以及攻击的影响范围。如果没有这个限制,只需要在网上放个钓鱼网站骗人去访问,一下子就可以在短时间内展开大范围的攻击。&/p&&h2&&b&十、写在后面&/b&&/h2&&p&WebSocket可写的东西还挺多,比如WebSocket扩展。客户端、服务端之间是如何协商、使用扩展的。WebSocket扩展可以给协议本身增加很多能力和想象空间,比如数据的压缩、加密,以及多路复用等。&/p&&p&篇幅所限,这里先不展开,感兴趣的同学可以留言交流。文章如有错漏,敬请指出。&/p&&h2&&b&十一、相关链接&/b&&/h2&&p&RFC6455:websocket规范&br&&a href=&https://link.zhihu.com/?target=https%3A//tools.ietf.org/html/rfc6455& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&tools.ietf.org/html/rfc&/span&&span class=&invisible&&6455&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&规范:数据帧掩码细节&br&&a href=&https://link.zhihu.com/?target=https%3A//tools.ietf.org/html/rfc6455%23section-5.3& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&tools.ietf.org/html/rfc&/span&&span class=&invisible&&6455#section-5.3&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&规范:数据帧格式&br&&a href=&https://link.zhihu.com/?target=https%3A//tools.ietf.org/html/rfc6455%23section-5.1& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&tools.ietf.org/html/rfc&/span&&span class=&invisible&&6455#section-5.1&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&server-example&br&&a href=&https://link.zhihu.com/?target=https%3A//github.com/websockets/ws%23server-example& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/websockets/w&/span&&span class=&invisible&&s#server-example&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&编写websocket服务器&br&&a href=&https://link.zhihu.com/?target=https%3A//developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&developer.mozilla.org/e&/span&&span class=&invisible&&n-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&对网络基础设施的攻击(数据掩码操作所要预防的事情)&br&&a href=&https://link.zhihu.com/?target=https%3A//tools.ietf.org/html/rfc6455%23section-10.3& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&tools.ietf.org/html/rfc&/span&&span class=&invisible&&6455#section-10.3&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&Talking to Yourself for Fun and Profit(含有攻击描述)&br&&a href=&https://link.zhihu.com/?target=http%3A//w2spconf.com/2011/papers/websocket.pdf& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&w2spconf.com/2011/paper&/span&&span class=&invisible&&s/websocket.pdf&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&What is Sec-WebSocket-Key for?&br&&a href=&https://link.zhihu.com/?target=https%3A//stackoverflow.com/questions//what-is-sec-websocket-key-for& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&stackoverflow.com/quest&/span&&span class=&invisible&&ions//what-is-sec-websocket-key-for&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&10.3. Attacks On Infrastructure (Masking)&br&&a href=&https://link.zhihu.com/?target=https%3A//tools.ietf.org/html/rfc6455%23section-10.3& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&tools.ietf.org/html/rfc&/span&&span class=&invisible&&6455#section-10.3&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&Talking to Yourself for Fun and Profit&br&&a href=&https://link.zhihu.com/?target=http%3A//w2spconf.com/2011/papers/websocket.pdf& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&w2spconf.com/2011/paper&/span&&span class=&invisible&&s/websocket.pdf&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&Why are WebSockets masked?&br&&a href=&https://link.zhihu.com/?target=https%3A//stackoverflow.com/questions//why-are-websockets-masked& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&stackoverflow.com/quest&/span&&span class=&invisible&&ions//why-are-websockets-masked&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&How does websocket frame masking protect against cache poisoning?&br&&a href=&https://link.zhihu.com/?target=https%3A//security.stackexchange.com/questions/36930/how-does-websocket-frame-masking-protect-against-cache-poisoning& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&security.stackexchange.com&/span&&span class=&invisible&&/questions/36930/how-does-websocket-frame-masking-protect-against-cache-poisoning&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&What is the mask in a WebSocket frame?&br&&a href=&https://link.zhihu.com/?target=https%3A//stackoverflow.com/questions//what-is-the-mask-in-a-websocket-frame& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&stackoverflow.com/quest&/span&&span class=&invisible&&ions//what-is-the-mask-in-a-websocket-frame&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&作者:程序猿小卡&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/39317/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&原文&/a& &/p&&p&&b&更多技术干货敬请关注云栖社区知乎机构号:&a href=&https://www.zhihu.com/org/a-li-yun-yun-qi-she-qu-48& class=&internal&&阿里云云栖社区 - 知乎&/a&&/b&&/p&
摘要: WebSocket的出现,使得浏览器具备了实时双向通信的能力。本文由浅入深,介绍了WebSocket如何建立连接、交换数据的细节,以及数据帧的格式。此外,还简要介绍了针对WebSocket的安全攻击,以及协议是如何抵御类似攻击的。一、内容概览WebSocket的出现…
&p&Nginx限速模块分为哪几种?按请求速率限速的burst和nodelay参数是什么意思?漏桶算法和令牌桶算法究竟有什么不同?本文将带你一探究竟。我们会通过一些简单的示例展示Nginx限速模块是如何工作的,然后结合代码讲解其背后的算法和原理。&/p&&h2&&b&核心算法&/b&&/h2&&p&在探究Nginx限速模块之前,我们先来看看网络传输中常用两个的流量控制算法:&b&漏桶算法&/b&和&b&令牌桶算法&/b&。这两只“桶”到底有什么异同呢?&/p&&h2&漏桶算法(leaky bucket)&/h2&&p&&a href=&https://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Leaky_bucket& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&漏桶算法(leaky bucket)&/a&算法思想如图所示:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-2ff7a9dbcabf960efe116813_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&572& data-rawheight=&978& class=&origin_image zh-lightbox-thumb& width=&572& data-original=&https://pic1.zhimg.com/v2-2ff7a9dbcabf960efe116813_r.jpg&&&/figure&&p&一个形象的解释是:&/p&&ul&&li&水(请求)从上方倒入水桶,从水桶下方流出(被处理);&/li&&li&来不及流出的水存在水桶中(缓冲),以&b&固定速率&/b&流出;&/li&&li&水桶满后水溢出(丢弃)。&/li&&/ul&&p&这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃。&/p&&h2&令牌桶算法(token bucket)&/h2&&p&&a href=&https://link.zhihu.com/?target=https%3A//www.cse.wustl.edu/%7Ejain/cis788-97/ftp/integrated_services/index.html%23tokenbucket& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&令牌桶(token bucket)&/a&算法思想如图所示:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-fe9ba462b5594faf2a80266b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&502& data-rawheight=&310& class=&origin_image zh-lightbox-thumb& width=&502& data-original=&https://pic4.zhimg.com/v2-fe9ba462b5594faf2a80266b_r.jpg&&&/figure&&p&算法思想是:&/p&&ul&&li&令牌以固定速率产生,并缓存到令牌桶中;&/li&&li&令牌桶放满时,多余的令牌被丢弃;&/li&&li&请求要消耗等比例的令牌才能被处理;&/li&&li&令牌不够时,请求被缓存。&/li&&/ul&&p&相比漏桶算法,令牌桶算法不同之处在于它不但有一只“桶”,还有个队列,这个桶是用来存放令牌的,队列才是用来存放请求的。&/p&&p&从作用上来说,漏桶和令牌桶算法最明显的区别就是是否允许&b&突发流量(burst)&/b&的处理,漏桶算法能够&b&强行限制数据的实时传输(处理)速率&/b&,对突发流量不做额外处理;而令牌桶算法能够在&b&限制数据的平均传输速率的同时允许某种程度的突发传输&/b&。&/p&&p&Nginx按请求速率限速模块使用的是漏桶算法,即能够强行保证请求的实时处理速度不会超过设置的阈值。&/p&&h2&&b&Nginx限速模块&/b&&/h2&&p&Nginx主要有两种限速方式:按连接数限速(&code&ngx_http_limit_conn_module&/code&)、按请求速率限速(&code&ngx_http_limit_req_module&/code&)。我们着重讲解按请求速率限速。&/p&&h2&按连接数限速&/h2&&p&&a href=&https://link.zhihu.com/?target=http%3A//nginx.org/en/docs/http/ngx_http_limit_conn_module.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&按连接数限速&/a&是指限制单个IP(或者其他的key)同时发起的连接数,超出这个限制后,Nginx将直接拒绝更多的连接。这个模块的配置比较好理解,详见&a href=&https://link.zhihu.com/?target=http%3A//nginx.org/en/docs/http/ngx_http_limit_conn_module.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ngx_http_limit_conn_module官方文档&/a&。&/p&&h2&按请求速率限速&/h2&&p&&a href=&https://link.zhihu.com/?target=http%3A//nginx.org/en/docs/http/ngx_http_limit_req_module.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&按请求速率限速&/a&是指限制单个IP(或者其他的key)发送请求的速率,超出指定速率后,Nginx将直接拒绝更多的请求。采用&i&leaky bucket&/i&算法实现。为深入了解这个模块,我们先从实验现象说起。开始之前我们先简单介绍一下该模块的配置方式,以下面的配置为例:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-7b6aec17baefc8aa3374_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&864& data-rawheight=&175& class=&origin_image zh-lightbox-thumb& width=&864& data-original=&https://pic1.zhimg.com/v2-7b6aec17baefc8aa3374_r.jpg&&&/figure&&p&使用&code&limit_req_zone&/code&关键字,我们定义了一个名为mylimit大小为10MB的共享内存区域(&code&zone&/code&),用来存放限速相关的统计信息,限速的&code&key&/code&值为二进制的IP地址(&code&$binary_remote_addr&/code&),限速上限(&code&rate&/code&)为2r/s;接着我们使用&code&limit_req&/code&关键字将上述规则作用到&code&/search/&/code&上。&code&burst&/code&和&code&nodelay&/code&的作用稍后解释。&/p&&p&使用上述规则,对于/search/目录的访问,单个IP的访问速度被限制在了2请求/秒,超过这个限制的访问将直接被Nginx拒绝。&/p&&p&&b&实验1——毫秒级统计&/b&&/p&&p&我们有如下配置:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-3dd07cea19becfbe276feac_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&866& data-rawheight=&179& class=&origin_image zh-lightbox-thumb& width=&866& data-original=&https://pic1.zhimg.com/v2-3dd07cea19becfbe276feac_r.jpg&&&/figure&&p&上述规则限制了每个IP访问的速度为2r/s,并将该规则作用于跟目录。如果单个IP在非常短的时间内并发发送多个请求,结果会怎样呢?&/p&&figure&&img src=&https://pic1.zhimg.com/v2-fa8d1ef9c89_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&861& data-rawheight=&195& class=&origin_image zh-lightbox-thumb& width=&861& data-original=&https://pic1.zhimg.com/v2-fa8d1ef9c89_r.jpg&&&/figure&&p&我们使用单个IP在10ms内发并发送了6个请求,只有1个成功,剩下的5个都被拒绝。我们设置的速度是2r/s,为什么只有1个成功呢,是不是Nginx限制错了?当然不是,&b&是因为Nginx的限流统计是基于毫秒的,我们设置的速度是2r/s,转换一下就是500ms内单个IP只允许通过1个请求&/b&,从501ms开始才允许通过第二个请求。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-a923be79e5bfb07aa0ea_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&766& data-rawheight=&274& class=&origin_image zh-lightbox-thumb& width=&766& data-original=&https://pic2.zhimg.com/v2-a923be79e5bfb07aa0ea_r.jpg&&&/figure&&p&&br&&/p&&p&&b&实验2——burst允许缓存处理突发请求&/b&&/p&&p&实验1我们看到,我们短时间内发送了大量请求,Nginx按照毫秒级精度统计,超出限制的请求直接拒绝。这在实际场景中未免过于苛刻,真实网络环境中请求到来不是匀速的,很可能有请求“突发”的情况,也就是“一股子一股子”的。Nginx考虑到了这种情况,可以通过&code&burst&/code&关键字开启对突发请求的缓存处理,而不是直接拒绝。&/p&&p&来看我们的配置:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-2121faf3d728d14e88f9fdb71e84d114_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&864& data-rawheight=&175& class=&origin_image zh-lightbox-thumb& width=&864& data-original=&https://pic2.zhimg.com/v2-2121faf3d728d14e88f9fdb71e84d114_r.jpg&&&/figure&&p&我们加入了&code&burst=4&/code&,意思是每个key(此处是每个IP)最多允许4个突发请求的到来。如果单个IP在10ms内发送6个请求,结果会怎样呢?&/p&&figure&&img src=&https://pic2.zhimg.com/v2-70b29aa0d30abec245bdea38088d7ded_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&868& data-rawheight=&196& class=&origin_image zh-lightbox-thumb& width=&868& data-original=&https://pic2.zhimg.com/v2-70b29aa0d30abec245bdea38088d7ded_r.jpg&&&/figure&&p&相比实验1成功数增加了4个,这个我们设置的burst数目是一致的。具体处理流程是:1个请求被立即处理,4个请求被放到burst队列里,另外一个请求被拒绝。&b&通过&code&burst&/code&参数,我们使得Nginx限流具备了缓存处理突发流量的能力&/b&。&/p&&p&但是请注意:burst的作用是让多余的请求可以先放到队列里,慢慢处理。如果不加nodelay参数,队列里的请求&b&不会立即处理&/b&,而是按照rate设置的速度,以毫秒级精确的速度慢慢处理。&/p&&p&&b&实验3——nodelay降低排队时间&/b&&/p&&p&实验2中我们看到,通过设置burst参数,我们可以允许Nginx缓存处理一定程度的突发,多余的请求可以先放到队列里,慢慢处理,这起到了平滑流量的作用。但是如果队列设置的比较大,请求排队的时间就会比较长,用户角度看来就是RT变长了,这对用户很不友好。有什么解决办法呢?&b&&code&nodelay&/code&参数允许请求在排队的时候就立即被处理,也就是说只要请求能够进入burst队列,就会立即被后台worker处理&/b&,请注意,这意味着burst设置了nodelay时,系统瞬间的QPS可能会超过rate设置的阈值。&code&nodelay&/code&参数要跟&code&burst&/code&一起使用才有作用。&/p&&p&延续实验2的配置,我们加入nodelay选项:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-ceb1d5c812c9cfdbf612_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&854& data-rawheight=&177& class=&origin_image zh-lightbox-thumb& width=&854& data-original=&https://pic3.zhimg.com/v2-ceb1d5c812c9cfdbf612_r.jpg&&&/figure&&p&单个IP 10ms内并发发送6个请求,结果如下:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-f73b1d56d201662def37be57528c60ff_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&858& data-rawheight=&215& class=&origin_image zh-lightbox-thumb& width=&858& data-original=&https://pic3.zhimg.com/v2-f73b1d56d201662def37be57528c60ff_r.jpg&&&/figure&&p&跟实验2相比,请求成功率没变化,但是&b&总体耗时变短了&/b&。这怎么解释呢?实验2中,有4个请求被放到burst队列当中,工作进程每隔500ms(rate=2r/s)取一个请求进行处理,最后一个请求要排队2s才会被处理;实验3中,请求放入队列跟实验2是一样的,但不同的是,队列中的请求同时具有了被处理的资格,所以实验3中的5个请求可以说是同时开始被处理的,花费时间自然变短了。&/p&&p&但是请注意,虽然设置burst和nodelay能够降低突发请求的处理时间,但是长期来看并不会提高吞吐量的上限,长期吞吐量的上限是由rate决定的,因为nodelay只能保证burst的请求被立即处理,但Nginx会限制队列元素释放的速度,就像是限制了令牌桶中令牌产生的速度。&/p&&p&看到这里你可能会问,加入了nodelay参数之后的限速算法,到底算是哪一个“桶”,是漏桶算法还是令牌桶算法?当然还算是漏桶算法。考虑一种情况,令牌桶算法的token为耗尽时会怎么做呢?由于它有一个请求队列,所以会把接下来的请求缓存下来,缓存多少受限于队列大小。但此时缓存这些请求还有意义吗?如果server已经过载,缓存队列越来越长,RT越来越高,即使过了很久请求被处理了,对用户来说也没什么价值了。所以当token不够用时,最明智的做法就是直接拒绝用户的请求,这就成了漏桶算法,哈哈~&/p&&h2&&b&源码剖析&/b&&/h2&&p&经过上面的示例,我们队请求限速模块有了一定的认识,现在我们深入剖析代码实现。按请求速率限流模块&code&ngx_http_limit_req_module&/code&代码位于[src/http/modules/ngx_http_limit_req_module.c&br&](&a href=&https://link.zhihu.com/?target=https%3A//github.com/nginx/nginx/blob/master/src/http/modules/ngx_http_limit_req_module.c%29& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/nginx/nginx/&/span&&span class=&invisible&&blob/master/src/http/modules/ngx_http_limit_req_module.c)&/span&&span class=&ellipsis&&&/span&&/a&,900多好代码可谓短小精悍。相关代码有两个核心数据结构:&/p&&ol&&li&红黑树:通过红黑树记录每个节点(按照声明时指定的key)的统计信息,方便查找;&/li&&li&LRU队列:将红黑树上的节点按照最近访问时间排序,时间近的放在队列头部,以便使用LRU队列淘汰旧的节点,避免内存溢出。&/li&&/ol&&p&这两个关键对象存储在&code&ngx_http_limit_req_shctx_t&/code&中:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-9f12f575fe2ffb2d1433_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&855& data-rawheight=&119& class=&origin_image zh-lightbox-thumb& width=&855& data-original=&https://pic4.zhimg.com/v2-9f12f575fe2ffb2d1433_r.jpg&&&/figure&&p&其中除了rbtree和queue之外,还有一个叫做sentinel的变量,这个变量用作红黑树的NIL节点。&/p&&p&该模块的核心逻辑在函数&code&ngx_http_limit_req_lookup()&/code&中,这个函数主要流程是怎样呢?对于每一个请求:&/p&&ol&&li&从根节点开始查找红黑树,找到key对应的节点;&/li&&li&找到后修改该点在LRU队列中的位置,表示该点最近被访问过;&/li&&li&执行漏桶算法;&/li&&li&没找到时根据LRU淘汰,腾出空间;&/li&&li&生成并插入新的红黑树节点;&/li&&li&执行下一条限流规则。&/li&&/ol&&p&流程很清晰,但是代码中牵涉到红黑树、LRU队列等高级数据结构,是不是会写得很复杂?好在Nginx作者功力深厚,代码写得简洁易懂,哈哈~&/p&&figure&&img src=&https://pic1.zhimg.com/v2-2f92dbd65b9fd201d7dba_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&865& data-rawheight=&788& class=&origin_image zh-lightbox-thumb& width=&865& data-original=&https://pic1.zhimg.com/v2-2f92dbd65b9fd201d7dba_r.jpg&&&/figure&&p&代码有三种返回值,它们的意思是:&/p&&ul&&li&NGX_BUSY 超过了突发门限,拒绝&/li&&li&NGX_OK 未超过限制,通过&/li&&li&NGX_AGAIN 未超过限制,但是还有规则未执行,需执行下一条限流规则&/li&&/ul&&p&上述代码不难理解,但我们还有几个问题:&/p&&ol&&li&LRU是如何实现的?&/li&&li&漏桶算法是如何实现的?&/li&&li&每个key相关的burst队列在哪里?&/li&&/ol&&p&&b&LRU是如何实现的&/b&&/p&&p&LRU算法的实现很简单,&b&如果一个节点被访问了,那么就把它移到队列的头部,当空间不足需要淘汰节点时,就选出队列尾部的节点淘汰掉&/b&,主要体现在如下代码中:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-dc050bd59f55a1c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&863& data-rawheight=&104& class=&origin_image zh-lightbox-thumb& width=&863& data-original=&https://pic1.zhimg.com/v2-dc050bd59f55a1c_r.jpg&&&/figure&&p&&b&漏桶算法是如何实现的&/b&&/p&&p&漏桶算法的实现也比我们想象的简单,其核心是这一行公式&code&excess = lr-&excess - ctx-&rate * ngx_abs(ms) / 1000 + 1000&/code&,这样代码的意思是:excess表示当前key上遗留的请求数,&i&本次遗留的请求数 = 上次遗留的请求数 - 预设速率 X 过去的时间 + 1&/i&。这个1表示当前这个请求,由于Nginx内部表示将单位缩小了1000倍,所以1个请求要转换成1000。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-39ad25a796c7ac16fbe4b35df8bfcbff_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&867& data-rawheight=&248& class=&origin_image zh-lightbox-thumb& width=&867& data-original=&https://pic4.zhimg.com/v2-39ad25a796c7ac16fbe4b35df8bfcbff_r.jpg&&&/figure&&p&上述代码受限算出当前key上遗留的请求数,如果超过了burst,就直接拒绝;由于Nginx允许多条限速规则同时起作用,如果已是最后一条规则,则允许通过,否则执行下一条规则。&/p&&p&&b&单个key相关的burst队列在哪里&/b&&/p&&p&没有单个key相关的burst队列。上面代码中我们看到当到达最后一条规则时,只要&code&excess&limit-&burst&/code&限速模块就会返回NGX_OK,并没有把多余请求放入队列的操作,这是因为Nginx是基于timer来管理请求的,当限速模块返回NGX_OK时,调度函数会计算一个延迟处理的时间,同时把这个请求放入到共享的timer队列中(一棵按等待时间从小到大排序的红黑树)。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&ngx_http_limit_req_handler(ngx_http_request_t *r)
for (n = 0; n & lrcf-&limits. n++) {
ngx_shmtx_lock(&ctx-&shpool-&mutex);// 获取锁
rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess, // 执行漏桶算法
(n == lrcf-&limits.nelts - 1));
ngx_shmtx_unlock(&ctx-&shpool-&mutex);// 释放锁
if (rc != NGX_AGAIN)
delay = ngx_http_limit_req_account(limits, n, &excess, &limit);// 计算当前请求需要的延迟时间
if (!delay) {
return NGX_DECLINED;// 不需要延迟,交给后续的handler进行处理
ngx_add_timer(r-&connection-&write, delay);// 否则将请求放到定时器队列里
return NGX_AGAIN; // the request has been successfully processed, the request must be suspended until some event. http://www.nginxguts.com/2011/01/phases/
&/code&&/pre&&/div&&p&我们看到&code&ngx_http_limit_req_handler()&/code&调用了函数&code&ngx_http_limit_req_lookup()&/code&,并根据其返回值决定如何操作:或是拒绝,或是交给下一个handler处理,或是将请求放入定期器队列。当限速规则都通过后,该hanlder通过调用函数&code&ngx_http_limit_req_account()&/code&得出当前请求需要的延迟时间,如果不需要延迟,就将请求交给后续的handler进行处理,否则将请求放到定时器队列里。注意这个定时器队列是共享的,并没有为单独的key(比如,每个IP地址)设置队列。关于handler模块背景知识的介绍,可参考Tengine团队撰写的&a href=&https://link.zhihu.com/?target=http%3A//tengine.taobao.org/book/chapter_03.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Nginx开发从入门到精通&/a&&/p&&p&关于按请求速率限速的原理讲解,可参考&a href=&https://link.zhihu.com/?target=https%3A//www.nginx.com/blog/rate-limiting-nginx/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Rate Limiting with NGINX and NGINX Plus&/a&,关于源码更详细的解析可参考&a href=&https://link.zhihu.com/?target=http%3A//dev.dafan.info/detail/Fp%3D29-44& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ngx_http_limit_req_module 源码分析&/a&以及y123456yz的&a href=&https://link.zhihu.com/?target=https%3A//github.com/y123456yz/reading-code-of-nginx-1.9.2/blob/master/nginx-1.9.2/src/http/modules/ngx_http_limit_req_module.c& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Nginx源码分析的git项目&/a&&/p&&h2&&b&结尾&/b&&/h2&&p&本文主要讲解了Nginx按请求速率限速模块的用法和原理,其中burst和nodelay参数是容易引起误解的,虽然可通过burst允许缓存处理突发请求,结合nodelay能够降低突发请求的处理时间,但是长期来看他们并不会提高吞吐量的上限,长期吞吐量的上限是由rate决定的。需要特别注意的是,burst设置了nodelay时,系统瞬间的QPS可能会超过rate设置的阈值。&/p&&p&本文只是对Nginx管中窥豹,更多关于Nginx介绍的文章,可参考Tengine团队撰写的&a href=&https://link.zhihu.com/?target=http%3A//tengine.taobao.org/book/index.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Nginx开发从入门到精通&/a&。&/p&&p&作者:a小鼠标&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/38226/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&原文&/a&&/p&&p&&b&更多技术干货敬请关注云栖社区知乎机构号:&a href=&https://www.zhihu.com/org/a-li-yun-yun-qi-she-qu-48& class=&internal&&阿里云云栖社区 - 知乎&/a&&/b&&/p&
Nginx限速模块分为哪几种?按请求速率限速的burst和nodelay参数是什么意思?漏桶算法和令牌桶算法究竟有什么不同?本文将带你一探究竟。我们会通过一些简单的示例展示Nginx限速模块是如何工作的,然后结合代码讲解其背后的算法和原理。核心算法在探究Nginx…
因为个人兴趣, 简单做过一些域名抢注方面的事情,
分 初级, 中级, 高级三个部分说一下, 希望大家都能有所得.&br&&br&&b&### 初级.
怎么注册刚过期域名 ###&/b&&br&&br&&b&1. 查看具体删除时间:&/b&&ul&&li&某域名过期/删除时间查询: &a href=&//link.zhihu.com/?target=http%3A//tool.chinaz.com/DomainDel& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&域名删除时间查询&/a&&/li&&li&最近可抢注域名查询 (国际): &a href=&//link.zhihu.com/?target=http%3A//www.pool.com/viewlist.aspx& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Pool.com : Pending Delete List&/a&&br&&/li&&li&最近可抢注域名查询 (国内): &a href=&//link.zhihu.com/?target=http%3A//backorder.ename.com/book/expiredendomainlist& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&易名:过期域名预订列表&/a&&/li&&/ul&&b&2.
预订抢注. &/b&&br&&ul&&li& 国内域名抢注: 去 ename,
等申请抢注&br&&/li&&ul&&li&好处是比较便宜,
国内域名成功较高,
国际域名成功较低, 并非很好的域名, 可以在此预订&/li&&/ul&&li& 国外域名抢注: 去 3个国外域名网站抢注: namejet, pool, snapnames &br&&/li&&ul&&li&好处是国际域名成功率高, 另外还有一些把抢注作为副业的注册商 name, godaddy 等也可以尝试一下.&/li&&/ul&&li& 品相不太好或只有你自己能理解意思的域名:
选择一家可信且相熟的机构,肉身自由发挥即可.&/li&&ul&&li&国内域名删除时间:
4点30 (北京时间)&/li&&li&国际域名删除时间:
2点~4点 (北京时间)&/li&&li&国内比较可信的服务商: &a href=&//link.zhihu.com/?target=http%3A//www.cnnic.cn/ggfw/hyzl/yxymzcfwjghdljgtj/59.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&2012年度注册服务机构服务水平评定结果&/a&&/li&&/ul&&/ul&&b&3.
竞价&/b&&br&&ul&&li& 上面的只是预订,
好域名一般不止一个人盯上,
这时就要时刻盯着最终竞价的结果,
每家注册商竞价的规则不同, 但大部分都是到竞价快结束时, 如有出价,会再将结束时间延长一些这样, 就不展开说了. &/li&&/ul&&br&&b&### 中级,
为什么这样注册过期域名 ###&/b&&br&&br&想要详细了解过期域名注册的信息及过程, 需要提前做一些功课:&br&&br&&b&1. 域名生态周期 &/b&&br&
其他同学的回答里也提到了,
域名有很多种状态,
和抢注有关的有以下四种:&br&&figure&&img src=&https://pic2.zhimg.com/50/2c1a09290ab6cbc3c77acef372afb933_b.jpg& data-rawwidth=&500& data-rawheight=&500& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&https://pic2.zhimg.com/50/2c1a09290ab6cbc3c77acef372afb933_r.jpg&&&/figure&&br&&ul&&li&活动状态 : 就是正在使用的,
如果你想要这个域名, 那要好好盯着 whois里面的过期时间..
年复一年.&/li&&li&保留状态 : 已经到了过期时间,
但还在注册商手中,
这时候可以进行续费, 并且没有额外的费用, 具体的时间需要看注册商的实力以及和 ICANN的关系, 越大的注册商一般这个期限可以留的越长.
一般是30天.
&/li&&ul&&li&例如 你在 日注册的域名,
日域名过期了,
日~ 日 你可以对域名进行续费, 并且不用多交钱. 前提是你选择的注册商还不错, 拿到了30天的赎回期域名保留权.&/li&&/ul&&br&&li&赎回状态:
如果很不幸,
没赶得及在保留状态续费域名,
域名会改变状态为 &赎回等待&.
这时候就需要多交钱才能重新得到域名所有权,
在这个状态的域名已经一只脚踏进了坟墓, 因为价格昂贵,不是很好的域名一般都会选择放弃.
ICANN 给出每次执行EPP命令的价格 是 40美元 ( 赎回也是EPP命令之一 ), 至于注册商给用户要多少钱就是他的事了, 一般有良心注册商的给的价格会是注册价格的10倍左右. 这是域名删除期的第一个阶段. &/li&&br&&li&等待删除状态:
这时候注册商的什么操作都不管用了,
老老实实等删除吧.
这是域名删除期的第二个阶段 ( 最后一个阶段是
咔嚓.. 删了. ). &/li&&br&&li&Pre-Relase 状态:
咦? 不是相关的是四种状态吗? pre-release状态怎么冒出来的?
这个状态其实是注册商 &私设& 的一个状态 ( 为了准确, 我翻了很多 ICANN 公布的档案和协议, 的确没有任何地方出现这个状态 ) . 但是 ICANN 并没有对其做出反应, 现在成了一个约定俗成的东西,被大家所接受。
那么 注册商为什么要设置这样一个状态, 它有什么用哪? &/li&&ul&&li&其实是注册商的把戏,归根结低还是为了多赚钱. &/li&&li&当一个还不错的域名经过了保留状态,
进入赎回期之时,
注册商觉得 这个傻小子现在还没续费, 应该不会再续费这个域名了,
这么个域名直接交给ICANN 删掉太可惜了, 自己什么也得不到, 怎么办哪?&/li&&li&贴心的几大抢注巨头出现了, 说你可以把域名让我来竞拍, 如果拍出去了 最后傻小子也没续费,
我分一部分钱给你.
如果拍出去了, 傻小子忽然肯花高价续费了, 那就再还给傻小子, 至于那些竞拍的人,道个歉就是,
你也能赚个赎回的差价.
注册商一听, 感觉不错, 自己怎么着都有钱拿, 于是pre-release 状态就这么产生了. &/li&&li&pre-release 状态严格来说属于赎回期,
所以域名所有者可进行高价续费,
但由于也在抢注巨头手中, 所以大家也可以去和注册商有协议的抢注商那里预订和竞价. &/li&&li&目前我只在 namejet 和 snapnames 两家见到过这种状态的域名.&/li&&li&我在上面吃过大亏.&/li&&/ul&&/ul&&b&2. 域名的将死之日&/b&&br&&ul&&li&一个正常的&域名轮回时长&是:
域名使用时间
续费时间 + 赎回时间 +
准备删除时间&/li&&li&这个时长会被注册商用各种手段来打破,
ICANN 对注册商有各种政策, 其中一条是 在域名付费5天内可随时撤销, 在付费5天以上30天内 可申请撤销,
所以很多注册商在域名过期之时会先续费一年, 如果在删除期到达之时还没有人高价赎回, 同同时又没有抢注商找他们洽谈, 自己又看不上这个域名的话, 才扔给 ICANN 回炉. 如果满足上面任意一点, 这个流程就会被打破.
类似的手段应该还有不少. &/li&&li&值得庆幸, 大部分域名的轮回时长还是遵守上面流程的.
&/li&&/ul&好吧, 里面这么多道道, 到底域名真正被删除的时间是多少哪?
我们的肉身什么时候才可以参与抢注? 其实对于我们, 不用想那么多, 这些我们只要了解就行了, 做好真正删除时间和自己心里预估时间不一致的准备, 看着下面几个地方等着即可,
&br&&ul&&li&找到域名所属的注册商, 了解域名注册商拿到的政策 ( 域名保留期的时长. )&/li&&li&紧盯域名的状态 &/li&&li&紧盯ICANN公布的近期要删除域名列表 &/li&&li&紧盯代注商公布的可抢注列表&/li&&/ul&&b&3. 你的对手&/b&&br&一般情况下, 如果不是肉身亲自上阵,
你的对手分为两个阶段:
抢注阶段 和 竞价阶段. &br&&ul&&li&
抢注阶段 :
一个稍微好点的域名, 选择一个或几个合适的代注商绝对是有必要的,
域名界也有28原则 甚至更高,
每年优质域名的80% ~ 90% 都被 10% ~ 20%的抢注巨头抢到,
那么怎么选择哪?
我的原则是好域名全网预订,
稍好域名看关系,
比如enom 和 namejet 本来就是一家,
enom 本身挂的抢注平台就是 namejet , 所以 enom 想卖点pre-release 的域名就会挂在 namejet 上, 如果你想抢这个域名, 那么在namejet上提前预订, 变数对你来说就会小很多,
这种技巧会帮你淘汰一批对手 ( 一般来说好点的域名还是要全网预订的,
很多网站只要押金 或填个信用卡号, 只预订抢注不成功也不交钱 ). &/li&&li&
竞价阶段 :
这个和传统拍卖很类似,
并且更熬人,
因为大部分的竞价都要持续3天以上,
并且我上面也提到, 越接近结束时间竞争越激烈, 可恨的是 注册商都会使用这一招, 当竞价接近结束时间时, 自动加一段时间.. 让好域名总会到达一个让他们满意的价格 而杜绝了人们想在最后一刻加价的投机心理.
注册商的另外一个手段是闯入竞价,
可以让一些最早没有参加抢注的人缴纳一部分&闯入费&
如果最终竞价成功, 闯入费退换,
竞价失败, 闯入费不退.
所以每个闯入者都可认为是对域名志在必得的人 价格也就会更高. &/li&&li&
给自己设定心里价位, 理性一点, 因为最终竞拍价格超过域名当前所值价格的例子有太多. &/li&&/ul&&br&&b&### 高级,
更多的知识及提高成功率 ###&/b&&br&&br&&b&1. 更多的知识&/b&&br&
上面我无数次的提到了 ICANN ,
它是干什么的哪?
从1985年第一个域名&a href=&//link.zhihu.com/?target=http%3A//nordu.net& class=& external& tar}

我要回帖

更多关于 不同的地方电脑的ip地址会相同吗 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信