DOM元素的特性与属性,刨根问底HTTP和WebSocket协议

HTTP Client Hints 介绍

2015/09/14 · HTML5 ·
算法

原文出处:
imququ(@屈光宇)   

最近几年各种 Web
技术一直在爆炸式发展,每天都有大量新东西涌现出来。针对这个现象,业内两位大佬最近先后发文表达了自己的观点:Stop
pushing the web
forward、Is
the web platform getting too
big?。其实很早之前我就意识到以我目前的精力,吃透所有
Web 新技术几乎是不可能完成的任务,我关注新技术的侧重点放在了性能优化上。

今天我要向大家介绍的技术是:HTTP Client
Hints,也与性能优化有关。利用这项技术,HTTP
客户端(通常可以认为是浏览器)能够主动将一些特性告诉服务端,以便服务端更有针对性地输出内容。这项技术由我们熟知的
Ilya Grigorik
提出,目前还处在较为早期的阶段,较为正式的描述文档可以在这里找到。目前 Chrome
46
(beta) 已支持它,IE
和 Firefox 则还在考虑中。

其实之前浏览器已经将很多自身特性放在 HTTP 请求中,例如下面这些头部字段:

  • User-Agent:提供浏览器类型及版本、操作系统及版本、浏览器内核等信息;
  • Accept:表明浏览器支持哪些 MIME type(例如 Chrome 通过 Accept
    表明自己支持 image/webp 图片格式);
  • Accept-Encoding:表明本浏览器支持哪些内容编码方式(例如:gzip、deflate、sdch);
  • Accept-Language:表明本浏览器支持那些语言;

通过以上这些头部字段,我们已经可以针对不同客户端输出不同内容。例如本博客对支持
Webp 格式的浏览器会使用 Webp 来减少图片大小;本博客还会通过 User-Agent
针对 IE 老版本禁用 localStorage 缓存策略。

但是有一些浏览器特性,我们无法直接获取,如屏幕分辨率、设备像素比(devicePixelRatio)、用户带宽等。而在移动
Web
中,为了尽可能节省用户流量,需要输出尺寸最合适的图片资源。为了解决这个问题,常见的方案有:1)使用
JS 获取这些特性,动态拼接图片 URL;2)使用 HTML 中的 sizes 和 srcset
属性、picture 标签或 CSS 中的 image-set 属性来实现响应式图片。方案 1
很简单,这里略过;方案 2
网上有很多相关文章,不熟悉的同学可以自行搜索「响应式图片」了解下。

这里看一个使用方案 2 中提到的 picture、sizes 和 srcset
实现的响应式图片代码(via):

<picture> <!– serve WebP to Chrome and Opera –> <source
media=”(min-width: 50em)” sizes=”50vw” srcset=”/image/thing-200.webp
200w, /image/thing-400.webp 400w, /image/thing-800.webp 800w,
/image/thing-1200.webp 1200w, /image/thing-1600.webp 1600w,
/image/thing-2000.webp 2000w” type=”image/webp”> <source
sizes=”(min-width: 30em) 100vw” srcset=”/image/thing-crop-200.webp 200w,
/image/thing-crop-400.webp 400w, /image/thing-crop-800.webp 800w,
/image/thing-crop-1200.webp 1200w, /image/thing-crop-1600.webp 1600w,
/image/thing-crop-2000.webp 2000w” type=”image/webp”> <!– serve
JPEGXR to Edge –> <source media=”(min-width: 50em)” sizes=”50vw”
srcset=”/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w,
/image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w,
/image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w”
type=”image/vnd.ms-photo”> <source sizes=”(min-width: 30em) 100vw”
srcset=”/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr
400w, /image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr
1200w, /image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr
2000w” type=”image/vnd.ms-photo”> <!– serve JPEG to others –>
<source media=”(min-width: 50em)” sizes=”50vw”
srcset=”/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
/image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
/image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w”> <source
sizes=”(min-width: 30em) 100vw” srcset=”/image/thing-crop-200.jpg 200w,
/image/thing-crop-400.jpg 400w, /image/thing-crop-800.jpg 800w,
/image/thing-crop-1200.jpg 1200w, /image/thing-crop-1600.jpg 1600w,
/image/thing-crop-2000.jpg 2000w”> <!– fallback for browsers that
don’t support picture –> <img src=”/image/thing.jpg”
width=”50%”> </picture>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<picture>
  <!– serve WebP to Chrome and Opera –>
  <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w,
        /image/thing-800.webp 800w, /image/thing-1200.webp 1200w,
        /image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w"
    type="image/webp">
  <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w,
        /image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w,
        /image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w"
    type="image/webp">
  <!– serve JPEGXR to Edge –>
  <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w,
        /image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w,
        /image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
  <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr 400w,
        /image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr 1200w,
        /image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
  <!– serve JPEG to others –>
  <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
        /image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
        /image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w">
  <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w,
        /image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w,
        /image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w">
  <!– fallback for browsers that don’t support picture –>
  <img src="/image/thing.jpg" width="50%">
</picture>

这段冗长的代码只是为了实现一张响应式图片,尽管有一些夸张,实际使用时一般不会写这么全,但从中可以得到一个结论:在客户端实现的策略越多,HTML
体积就越大越冗余,可维护性和可读性就越差。

而使用了 HTTP Client Hints
之后,浏览器在页面发起子资源请求时,会通过新增的一系列头部字段带上分辨率、设备像素比、图片宽度等信息,使得各种复杂的策略可以挪到服务端去实现了。下面来看一看具体细节:

首先,有了支持 HTTP Client Hints
的浏览器之后,页面上还需要显式启用它。这是因为不是所有服务端都实现了响应式输出策略,每次都发送这些新增的头部可能会造成浪费。

与往常一样,这个功能也可以通过 HTTP 响应头和 meta
标签两种方式开启并配置:

Accept-CH: DPR, Width, Viewport-Width

1
Accept-CH: DPR, Width, Viewport-Width

或:

<meta http-equiv=”Accept-CH” content=”DPR, Width, Viewport-Width”>

1
<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width">

在启用了 HTTP Client Hints
的页面中,所有子资源请求(无论什么类型,无论什么方式创建),都会携带
Accept-CH 属性中所指明的头部,例如:

Accept: image/webp,image/*,*/*;q=0.8 Accept-Encoding: gzip, deflate,
sdch Accept-Language:
zh-CN,zh;q=0.8,en;q=0.6,en-US;q=0.4,ja;q=0.2,de;q=0.2,zh-TW;q=0.2,cs;q=0.2,pt;q=0.2,ko;q=0.2
Connection: keep-alive DPR: 2 Host: qgy18.imququ.com User-Agent:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/46.0.2490.13 Safari/537.36 Viewport-Width:
1280 Width: 128

1
2
3
4
5
6
7
8
9
Accept: image/webp,image/*,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,en-US;q=0.4,ja;q=0.2,de;q=0.2,zh-TW;q=0.2,cs;q=0.2,pt;q=0.2,ko;q=0.2
Connection: keep-alive
DPR: 2
Host: qgy18.imququ.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.13 Safari/537.36
Viewport-Width: 1280
Width: 128

有了这些头部,图片服务器可以知道客户端的 devicePixelRatio 是
2、图片宽度是 128px、支持 Webp 格式,所以输出 256px 的双倍 Webp
图最合适。但是浏览器怎么知道这个图片需要作为双倍图来使用呢(也就是说还是显示为
128px)?这就需要在响应头中增加下面这个字段作为 DPR 的回应:

Content-DPR: 2

1
Content-DPR: 2

需要注意的是,请求头中的 Width 字段,是根据 img 标签上的 sizes
属性算出来的。如果图片没有指定 sizes,或者图片请求是通过 JS
创建的,浏览器无法得知 Width,也就不会携带这个头部。

实际上,除了 DPR、Viewport-Width 和 Width
之外,文档还规定了两个字段,但是经过我的测试 Chrome 46
并没有支持它们,这里简单介绍下:

  • Downlink:用来指示当前网络的下行链路带宽,单位是 Mbps;
  • Save-Data:用来指示当前浏览器是否工作在省流模式之下,取值为 1 或 0;

可以看出这两个属性,也是为了尽可能给用户节省带宽而设计的。可以预见,后续还会有更多字段加到
HTTP Client Hints 协议中来。随着 HTTP/2
的普及,头部压缩使得增加几个头部字段带来的开销变得很小了。

值得注意的是,使用了 HTTP Client Hints 之后,服务端针对同一个 URL
可能会输出不同的内容,所以无论是中间节点,还是浏览器,在实现响应 Cache
时必须小心,需要针对不同的情况缓存多份内容。这需要用到 HTTP/1 中的 
Vary 响应头,例如:

Vary: DPR, Width, Downlink

1
Vary: DPR, Width, Downlink

表明如果需要缓存这个响应,在生成缓存 Key 的时候需要将请求头中的
DPR、Width 和 Downlink 的值计算进去。

好了,HTTP Client Hints 技术就介绍到这里。很欣慰地看到,大部分 Web
新技术都是在给 HTML、CSS 和 JavaScript
增加功能和特性,而这项技术却是把之前复杂的代码和逻辑往后移,让我们的
HTML
代码能够轻装上阵。一些开源图片处理系统已经开始支持这个新特性了,国外的一些
CDN 托管服务肯定也在蠢蠢欲动,我十分期待它的未来。

1 赞 收藏
评论

图片 1

返本求源——DOM元素的特性与属性

2015/09/06 · HTML5,
JavaScript ·
DOM

原文出处: 木的树   

抛砖引玉

很多前端类库(比如dojo与JQuery)在涉及dom操作时都会见到两个模块:attr、prop。某天代码复查时,见到一段为某节点设置文本的代码:

JavaScript

attr.set(node, ‘innerText’, ‘Hello World!’)

1
attr.set(node, ‘innerText’, ‘Hello World!’)

这段代码执行后并未生效,虽说innerText不是标准属性,尚未被ff支持,可用的是chrome,这个属性是被支持的。既然显示的文本没变,那就查看一下元素吧。
图片 2

innerText被添加到了html标签上,而换成prop模块后,成功的为节点替换文本。

以上的这个小案例就涉及到了DOM操作时常常被忽略的一个问题:特性与属性的区别

返本求源

在DOM中,特性指的是html标签上的属性,比如:

图片 3

Property是对于某一类型特征的描述。可以这样理解,在DOM元素中可以通过点语法访问,又不是标准特性的都可以成为属性。

DOM中所有的节点都实现了Node接口。Node接口是在DOM1级中定义的,其中定义了一些用来描述DOM节点的属性和操作方法。

图片 4

常见的nodeType、nodeValue、节点关系(parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling等)都属于Node接口定义的属性。对于Node接口的具体实现者,HTMLElement不仅继承了这些属性,还拥有五个wac规范中的五个标准特性:id、title、lang、dir、class和一个属性:attributes。

每一个元素都有一个或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。通过DOM元素直接操作特性的的方法有三个:

  • getAttribute(attrName)
  • setAttribute(attrName, value)
  • removeAttribute(name)

这三个方法都可以操作自定义特性。但是只有公认的(非自定义)特性才会以属性的形式添加到DOM对象中,以属性方式操作这些特性会被同步到html标签中。HTMLElement的五个特性都有相应属性与其对待:id、title、lang、dir、className。在DOM中以属性方式操作这几个特性会同步到html标签中。

不过,HTML5规范对自定义特性做了增强,只要自定义特性以”data-attrName”的形式写入到html标签中,在DOM属性中就可以通过element.dataset.attrName的形式来访问自定义特性,如:

XHTML

<input type=​”text” name=​”as_q” class=​”box”
id=​”searched_content” title=​”在此输入搜索内容。” disabled=​”false”
data-ff=​”fsdf”>​ seh.dataset.ff

1
2
<input type=​"text" name=​"as_q" class=​"box" id=​"searched_content" title=​"在此输入搜索内容。" disabled=​"false" data-ff=​"fsdf">​
seh.dataset.ff

元素的特性在DOM中以Attr类型来表示,Attr类型也实现了Node接口。Attr对象有三个属性:name、value、specified。其中,name是特性的名称,value是特性值,specified是一个布尔值,用来指示该特性是否被明确设置。

document.createAttribute方法可以用来创建特性节点。例如,要为元素添加align特性可以使用如下方法:

JavaScript

ar attr = document.createAttribute(‘align’) attr.value = ‘left’
seh.setAttributeNode(attr)

1
2
3
ar attr = document.createAttribute(‘align’)
attr.value = ‘left’
seh.setAttributeNode(attr)

要将新创建的特性添加到元素上,必须使用元素的setAttributeNode方法。添加特性后,特性会反映在html标签上:

图片 5

注意,尽管特性节点也实现了Node接口,但特性却不被认为是DOM文档树的一部分。

在所有的DOM节点中attributes属性是Element类型所独有的的属性。从技术角度来说,特性就是存在于元素的attributes属性中的节点。attributes属性属于NamedNodeMap类型的实例。元素的每一个特性节点都保存在NamedNodeMap对象中。NamedNodeMap类型拥有如下方法:

  • getNamedItem(name):返回特性名为name的特性节点
  • removeNamedItem(name):删除特性名为name的特性节点
  • setNamedItem(attr):像元素中添加一个特性节点
  • item(pos):返回位于数组pos处的节点

获取、设置、删除元素节点可以如下方式:

JavaScript

element.attributes.getNamedItem(‘align’) //获取 var attr =
document.createAttribute(‘align’); attr.value = ‘right’;
element.attributes.setNamedItem(attr); //添加
element.attributes.removeNamedItem(‘align’); //删除

1
2
3
4
5
6
7
element.attributes.getNamedItem(‘align’) //获取
 
var attr = document.createAttribute(‘align’);
attr.value = ‘right’;
element.attributes.setNamedItem(attr); //添加
 
element.attributes.removeNamedItem(‘align’); //删除

实际应用中并不建议使用特性节点的方式,而getAttribute、setAttribute、removeAttribute方法远比操作特性节点更方便。

DOM、attributes、Attr三者关系应该这么画:

图片 6

应用总结

基于以上DOM基础知识和实际工作经验,我将特性和属性的区别联系总结如下:

  1. 属性以及公认特性可以通过点语法访问;html5规范中,data-*形式的自定义特性可以通过element.dataset.*的形式来访问,否则用getAttribute
  2. 特性值只能是字符串,而属性值可以是任意JavaScript支持的类型
  3. 几个特殊特性:
    1. style,通过getAttrbute和setAttribute来操作这个特性只能得到或设置字符串;而已属性方式来操作就是在操作CSSStyleDeclaration对象
    2. 事件处理程序,通过特性方式得到和传递的都只是函数字符串;而已属性方式操作的是函数对象
    3. value,对于支持value的元素,最好通过属性方式操作,而且操作不会反映在html标签上
    XHTML

    seh.value = 10 &lt;input type="text" name="as\_q" class="box"
    id="searched\_content" title="在此输入搜索内容。"
    disabled="false" data-ff="fsdf" align="left"&gt;

    <table>
    <colgroup>
    <col style="width: 50%" />
    <col style="width: 50%" />
    </colgroup>
    <tbody>
    <tr class="odd">
    <td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
    <div class="crayon-num" data-line="crayon-5b8f379c97d12396477911-1">
    1
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d12396477911-2">
    2
    </div>
    </div></td>
    <td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
    <div id="crayon-5b8f379c97d12396477911-1" class="crayon-line">
    seh.value = 10
    </div>
    <div id="crayon-5b8f379c97d12396477911-2" class="crayon-line crayon-striped-line">
    &lt;input type=&quot;text&quot; name=&quot;as_q&quot; class=&quot;box&quot; id=&quot;searched_content&quot; title=&quot;在此输入搜索内容。&quot; disabled=&quot;false&quot; data-ff=&quot;fsdf&quot; align=&quot;left&quot;&gt;
    </div>
    </div></td>
    </tr>
    </tbody>
    </table>

4.  href,通过属性方式设置可以反映到html标签上,但用过点语法和getAttribute能够取到的值并不一定相同


    XHTML

    &lt;a href="/jsref/prop\_checkbox\_tabindex.asp"
    id="tabI"&gt;tabIndex&lt;/a&gt; link.getAttribute('href') //
    "/jsref/prop\_checkbox\_tabindex.asp" link.href //
    "http://www.w3school.com.cn/jsref/prop\_checkbox\_tabindex.asp"

    <table>
    <colgroup>
    <col style="width: 50%" />
    <col style="width: 50%" />
    </colgroup>
    <tbody>
    <tr class="odd">
    <td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
    <div class="crayon-num" data-line="crayon-5b8f379c97d15903857159-1">
    1
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d15903857159-2">
    2
    </div>
    <div class="crayon-num" data-line="crayon-5b8f379c97d15903857159-3">
    3
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d15903857159-4">
    4
    </div>
    <div class="crayon-num" data-line="crayon-5b8f379c97d15903857159-5">
    5
    </div>
    </div></td>
    <td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
    <div id="crayon-5b8f379c97d15903857159-1" class="crayon-line">
    &lt;a href=&quot;/jsref/prop_checkbox_tabindex.asp&quot; id=&quot;tabI&quot;&gt;tabIndex&lt;/a&gt;
    </div>
    <div id="crayon-5b8f379c97d15903857159-2" class="crayon-line crayon-striped-line">
     
    </div>
    <div id="crayon-5b8f379c97d15903857159-3" class="crayon-line">
    link.getAttribute('href') // &quot;/jsref/prop_checkbox_tabindex.asp&quot;
    </div>
    <div id="crayon-5b8f379c97d15903857159-4" class="crayon-line crayon-striped-line">
     
    </div>
    <div id="crayon-5b8f379c97d15903857159-5" class="crayon-line">
    link.href // &quot;http://www.w3school.com.cn/jsref/prop_checkbox_tabindex.asp&quot;
    </div>
    </div></td>
    </tr>
    </tbody>
    </table>

5.  disabled和checked,对于支持这两个特性的元素来说,他们在html标签中都是无状态的,只要有独立的标签属性在以点语法访问时就返回true,如果html标签属性不存在,则以点语法访问时就是false


    XHTML

    &lt;input type=​"text" name=​"as\_q" class=​"box"
    id=​"searched\_content" title=​"在此输入搜索内容。"
    disabled=​"false" data-ff=​"fsdf" align=​"left"&gt;​
    seh.disabled // true seh.disabled = false &lt;input type=​"text"
    name=​"as\_q" class=​"box" id=​"searched\_content"
    title=​"在此输入搜索内容。" data-ff=​"fsdf" align=​"left"&gt;​

    <table>
    <colgroup>
    <col style="width: 50%" />
    <col style="width: 50%" />
    </colgroup>
    <tbody>
    <tr class="odd">
    <td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
    <div class="crayon-num" data-line="crayon-5b8f379c97d19172676562-1">
    1
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d19172676562-2">
    2
    </div>
    <div class="crayon-num" data-line="crayon-5b8f379c97d19172676562-3">
    3
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d19172676562-4">
    4
    </div>
    <div class="crayon-num" data-line="crayon-5b8f379c97d19172676562-5">
    5
    </div>
    <div class="crayon-num crayon-striped-num" data-line="crayon-5b8f379c97d19172676562-6">
    6
    </div>
    </div></td>
    <td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
    <div id="crayon-5b8f379c97d19172676562-1" class="crayon-line">
    &lt;input type=​&quot;text&quot; name=​&quot;as_q&quot; class=​&quot;box&quot; id=​&quot;searched_content&quot; title=​&quot;在此输入搜索内容。&quot; disabled=​&quot;false&quot; data-ff=​&quot;fsdf&quot; align=​&quot;left&quot;&gt;​
    </div>
    <div id="crayon-5b8f379c97d19172676562-2" class="crayon-line crayon-striped-line">
     
    </div>
    <div id="crayon-5b8f379c97d19172676562-3" class="crayon-line">
    seh.disabled // true
    </div>
    <div id="crayon-5b8f379c97d19172676562-4" class="crayon-line crayon-striped-line">
     
    </div>
    <div id="crayon-5b8f379c97d19172676562-5" class="crayon-line">
    seh.disabled = false
    </div>
    <div id="crayon-5b8f379c97d19172676562-6" class="crayon-line crayon-striped-line">
    &lt;input type=​&quot;text&quot; name=​&quot;as_q&quot; class=​&quot;box&quot; id=​&quot;searched_content&quot; title=​&quot;在此输入搜索内容。&quot; data-ff=​&quot;fsdf&quot; align=​&quot;left&quot;&gt;​
    </div>
    </div></td>
    </tr>
    </tbody>
    </table>

1 赞 1 收藏
评论

图片 1

刨根问底HTTP和WebSocket协议

2016/08/17 · 基础技术 ·
1 评论 ·
HTTP,
websocket

原文出处: TheAlchemist   

图片 8

那天和boss聊天,不经意间提到了Meteor,然后聊到了WebSocket,然后就有了以下对话,不得不说,看问题的方式不同,看到的东西也会大不相同。
A:Meteor是一个很新的开发框架,我觉得它设计得十分巧妙。
B:怎么个巧妙之处?
A:它的前后端全部使用JS,做到了真正的前后端统一;前端浏览器里存有一份后台开放出来的数据库的拷贝,快;使用WebSocket协议来做数据传输协议,来同步前后端的数据库,实现了真正的实时同步。
B:哦?WebSocket是什么东西?真实时?那底层是不是还是轮训?和HTTP的长连接有什么不同?
A:(开始心虚)它是一个新的基于TCP的应用层协议,只需要一次连接,以后的数据不需要重新建立连接,可以直接发送,它是基于TCP的,属于和HTTP相同的地位(呃,开始胡诌了),底层不是轮训,和长连接的区别……这个就不清楚了。
B:它的传输过程大致是什么样子的呢?
A:首先握手连接(又是胡诌),好像可以基于HTTP建立连接(之前用过Socket.io,即兴胡诌),建立了连接之后就可以传输数据了,还包括断掉之后重连等机制。
B:看起来和HTTP长连接做的事情差不多嘛,好像就是一种基于HTTP和Socket的协议啊。
A:呃……(我还是回去看看书吧)

有时候看事情确实太流于表面,了解到了每个事物的大致轮廓,但不求甚解,和朋友聊天说出来也鲜有人会刨根问底,导致了很多基础知识并不牢靠,于是回来大致把HTTP和WebSocket协议的RFC文档(RFC2616

RFC6455),刚好对HTTP的传输过程一直有点模糊,这里把两个协议的异同总结一下。

协议基础

仔细去看这两个协议,其实都非常简单,但任何一个事情想做到完美都会慢慢地变得异常复杂,各种细节。这里只会简单地描述两个协议的结构,并不会深入到很深的细节之处,对于理解http已经足够了。

HTTP

HTTP的地址格式如下:

JavaScript

http_URL = “http:” “//” host [ “:” port ] [ abs_path [ “?” query
]] 协议和host不分大小写

1
2
http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
协议和host不分大小写
HTTP消息

一个HTTP消息可能是request或者response消息,两种类型的消息都是由开始行(start-line),零个或多个header域,一个表示header域结束的空行(也就是,一个以CRLF为前缀的空行),一个可能为空的消息主体(message-body)。一个合格的HTTP客户端不应该在消息头或者尾添加多余的CRLF,服务端也会忽略这些字符。

header的值不包括任何前导或后续的LWS(线性空白),线性空白可能会出现在域值(filed-value)的第一个非空白字符之前或最后一个非空白字符之后。前导或后续的LWS可能会被移除而不会改变域值的语意。任何出现在filed-content之间的LWS可能会被一个SP(空格)代替。header域的顺序不重要,但建议把常用的header放在前边(协议里这么说的)。

Request消息

RFC2616中这样定义HTTP Request 消息:

JavaScript

Request = Request-Line *(( general-header |
request-header(跟本次请求相关的一些header) | entity-header )
CRLF)(跟本次请求相关的一些header) CRLF [ message-body ]

1
2
3
4
5
6
Request = Request-Line
          *(( general-header
            | request-header(跟本次请求相关的一些header)
            | entity-header ) CRLF)(跟本次请求相关的一些header)
          CRLF
          [ message-body ]

一个HTTP的request消息以一个请求行开始,从第二行开始是header,接下来是一个空行,表示header结束,最后是消息体。

请求行的定义如下:

JavaScript

//请求行的定义 Request-Line = Method SP Request-URL SP HTTP-Version CRLF
//方法的定义 Method = “OPTIONS” | “GET” | “HEAD” |”POST” |”PUT”
|”DELETE” |”TRACE” |”CONNECT” | extension-method //资源地址的定义
Request-URI =”*” | absoluteURI | abs_path | authotity(CONNECT)

1
2
3
4
5
6
7
8
//请求行的定义
Request-Line = Method SP Request-URL SP HTTP-Version CRLF
 
//方法的定义
Method = "OPTIONS" | "GET" | "HEAD"  |"POST" |"PUT" |"DELETE" |"TRACE" |"CONNECT"  | extension-method
 
//资源地址的定义
Request-URI   ="*" | absoluteURI | abs_path | authotity(CONNECT)

Request消息中使用的header可以是general-header或者request-header,request-header(后边会讲解)。其中有一个比较特殊的就是Host,Host会与reuqest
Uri一起来作为Request消息的接收者判断请求资源的条件,方法如下:

  1. 如果Request-URI是绝对地址(absoluteURI),这时请求里的主机存在于Request-URI里。任何出现在请求里Host头域值应当被忽略。
  2. 假如Request-URI不是绝对地址(absoluteURI),并且请求包括一个Host头域,则主机由该Host头域值决定。
  3. 假如由规则1或规则2定义的主机是一个无效的主机,则应当以一个400(错误请求)错误消息返回。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

标签:, ,
网站地图xml地图