首页 » 通讯 » 不为人知的收集编程(八):从数据传输层深度解密HTTP_报文_年夜众

不为人知的收集编程(八):从数据传输层深度解密HTTP_报文_年夜众

少女玫瑰心 2024-12-23 11:53:18 0

扫一扫用手机浏览

文章目录 [+]

为了对网络数据包的“流转”有更加深刻的理解,我在docker(远程)上支配一个做事,支持http办法调用。
从客户端(本地)用http办法要求个中的一个接口,并得到相应数据。
同时本地通过wireshark抓包,远程用tcpdump抓包,然后剖析过程中的所有通信细节(悲剧是把美好的东西撕碎给人看,而我则是把繁芜的东西撕碎了给人看)。

本文的紧张内容是:先通过工具获取HTTP通信的数据包,再来抽丝剥茧,深入传输层二进制的天地里,解密HTTP所有的通信细节。
剖析过程中,由点到面,将干系知识串接起来。
市情上讲HTTP协议的文章很多,但深入到传输层从2进制的角度来解析,则相称少见。
担保全篇读完之后,你对HTTP的理解会上升一个台阶!

不为人知的收集编程(八):从数据传输层深度解密HTTP_报文_年夜众 不为人知的收集编程(八):从数据传输层深度解密HTTP_报文_年夜众 通讯

本文稍长,请在看本文时保持耐心。

不为人知的收集编程(八):从数据传输层深度解密HTTP_报文_年夜众 不为人知的收集编程(八):从数据传输层深度解密HTTP_报文_年夜众 通讯
(图片来自网络侵删)

(本文同步发布于:http://www.52im.net/thread-2456-1-1.html)

2、关于作者

饶全成:毕业于华中科技大学,中科院打算所硕士,滴滴出行后端研发工程师。
微信"大众年夜众号: 码农桃花源,个人博客:https://www.cnblogs.com/qcrao-2018/。

3、系列文章

1)本文是系列文章中的第8篇,本系列文章的大纲如下:

《不为人知的网络编程(一):浅析TCP协议中的疑难杂症(上篇)》

《不为人知的网络编程(二):浅析TCP协议中的疑难杂症(下篇)》

《不为人知的网络编程(三):关闭TCP连接时为什么会TIME_WAIT、CLOSE_WAIT》

《不为人知的网络编程(四):深入研究剖析TCP的非常关闭》

《不为人知的网络编程(五):UDP的连接性和负载均衡》

《不为人知的网络编程(六):深入地理解UDP协议并用好它》

《不为人知的网络编程(七):如何让不可靠的UDP变的可靠?》

《不为人知的网络编程(八):从数据传输层深度解密HTTP》(本文)

2)如果您以为本系列文章过于专业,您可先阅读《网络编程

《网络编程

《网络编程

《网络编程

《网络编程

《网络编程

3)《脑残式网络编程入门》也适宜入门学习,本系列大纲如下:

《脑残式网络编程入门(一):随着动画来学TCP三次握手和四次挥手》

《脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?》

《脑残式网络编程入门(三):HTTP协议必知必会的一些知识》

《脑残式网络编程入门(四):快速理解HTTP/2的做事器推送(Server Push)》

4)其它跟HTTP有关的文章:

《从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演化和设计思路》

《美图App的移动端DNS优化实践:HTTPS要求耗时减小近半》

《一分钟理解 HTTPS 到底办理了什么问题》

《一篇读懂HTTPS:加密事理、安全逻辑、数字证书等》

《小白必读:闲话HTTP短连接中的Session和Token》

《IM开拓根本知识补课:精确理解前置HTTP SSO单点上岸接口的事理》

《从HTTP到MQTT:一个基于位置做事的APP数据通信实践概述》

《基于APNs最新HTTP/2接口实现iOS的高性能推送(做事端篇)》

《Comet技能详解:基于HTTP长连接的Web端实时通信技能》

《WebSocket详解(四):刨根问底HTTP与WebSocket的关系(上篇)》

《WebSocket详解(五):刨根问底HTTP与WebSocket的关系(下篇)》

4、在传输层捕获HTTP报文

4.1 背景先容

我手头现在有一个地理几何干系的做事,它供应一组接口对外利用。
个中有一个接口是Fence2Area. 利用方传入一个围栏(由点的列表组成,点由<经度,纬度>表示)、点的坐标系类型(谷歌舆图用的是wgs84, 海内腾讯、高德用的是soso, 而百度用的是另一套自己的坐标系),接口输出的则是围栏的面积。

我要求做事的“Fence2Area”接口,输入围栏(fence)顶点(lng, lat)坐标、坐标系类型(coordtype),输出的则是多边形的面积(area).

一次正常的要求示例url, 这个大家都不陌生(我用docker_ip代替真实的ip):

http://docker_ip:7080/data?cmd=Fence2Area&meta={\"大众caller\"大众:\公众test\"大众,\"大众TraceId\"大众:\"大众test\"大众}&request={\"大众fence\公众:[{\"大众lng\"大众:10.2,\"大众lat\"大众:10.2}, {\"大众lng\"大众:10.2,\公众lat\"大众:8.2}, {\"大众lng\公众:8.2,\"大众lat\公众:8.2}, {\公众lng\"大众:8.2,\公众lat\公众:10.2}],\公众coordtype\"大众:2}

要求发出后,做事器进行处理,之后,客户端收到返回的数据如下:

{

\公众data\公众: {

\"大众area\"大众: 48764135597.842606

},

\公众errstr\公众: \公众\"大众

}

area字段表示面积,errstr表示出错信息,空解释没有出错。

4.2 抓包

在真正发送要求之前,须要进行抓包前的设置。
在本地mac,我用wireshark; 而在远程docker上,我用tcpdump工具。

mac本地:设置wireshark包过滤器,监控本地主机和远程docker之间的通信。

ip.addr eq docker_ip

点击开始捕获。

远程docker:该做事通过7080端口对外供应,利用如下命令捕获网络包:

tcpdump -w /tmp/testHttp.cap port 7080 -s0

4.3 要求、捕获、剖析

准备事情做完,我选了一个神圣的时候,在本地通过浏览器访问如下url:

http://docker_ip:7080/data?cmd=Fence2Area&meta={\"大众caller\公众:\"大众test\公众,\"大众TraceId\公众:\"大众test\公众}&request={\公众fence\"大众:[{\"大众lng\公众:10.2,\公众lat\公众:10.2}, {\公众lng\"大众:10.2,\"大众lat\"大众:8.2}, {\"大众lng\"大众:8.2,\"大众lat\"大众:8.2}, {\"大众lng\公众:8.2,\公众lat\"大众:10.2}],\公众coordtype\"大众:2}

这样本地的wireshark和远程的tcpdump都能抓取到HTTP网络数据包。

【关闭做事进程】:

正式要求之前,我们先看一下几种分外的环境。

首先,关闭gcs做事进程,要求直接返回RST报文。

如上图,我在要求的时候,访问做事真个另一个端口5010, 这个端口没有做事监听,和关闭gcs做事进程是同样的效果。
可以看到,客户端发送SYN报文,但直接被远程docker RST掉了。
由于做事端操作系统找不到监听此端口的进程。

【关闭docker】:

关闭docker, 由于发送的SYN报文段得不到相应,因此会进行重试,mac下重试的次数为10次。

先每隔1秒重试了5次,再用“指数退避”的韶光间隔重试,2s, 4s, 8s, 16s, 32s. 末了结束。

【重启docker】:

前辈行一次正常的访问,随后重启docker。
并再次在本地访问以上url, 浏览器这时还是用的上一次的端口,访问到做事端后,由于它已经重启了,以是做事端已经没有这个连接的了。
因此会返回一个RST报文。

【正常要求】:

做事正常启动,正常发送要求,这次要求成功,那是当然的,嘿嘿!

这是在mac上用wireshark捕获的数据包,共7个包,前三个包为3次握手的包,第四个包为HTTP层发送的要求数据,第五个包为做事真个TCP 确认报文,第六个包为做事端在HTTP层发送的相应数据,第七个包为mac对第六个包的确认报文。

重点来关注后面几个包,先看第四个包:

0x0000: 4500 0295 0000 4000 3606 623b ac17 ccdc

0x0010: 0a60 5cd4 db9b 1ba8 a59a 46ce 6d03 e87d

0x0020: 8018 1015 0ee7 0000 0101 080a 2e4c b2ef

0x0030: 0f20 3acf 4745 5420 2f64 6174 613f 636d

0x0040: 643d 4665 6e63 6532 4172 6561 266d 6574

0x0050: 613d 7b25 3232 6361 6c6c 6572 2532 323a

0x0060: 2532 3274 6573 7425 3232 2c25 3232 5472

0x0070: 6163 6549 6425 3232 3a25 3232 7465 7374

0x0080: 2532 327d 2672 6571 7565 7374 3d7b 2532

0x0090: 3266 656e 6365 2532 323a 5b7b 2532 326c

0x00a0: 6e67 2532 323a 3130 2e32 2c25 3232 6c61

0x00b0: 7425 3232 3a31 302e 327d 2c25 3230 7b25

0x00c0: 3232 6c6e 6725 3232 3a31 302e 322c 2532

0x00d0: 326c 6174 2532 323a 382e 327d 2c25 3230

0x00e0: 7b25 3232 6c6e 6725 3232 3a38 2e32 2c25

0x00f0: 3232 6c61 7425 3232 3a38 2e32 7d2c 2532

0x0100: 307b 2532 326c 6e67 2532 323a 382e 322c

0x0110: 2532 326c 6174 2532 323a 3130 2e32 7d5d

0x0120: 2c25 3232 636f 6f72 6474 7970 6525 3232

0x0130: 3a32 7d20 4854 5450 2f31 2e31 0d0a 486f

0x0140: 7374 3a20 3130 2e39 362e 3932 2e32 3132

0x0150: 3a37 3038 300d 0a55 7067 7261 6465 2d49

0x0160: 6e73 6563 7572 652d 5265 7175 6573 7473

0x0170: 3a20 310d 0a41 6363 6570 743a 2074 6578

0x0180: 742f 6874 6d6c 2c61 7070 6c69 6361 7469

0x0190: 6f6e 2f78 6874 6d6c 2b78 6d6c 2c61 7070

0x01a0: 6c69 6361 7469 6f6e 2f78 6d6c 3b71 3d30

0x01b0: 2e39 2c2a 2f2a 3b71 3d30 2e38 0d0a 5573

0x01c0: 6572 2d41 6765 6e74 3a20 4d6f 7a69 6c6c

0x01d0: 612f 352e 3020 284d 6163 696e 746f 7368

0x01e0: 3b20 496e 7465 6c20 4d61 6320 4f53 2058

0x01f0: 2031 305f 3133 5f36 2920 4170 706c 6557

0x0200: 6562 4b69 742f 3630 352e 312e 3135 2028

0x0210: 4b48 544d 4c2c 206c 696b 6520 4765 636b

0x0220: 6f29 2056 6572 7369 6f6e 2f31 322e 302e

0x0230: 3220 5361 6661 7269 2f36 3035 2e31 2e31

0x0240: 350d 0a41 6363 6570 742d 4c61 6e67 7561

0x0250: 6765 3a20 7a68 2d63 6e0d 0a41 6363 6570

0x0260: 742d 456e 636f 6469 6e67 3a20 677a 6970

0x0270: 2c20 6465 666c 6174 650d 0a43 6f6e 6e65

0x0280: 6374 696f 6e3a 206b 6565 702d 616c 6976

0x0290: 650d 0a0d 0a

我们来逐字节剖析:

剩余的数据部分即为TCP协议干系的。

TCP也是20B固定长度+可变长度部分:

可变长度部分,协议如下:

剩下来的便是数据部分了。
我们一行一行地看。

由于http是字符流,以是我们先看一下ascii字符集,实行命令:

man ascii

可以得到ascii码,我们直接看十六进制的结果:

把上表的末了一列连起来,便是:

GET /data?cmd=Fence2Area&meta={%22caller%22:%22test%22,%22TraceId%22:%22test%22}&request={%22fence%22:[{%22lng%22:10.2,%22lat%22:10.2},%20{%22lng%22:10.2,%22lat%22:8.2},%20{%22lng%22:8.2,%22lat%22:8.2},%20{%22lng%22:8.2,%22lat%22:10.2}],%22coordtype%22:2} HTTP/1.1

Host: 10.96.92.212:7080

Upgrade-Insecure-Requests: 1

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15

Accept-Language: zh-cn

Accept-Encoding: gzip, deflate

Connection: keep-alive

个中,cr nl表示回车,换行。

docker收到数据后,会回答一个ack包。
第四个包的总长度为661字节,去掉IP头部20字节,TCP头部固定部分20字节,TCP头部可选长度为12字节,共52字节,因此TCP数据部分总长度为661-52=609字节。
其余,序列号为2778351310.

再来看第5个包,字节流如下:

0x0000: 4500 0034 d28b 4000 4006 8810 0a60 5cd4

0x0010: ac17 ccdc 1ba8 db9b 6d03 e87d a59a 492f

0x0020: 8010 00ec e04e 0000 0101 080a 0f20 3af7

0x0030: 2e4c b2ef

剩余的数据部分即为TCP协议干系的。

TCP也是20B固定长度+可变长度部分:

可变长度部分,协议如下:

数据部分为空,这个包仅为确认包。

再来看第六个包,字节流如下:

0x0000: 4500 00f9 d28c 4000 4006 874a 0a60 5cd4

0x0010: ac17 ccdc 1ba8 db9b 6d03 e87d a59a 492f

0x0020: 8018 00ec e113 0000 0101 080a 0f20 3af8

0x0030: 2e4c b2ef 4854 5450 2f31 2e31 2032 3030

0x0040: 204f 4b0d 0a41 6363 6573 732d 436f 6e74

0x0050: 726f 6c2d 416c 6c6f 772d 4f72 6967 696e

0x0060: 3a20 2a0d 0a44 6174 653a 2054 6875 2c20

0x0070: 3033 204a 616e 2032 3031 3920 3132 3a32

0x0080: 333a 3437 2047 4d54 0d0a 436f 6e74 656e

0x0090: 742d 4c65 6e67 7468 3a20 3438 0d0a 436f

0x00a0: 6e74 656e 742d 5479 7065 3a20 7465 7874

0x00b0: 2f70 6c61 696e 3b20 6368 6172 7365 743d

0x00c0: 7574 662d 380d 0a0d 0a7b 2264 6174 6122

0x00d0: 3a7b 2261 7265 6122 3a34 3837 3634 3133

0x00e0: 3535 3937 2e38 3432 3630 367d 2c22 6572

0x00f0: 7273 7472 223a 2222 7d

剩余的数据部分即为TCP协议干系的。
TCP也是20B固定长度+可变长度部分:

可变长度部分,协议如下:

剩下来的便是数据部分了。
我们一行一行地看:

把上表的末了一列连起来,便是:

HTTP/1.1 200 OK

Access-Control-Allow-Origin:

Date: Thu, 03 Jan 2019 12:23:47 GMT

Content-Length: 48

Content-Type: text/plain; charset=utf-8

{\"大众data\公众:{\"大众area\"大众:48764135597.842606},\公众errstr\"大众:\公众\公众}

Content-Length: 48,末了一行的长度即为48个字节。

末了,第七个包,字节流如下:

0x0000: 4500 0034 0000 4000 3606 649c ac17 ccdc

0x0010: 0a60 5cd4 db9b 1ba8 a59a 492f 6d03 e942

0x0020: 8010 100f 1eb9 0000 0101 080a 2e4c b314

0x0030: 0f20 3af8

剩余的数据部分即为TCP协议干系的。
TCP也是20B固定长度+可变长度部分:

可变长度部分,协议如下:

至此,一次完全的http要求的报文就解析完了。
觉得如何,是不是很亲切?(PS: WTF?看的人都抓狂了,还亲切?哈哈)

5、在运用层学习HTTP协议

上面我们把HTTP协议干系的数据从2进制层给解密了,下面我将对照上面的数据拆解结果,一步步带你从运用层深入认识HTTP协议。

5.1 整体先容

HTTP(Hypertext Transfer Protocol)超文本传输协议,是在互联网上进行通信时利用的一种协议。
说得更形象一点:HTTP是当代互联网中利用的公共措辞。
它最著名的运用是用在浏览器的做事器间的通信。

HTTP属于运用层协议,底层是靠TCP进行可靠地信息传输。

HTTP在传输一段报文时,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。
TCP接到上层运用交给它的数据流之后,会按序将数据流打散成一个个的分段。
再交到IP层,通过网络进行传输。
另一真个吸收方则相反,它们将吸收到的分段按序组装好,交给上层HTTP协议进行处理。

5.2 编码

我们再来回顾一下:

在之前的报文拆解过程中,我们看到多了很多%22,实在,0x22是单引号\"大众的ascii值。

一方面,URL描述的资源为了能通过其他各种协议传送,但是有些协议在传输过程中会剥去一些特定的字符;

另一方面,URL还是可读的,以是那些不可打印的字符就不能在URL中利用了,比如空格;

末了,URL还得是完全的,它须要支持所有措辞的字符。

总之,基于很多缘故原由,URL设计者将US-ASCII码和其转义序列集成到URL中,通过转义序列,就可以用US-ASCII字符集的有限子集对任意字符或数据进行编码了。

转义的方法:百分号(%)后随着两个表示ASCII码的十六进制数。
比如:

以是上面在浏览器发送给做事器的URL进行了非“安全字符”编码,也就不奇怪了吧?

在URL中,当上面的保留字符用在保留用场之外的场合时,须要对URL进行编码。

5.3 MIME类型

相应数据中,我们把稳到有一个首部:

Content-Type: text/plain; charset=utf-8

互联网上有数千种不同的数据类型,HTTP给每种工具都打上了MIME(Multipurpose Internet Media Extension, 多用场因特网邮件扩展)标签,也便是相应数据中的Content-Type. MIME本来是用在邮件协议中的,后来被移植到了HTTP中。
浏览器从做事器上取回了一个工具时,会去查看MIME类型,从而得知如何处理这种工具,是该展示图片,还是调用声卡播放声音。

MIME通过斜杠来标识工具的主类型和个中的特定的子类型,下表展示了一些常见的类型,个中的实体主体是指body部分:

5.4 URI/URL/URN

URI(Uniform Resource Identifier, 统一资源标识符)表示做事器资源,URL(Uniform Resource Locator, 统一资源定位符)和URN(Uniform Resource Name, 统一资源名)是URI的详细实现。
URI是一个通用的观点,由两个紧张的子集URL和URN构成,URL通过位置、URN通过名字来标识资源。

URL定义了资源的位置,表示资源的实际地址,在利用URL的过程中,如果URL背后的资源发生了位置移动,访问者就找不到它了。
这个时候就要用到URN了,它给定资源一个名字,无论它移动到哪里,都可以通过这个名字来访问到它,切实其实完美!

URL常日的格式是:

协议方案+做事器地址+详细的资源路径

协议方案(scheme),如 http, ftp,奉告web客户端若何访问资源);做事器地址,如 www.oreilly.com; 详细的资源路径,如 index.html.

5.5 HTTP方法

HTTP支持几种不同的要求方法,每种方法对做事器哀求的动作不同,如下图是几种常见的方法:

HEAD方法只获取头部,不获取数据部分。
通过头部可以获取比如资源的类型(Content-Type)、资源的长度(Content-Length)这些信息。
这样,客户端可以获取即将要求资源的一些情形,可以做到心中有数。

1)POST用于向做事器发送数据,常见的是提交表单;

2)PUT用于向做事器上的资源存储数据。

5.6 状态码

每条HTTP的相应报文都会带上一个三位数字的状态码和一条阐明性的“缘故原由短语”,关照客户端本次要求的状态,帮助客户端快速理解事务处理结果,最常见的是:

200 OK

404 Not Found

500 Internal Server Error

我们平时利用浏览器的时候,很多的缺点码实在是由浏览器处理的,我们感知不到。
但是404 Not Found会穿透重重迷雾,来到我们面前,为何?那是由于他对我们爱的深奥深厚啊!

客户端可以据此状态码,决定下一步的行动(如重定向等)。

三位数字的第一位表示分类:

5.7 报文格式

HTTP报文实际上是由一行行的字符串组成的,每行字符串的末端用\r\n分隔,人类可以很方便的阅读。
顺便说一句,不是所有的协议都对人类这么友好的,像thrift协议,直接甩一堆字节给你,见告你说0x0001表示调用方法,诸如此类的,你只能对着一个十六进制的数据块一个个地去“解码”。
不可能像HTTP协议这样,直接将字符编码,人类可以直接读懂。

举个大略的要求报文和相应报文的格式的例子:

实际上,要求报文也是可以有body(主体)部分的。
要求报文是由要求行(request line)、要求头部(header)、空行、要求数据四个部分组成。
唯一要把稳的一点便是,要求报文纵然body部分是空的,要求头部后的回车换行符也是必须要有的。

相应报文的格式和要求报文的格式类似:

要求报文、相应报文的起始行和相应头部里的字段都是文本化、构造化的。
而要求body却可以包含任意二进制数据(如图片、视频、软件等),当然也可以包含文本。

有些首部是通用的,有些则是要求或者相应报文才会有的。

顺便提一下, 用telnet直连做事器的http端口,telnet命令会建立一条TCP通道,然后就可以通过这个通道直接发送HTTP要求数据,获取相应数据了。

6、HTTP协议进阶

6.1 代理

HTTP的代理做事器既是Web做事器,又是Web客户端。

利用代理可以“打仗”到所有流过的HTTP流量,代理可以对其进行监视和修正。
常见的便是对儿童过滤一些“成人”内容;网络工程师会利用代理做事器来提高安全性,它可以限定哪些运用层的协议数据可以通过,过滤“病毒”等数据;代理可以存储缓存的文件,直接返回给访问者,无必要求原始的做事器资源;对付访问慢速网络上的公共内容时,可以假扮做事器供应做事,从而提高访问速率;这被称为反向代理;可以作为内容路由器,如对付用度户,则将要求导到缓存做事器,提高访问速率;可以将页面的措辞转换到与客户审察匹配,这称为内容转码器; 匿名代理会主动从HTTP报文中删除身份干系的信息,如User-Agent, Cookie等字段。

现实中,要求通过以下几种办法打到代理做事器上去:

报文每经由一个中间点(代理或网关),都须要在首部via字段的末端插入一个可以代表本节点的独特的字符串,包含实现的协议版本和主机地址。
把稳下图中的via字段。

要乞降相应的报文传输路径常日都是同等的,只不过方向是相反的。
因此,相应报文上的via字段表示的中间节点的顺序是刚好相反的。

6.2 缓存

当有很多要求访问同一个页面时,做事器会多次传输同一份数据,这些数据重复地在网络中传输着,花费着大量带宽。
如果将这些数据缓存下来,就可以提高相应速率,节省网络带宽了。

大部分缓存只有在客户端发起要求,并且副本已经比较旧的情形下才会对副本的新鲜度进行检测。
最常用的要求首部是If-Modified-Since, 如果在xx韶光(此韶光即为If-Modified-Since的值)之后内容没有变革,做事器会回应一个304 Not Modified. 否则,做事器会正常相应,并返回原始的文件数据,而这个过程中被称为再验证命中。

再验证可能涌现命中或未命中的情形:

1)未命中时,做事器回答200 OK,并且返回完全的数据;

2)命中时,做事器回答304 Not Modified。

还有一种情形,缓存被删除了,那么根据相应状态码,缓存做事器也会删除自己缓存的副本。

顺带提一句,若要在项目中利用缓存,就一定要关注缓存命中比例。
若命中比例不高,就要重新考虑设置缓存的必要性了。

缓存做事器返回相应的时候,是基于已缓存的做事器相应的首部,再对一些首部字段做一些微调。
比如向个中插入新鲜度信息(如Age, Expires首部等),而且常日会包含一个via首部来解释缓存是由一个缓存代理供应的。
把稳,这时不要修正Date字段,它表示原始做事器最初构建这条相应的日期。

HTTP通过文档过期机制和做事器再验证机制保持已缓存数据和做事器间的数据充分同等。

文档过期通过如下首部字段来表示缓存的有效期:

当上面两个字段暗示的过期韶光已到,须要向做事器再次验证文档的新鲜度。
如果这时缓存仍和做事器上的原始文档同等,缓存只须要更新头部的干系字段。
如上表中提到的Expires字段等。

为了更好的节省网络流量,缓存做事器可以通过干系首部向原始做事器发送一个条件GET要求, 这样只有在缓存真正过期的情形下,才会返回原始的文档,否则只会返回干系的首部。

条件GET要求会用到如下的字段:

6.3 cookie

cookie是做事器“贴在”客户端身上的标签,由客户端掩护的状态片段,并且只会回送给得当的站点。

有两类cookie:

1)会话cookie、持久cookie. 会话cookie在退出浏览器后就被删除了;

2)而持久cookie则保存在硬盘中,打算机重启后仍旧存在。

做事器在给客户真个相应字段首部加上Set-cookie或Set-cookie2, 值为名字=值的列表,即可以包含多个字段。
当下次浏览器再次访问到相同的网站时,会将这些字段通过Cookie带上。
cookie中保留的内容是做事器给此客户端打的标签,方便做事进行追踪的识别码。
浏览器会将cookie以特定的格式存储在特定的文件中。

浏览器只会向产生这条cookie的站点发生cookie. Set-cookie字段的值会包含domain这个字段,奉告浏览器可以把这条cookie发送给给干系的匹配的站点。
path字段也是相似的功能。

如i浏览器收到如下的cookie:

Set-cookie: user=\"大众mary\"大众; domain=\"大众stefno.com\"大众

那么浏览器在访问任意以stefno.com结尾的站点都会发送:

Cookie: user=\"大众mary\公众

6.4 实体和编码

相应报文中的body部分传输的数据实质上都是二进制。
我们从上面的报文数据也可以看出来,都是用十六进制数来表示,关键是怎么阐明这块内容。

如果Content-Type定义是text/plain, 那解释body内容便是文本,我们直接按文本编码来阐明;如果Content-Type定义是image/png, 解释body部分是一幅图片,那我们就按图片的格式去阐明数据。

Content-Length标示报文主体部分的数据长度大小,如果内容是压缩的,那它表示的便是压缩后的大小。
其余,Content-Length在长连接的情形下,可以对多个报文进行精确地分段。
以是,如果没有采取分块编码,相应数据中必须带上Content-Length字段。
分块编码的环境中,数据被拆分成很多小块,每块都有大小解释。
因此,任何带有主体部分的报文(要求或是相应)都应带上精确的Content-Length首部。

HTTP的早期版本采取关闭连接的办法来划定报文的结束。
这带来的问题是显而易见的:客户端并不能分清是由于做事器正常结束还是中途崩溃了。
这里,如果是客户端用关闭来表示要求报文主体部分的结束,是不可取的,由于关闭之后,就无法获取做事器的相应了。
当然,客户端可以采取半关闭的办法,只关闭数据发送方向,但是很多做事器是不识别的,会把半关闭当成客户端要成做事器断开来处理。

HTTP报文在传输的过程中可能会遭到代理或是其他通信实体的无意修正,为了让吸收方知道这种情形,做事器会对body部分作一个md5, 并把值放到Content-MD5这个字段中。
但是,如果中间的代理即修正了报文主体,又修正了md5, 就不好检测了。
因此规定代理是不能修正Content-MD5首部的。
这样,客户端在收到数据后,前辈行解码,再算出md5, 并与Content-MD5首部进行比较。
这紧张是防止代理对报文进行了无意的改动。

HTTP在发送内容之前须要对其进行编码,它是对报文主体进行的可逆变换。
比如将报文用gzip格式进行压缩,减少传输韶光。

常见的编码类型如下:

当然,客户端为了避免做事器返回自己不能解码的数据,要求的时候,会在Accept-Encoding首部里带上自己支持的编码办法。
如果不传输的话,默认可以接管任何编码办法。

上面提到的编码是内容编码,它只是在相应报文的主体报文将原始数据进行编码,改变的是内容的格式。
还有另一种编码:传输编码。
它与内容无关,它是为了改变报文数据在网络上传输的办法。
传输编码是在HTTP 1.1中引入的一个新特性。

常日,做事器须要师长西席成数据,再进行传输,这时,可以打算数据的长度,并将其编码到Content-Length中。
但是,有时,内容是动态天生的,做事器希望在数据天生之前就开始传输,这时,是没有办法知道数据大小的。
这种情形下,就要用到传输编码来标注数据的结束的。

HTTP协议中通过如下两个首部来描述和掌握传输编码:

分块编码的报文形式是这样的:

每个分块包含一个长度值(十六进制,字节数)和该分块的数据。
用于区隔长度值和数据。
长度值不包含分块中的任何序列。
末了一个分块,用长度值0来表示结束。
把稳报文首部包含一个Trailer: Content-MD5, 以是在紧随着末了一个报文结束之后,便是一个拖挂。
其他如,Content-Length, Trailer, Transfer-Encoding也可以作为拖挂。

内容编码和传输编码是可以结合起来利用的。

6.5 国际化支持

HTTP为了支持国际化的内容,客户端要奉告做事器自己能理解的何种措辞,以及浏览器上安装了何种字母表编码算法。
这通过Accept-Charset和Accept-Language首部实现。

比如:

Accept-Language: fr, en;q=0.8

Accept-Charset: iso-8859-1, utf-8

表示:客户端接管法语(fr, 优先级默认为1.0)、英语(en, 优先级为0.8),支持iso-8859-1, utf-8两种字符集编码。
做事器则会在Content-Type首部里放上charset.

实质上,HTTP报文的body部分存放的便是一串二进制码,我们先把二进制码转换成字符代码(如ascii是一个字节表示一个字符,而utf-8则表示一个字符的字节数不定,每个字符1~6个字节),之后,用字符代码去字符集中找到对应的元素。

比较常见的字符集是US-ASCII: 这个字符集是所有字符集的开山祖师,早在1968年就发布了标准。
ASCII码的代码值从0到127, 只须要7个bit位就可以覆盖代码空间。
HTTP报文的首部、URL利用的字符集便是ASCII码。
可以再看下上文报文剖析部分的acsii码集。

US-ASCII是把每个字符编码成固定的7位二进制值。
UTF-8则是无固定的编码方案。
第一个字节的高位用来表示编码后的字符所用的字节数(如果所用的字节数是5,则第一个字节前5bit都是1,第6bit是0),所需的后续的字节都含有6位的代码值,前两个bit位是用10标识。

举个例子,汉字“严”的Unicode编码为4E25(100111000100101), 共有15位,落在上表中的第三行,因此“严”的编码就须要三个字节。
将100111000100101填入上表中的c位即可。
因此,严的UTF-8编码是11100100 10111000 10100101,转换成十六进制便是E4B8A5. 比如我在谷歌搜索框里搜索“严”字,google发出的要求如下:

https://www.google.com.hk/search?q=%E4%B8%A5&oq=%E4%B8%A5&aqs=chrome..69i57j0l5.3802j0j4&sourceid=chrome&ie=UTF-8&gws_rd=cr

q=%E4%B8%A5 这个便是搜索的词了。

6.6 重定向与负载均衡

Web内容常日分散地分布在很多地方,这可以防止“单点故障”,万一某个地方发生地震了,机房被毁了,那还有其他地方的机房可以供应做事。
一样平常都会有所谓的“双活”,“多活”,所谓狡兔三窟嘛。

这样,用户的要求会根据负载均衡的原则,被重定向到它该当去的地方。

HTTP重定向:

做事器收到客户端要求后,向客户端返回一条带有状态码302重定向的报文,见告他们该当去其他的地方试试。
web站点将重定向算作一种大略的负载均衡策略来利用,重定向做事器找到可用的负载最小的机器,由于做事器知道客户真个地址,理论上来说,可以做到最优的重定向选择。

当然,缺陷也是显而易见的,由于客户端要发送两次要求,因此会增加耗时。

DNS重定向:

DNS将几个IP地址关联到一个域上,采取算法决定返回的IP地址。
可以是大略的轮转;也可以是更高等的算法,如返回负载最轻的做事器的IP地址,称为负载均衡算法;如果考虑地理位置,返回给客户端最近位置的地址,称为毗邻路由算法;还有一种是绕过涌现故障的地址,称为故障屏蔽算法。

DNS做事器总是会返回所有的IP地址,但是DNS客户端一样平常只会利用第一个IP地址,而且会缓存下来,之后会一贯用这个地址。
以是,DNS轮转常日不会平衡单个客户真个负载。
但是,由于DNS做事器对付不同的要求,总是会返回轮转后的IP地址列表,因此,会把负载分散到多个客户端。

6.7 HTTP连接

HTTP连接是HTTP报文传输的关键通道。

【并行连接】:

对付一个页面上同时涌现多个工具的时候,如果浏览器并行地打开多个连接,同时去获取这些工具,多个连接的TCP握手时延可以进行重叠,速率会快起来。

如一个包含3张图片的页面,浏览器要发送4次HTTP要求来获取页面。
1个用于顶层的HTML页面,3个用于图片。

如果采取串行办法,那么连接时延会进行叠加:

采取并行连接之后:

但是并行连接也不绝对提升速率,如果一个页面有数百个内嵌工具,那要启动数百个连接,对做事器的性能也是非常大的寻衅。
以是,常日浏览器会限定并行连接的总数据在一个较小的值,常日是4个,而且做事端可以随意关闭客户端超量的连接。

另一方面,如果客户端网络带宽较小,每个连接都会去争抢有限的带宽,每个连接都会获取较小的速率,即每个工具都会以较小的速率去加载。
这样,并行连接带来的速率提升就会比较小,乃至没有提升。

【持久连接】:

持久连接即HTTP的keep-alive机制。

我们知道HTTP要求是“要求-应答”模式,每次要求-应答都要新建一个连接,完成之后要断开连接。
HTTP是无状态的,连接之间没有任何关系。

HTTP是运用层协议,TCP是传输层协议。
HTTP底层仍旧采取TCP进行传输数据。
TCP为HTTP供应了一层可靠的比特传输通道。
HTTP一样平常交流的数据都不大,而每次连接都要进行TCP三次握手,很大一部分韶光都花费在这上面,有时候乃至能达到50%。
如果能复用连接,就可以减少由于TCP三次握手所带来的时延。

HTTP 1.1默认开启keep-alive机制,从上面抓到的包也可以看到。
这样,数据传输完成之后保持TCP连接不断开,之后同域名下复用连接,连续用这个通道传输数据。
做事器在相应一个要求后,可以保持这个连接keep-alive timeout的韶光,在这个韶光内没有要求,则关闭此连接;否则,重新开始倒计时keep-alive timeout韶光。

HTTP有keep-alive机制,目的是可以在一个TCP连接上传输多个HTTP事务,以此提高通信效率。
底层的TCP实在也有keep-alive机制,它是为了探测TCP连接的生动性。
TCP层的keepalive可以在任何一方设置,可以是一端设置、两端同时设置或者两端都没有设置。
新建socket的时候须要设置,从而使得协议栈调用干系函数tcp_set_keepalive,来激活连接的keep-alive属性。

当网络两端建立了TCP连接之后,闲置(双方没有任何数据流发送往来)韶光超过tcp_keepalive_time后,做事器内核就会考试测验向客户端发送侦测包,来判断TCP连接状况(有可能客户端崩溃、逼迫关闭了运用、主机不可达等等)。
如果没有收到对方的回答(ack包),则会在 tcp_keepalive_intvl后再次考试测验发送侦测包,直到收到对方的ack,如果一贯没有收到对方的ack,一共会考试测验 tcp_keepalive_probes次,每次的间隔韶光在这里分别是15s, 30s, 45s, 60s, 75s。
如果考试测验tcp_keepalive_probes次后,依然没有收到对方的ack包,则会丢弃该TCP连接。
TCP连接默认闲置韶光是2小时,一样平常设置为30分钟足够了。

【管道化连接】:

在keep-alive的根本上,我们可以做地更进一步,在相应到达之前,我们将多条要求按序放入要求行列步队,做事端在收到要求后,必须按照顺序对应要求的相应。
但由于网络环境非常繁芜,因此纵然要求是按顺序发送的,也不一定是按顺序到达做事真个。
而且就算是做事端按序处理的,也不一定是按序返回给客户端,以是最好是在相应中附带一些可以标识要求的参数。

为了安全起见,管道化的连接只适宜“幂等”的要求,一样平常我们认为:GET/HEAD/PUT/DELETE/TRACE/OPTIONS等方法都是幂等的。

7、本文小结

以上,便是所有HTTP的通信细节了,足够在日常开拓 作中利用了。
更多没有涉及的细节可以在用到的时候再去仔细研究。

文章看完了,不知道你对HTTP的理解有没有更上一层楼?欢迎一起互换磋商。

8、参考资料

[1]【http长连接】https://www.cnblogs.com/cswuyg/p/3653263.html

[2]【http/tcp keep alive】https://segmentfault.com/a/1190000012894416

[3]【http/tcp keep alive】http://www.nowamagic.net/academy/detail/23350305

[4]【http/tcp keep alive】https://laravel-china.org/articl ... n-the-http-protocol

[5]【tcp keep alive】http://blog.51cto.com/zxtong/1788252

[6]【http威信指南】https://book.douban.com/subject/10746113/

[7]【HTTP状态码】https://www.cnblogs.com/starof/p/5035119.html

[8]【HTTP协议】https://www.cnblogs.com/ranyonsue/p/5984001.html

[9]【HTTP状态分类】http://www.runoob.com/http/http-status-codes.html

[10]【url编码】http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

附录:更多网络编程文章

《TCP/IP详解 - 第11章·UDP:用户数据报协议》

《TCP/IP详解 - 第17章·TCP:传输掌握协议》

《TCP/IP详解 - 第18章·TCP连接的建立与终止》

《TCP/IP详解 - 第21章·TCP的超时与重传》

《技能往事:改变天下的TCP/IP协议(宝贵多图、手机慎点)》

《普通易懂-深入理解TCP协议(上):理论根本》

《普通易懂-深入理解TCP协议(下):RTT、滑动窗口、拥塞处理》

《理论经典:TCP协议的3次握手与4次挥手过程详解》

《理论联系实际:Wireshark抓包剖析TCP 3次握手、4次挥手过程》

《打算机网络通讯协议关系图(中文珍藏版)》

《UDP中一个包的大小最大能多大?》

《P2P技能详解(一):NAT详解——详细事理、P2P简介》

《P2P技能详解(二):P2P中的NAT穿越(打洞)方案详解》

《P2P技能详解(三):P2P技能之STUN、TURN、ICE详解》

《普通易懂:快速理解P2P技能中的NAT穿透事理》

《高性能网络编程(一):单台做事器并发TCP连接数到底可以有多少》

《高性能网络编程(二):上一个10年,著名的C10K并发连接问题》

《高性能网络编程(三):下一个10年,是时候考虑C10M并发问题了》

《高性能网络编程(四):从C10K到C10M高性能网络运用的理论探索》

《高性能网络编程(五):一文读懂高性能网络编程中的I/O模型》

《高性能网络编程(六):一文读懂高性能网络编程中的线程模型》

《技能扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解》

《让互联网更快:新一代QUIC协议在腾讯的技能实践分享》

《当代移动端网络短连接的优化手段总结:要求速率、弱网适应、安全保障》

《聊聊iOS中网络编程长连接的那些事》

《移动端IM开拓者必读(一):普通易懂,理解移动网络的“弱”和“慢”》

《移动端IM开拓者必读(二):史上最全移动弱网络优化方法总结》

《IPv6技能详解:基本观点、运用现状、技能实践(上篇)》

《IPv6技能详解:基本观点、运用现状、技能实践(下篇)》

《从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演化和设计思路》

《以网游做事真个网络接入层设计为例,理解实时通信的技能寻衅》

《迈向高阶:精良Android程序员必知必会的网络根本》

《全面理解移动端DNS域名挟制等杂症:技能事理、问题根源、办理方案等》

《美图App的移动端DNS优化实践:HTTPS要求耗时减小近半》

《Android程序员必知必会的网络通信传输层协议——UDP和TCP》

《IM开拓者的零根本通信技能入门(一):通信交流技能的百年景长史(上)》

《IM开拓者的零根本通信技能入门(二):通信交流技能的百年景长史(下)》

《IM开拓者的零根本通信技能入门(三):国人通信办法的百年变迁》

《IM开拓者的零根本通信技能入门(四):手机的演进,史上最全移动终端发展史》

《IM开拓者的零根本通信技能入门(五):1G到5G,30年移动通信技能演进史》

《IM开拓者的零根本通信技能入门(六):移动终真个接头人——“基站”技能》

《IM开拓者的零根本通信技能入门(七):移动终真个千里马——“电磁波”》

《IM开拓者的零根本通信技能入门(八):零根本,史上最强“天线”事理扫盲》

《IM开拓者的零根本通信技能入门(九):无线通信网络的中枢——“核心网”》

《IM开拓者的零根本通信技能入门(十):零根本,史上最强5G技能扫盲》

《IM开拓者的零根本通信技能入门(十一):为什么WiFi旗子暗记差?一文即懂!

《IM开拓者的零根本通信技能入门(十二):上网卡顿?网络掉线?一文即懂!

《IM开拓者的零根本通信技能入门(十三):为什么手机旗子暗记差?一文即懂!

《IM开拓者的零根本通信技能入门(十四):高铁上无线上网有多难?一文即懂!

《IM开拓者的零根本通信技能入门(十五):理解定位技能,一篇就够》

>> 更多同类文章 ……

(本文同步发布于:http://www.52im.net/thread-2456-1-1.html)

标签:

相关文章

Excel图表|散点图(气泡图)_变量_图表

在我的理解中,我之前先容的柱(条)形图、折线图、面积图、饼图,紧张是描述性的图表类型。它们的浸染在于将数据中原有的信息进一步挖掘并...

通讯 2024-12-25 阅读0 评论0

IT融合工程,推动产业转型升级的关键引擎

随着信息技术的飞速发展,IT融合工程逐渐成为推动产业转型升级的关键引擎。本文将从IT融合工程的内涵、发展现状、应用领域以及未来发展...

通讯 2024-12-25 阅读0 评论0

IT行业IU,驱动未来发展的核心动力

随着信息技术的飞速发展,IT行业已经成为推动全球经济增长的重要引擎。而在这场技术变革的浪潮中,IU(Information Uni...

通讯 2024-12-25 阅读0 评论0

IT行业,驱动未来创新的数字引擎

在当今社会,信息技术(Information Technology,简称IT)已经渗透到了人们生活的方方面面,成为推动社会进步的重...

通讯 2024-12-25 阅读0 评论0