欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

HTTP协议学习(上)

程序员文章站 2022-07-03 08:10:30
首先,协议是干嘛的?为什么需要协议?我想到一个不太恰当的比喻,战争期间情报人员接头都要使用暗号: “脸红什么?” “精神焕发。” &ldq...

首先,协议是干嘛的?为什么需要协议?我想到一个不太恰当的比喻,战争期间情报人员接头都要使用暗号:

“脸红什么?”

“精神焕发。”

“怎么又黄拉?”

“防冷涂的蜡。”

在外人看来就是普通的对话,但是对于掌握而破解规则的情报人员,里面就包含了大量的信息。所以说什么不要紧,重要的是规则,也就是如何去解读。

地球人都知道,计算机内部以及构建在计算机基础上的网络世界,二进制才是他们使用的语言,也就是一串的“010111010100012”。(为什么是二进制?因为二进制表示起来很方便啊!这个以后会写一篇文章详谈。)同样的道理,浏览器给服务器发送了一堆0-1序列,服务器怎么才能听得懂?所以,二者需要共同制定一种大家都遵守的解读规则,这就是针对Web服务的HTTP协议。当然也有其他的协议,比如用于邮件的SMTP协议,文件传输的FTP协议,本质上都是一个道理。

HTTP协议分为请求和响应两部分,他们具有相似但不同的格式。这篇先介绍请求。我们平时上网我们使用的都是浏览器,然而浏览器都设计的十分智能以方便普罗大众,但是这样以来也屏蔽掉了太多底层技术细节。为了真实的展现一次http协议通信的过程,我们可以试试更加原始的telnet程序,纯手敲协议与服务器进行交流(话说这样真的很酷~)。

在Ubuntu的终端下敲入以下命令(Windows也有telnet程序,自己搜索安装一下):

telnet www.baidu.com 80

这一步是为了连接到服务器(可以理解为建立TCP连接,这是HTTP协议所依赖的底层协议),所以给telnet加上了两个参数:URL和端口号,URL可以是IP,也可以是域名;端口号一般都是80。不出意外会有以下回应:

Trying 119.75.217.109...

Connected to www.a.shifen.com.

Escape character is '^]'.

这时,就可以输入HTTP协议本身了:

GET / HTTP/1.1

Host: www.baidu.com

输入完之后,连敲两个回车,就会出现以下内容:

HTTP/1.1 200 OK

Date: Sat, 29 Oct 2016 10:12:11 GMT

Content-Type: text/html

Content-Length: 14613

Last-Modified: Tue, 18 Oct 2016 06:34:00 GMT

Connection: Keep-Alive

。。。略

Pragma: no-cache

Cache-control: no-cache

Accept-Ranges: bytes

。。。。。。略

请求头分为三个部分:请求行、请求头、请求体(可选)。每一部分都必须以结尾,也就是一个回车。

这里需要注意的是,请求头和请求体之间需要加一个空行,即此行内容只有只有。为什么要这样呢?这得从请求头说起,请求头实际上是一组键值对,每个键值对的格式是“键: 值”,注意冒号后面有个空格。但是键值对之间如何分隔呢?答案是。我试过把两个键值对放在一行,中间用一个空格分开,然后服务器主动关闭连接了。。。这说明,键值对之间必须以回车分隔。

然后问题来了,请求头内部就以回车来分隔了,那它如何跟请求体分隔呢?所以,只有加一个空行了。

当然,我是一个善于思考的淫,我很好奇为啥请求行与请求头之间不来一个空行呢?甚至,三者之间为什么一定要分隔呢?

对于第二个问题,三者解读的方式应该是不一样的,所以必须要分隔。比如请求行,一共三个部分,分别是请求方法、请求资源的路径和协议版本,它们之间以空格分隔,当遇到第一个回车时,意味着请求行的结束,下面的数据就是请求头了(第一个问题顺便解决)。但是请求头每个键值对都是一行,到底遇到哪个回车才算完呢?所以需要一个空行代表请求头的结束。在这之后就是请求体了,这部分是可选的,基本上只有请求方法是POST的时候,这部分才是必须的。同样的,这部分也需要一个回车作为结束标志。

好,下面分析请求行。先看第二部分,即资源路径。在写这个路径的时候,我们不必再加上IP和端口号了,因为TCP已经帮HTTP做好了——IP定位到某台机器,而端口号定位到机器上的某个进程。这时候,我们可以认为请求的资源是存放一个文件系统中的,尤其是静态资源,动态资源先不管。根路径“/”就代表了进程所规定的根目录,各级子路径代表对应的子目录。要注意的是,“/”所代表的根目录未必是该服务器文件系统真正的根目录。实际上,出于安全的考虑,前者肯定不能是真正的根目录,只能是某个子目录。因为一旦黑客通过攻击该进程而获取了整台机器的最高权限,那就什么都完了。

第三部分是协议版本,目前HTTP最新的版本时1.1,这也是使用最广泛的一个版本。不过为了学习,我们还是看一下最初的版本长啥样:

GET /index.html

。。。。是的,只有一行,两部分。第一部分是请求方法,只有GET一个类型,且因为是第一版(HTTP/0.9),连版本号都省略了。对这个请求的回应,也只能是HTML字符串,不能有其他东西。。多简陋啊,所以有了第二个版本HTTP/1.0,首先就是增加了POST和HEAD两个请求方法,同时由于版本多了,所以请求行多了一个协议版本部分;然后就是增加了请求头,比如User-Agent、Accept等。

下面以HTTP/1.1为蓝本介绍请求格式。

首先是常见的请求方法:

GET - get嘛,就是“获取”的意思,向服务器请求特定的、由URI标识的资源,但不会改变服务器的数据,仅仅是被动的接收。

POST - 用于向指定URI提交数据,数据被包含在请求体中。正如上文所说,POST请求是在第二个版本中增加的,因为第一个版本中只有GET,浏览器只能被动接收服务器的数据;但一些场景下,需要浏览器主动向服务器发送数据,以对服务器产生影响。这就是POST方法的用途。

一般情况下,我们只会用到这两个请求方法,面试的时候经常会被问到这两个方法的区别,在这里也捎带说一下:

1、GET方法实际上也可以向服务器发送数据,但是这个数据依然不会对服务器造成改变,这些参数只是为了限定查询条件,类似于SQL查询的where子句;POST方法也可以不带任何参数,纯粹当个GET来使,只不过有点浪费而已。

2、GET方法的参数是放在URL里面,在请求资源路径的后面用一个问号分隔;参数的形式也是键值对,但是使用等号相连;键值对之间以“&”符号分隔。然而GET的参数有个限制,因为参数是放在URL里面,但是URL的总长度是有限制的,所以GET方法的参数也不能太长,最多只有1024字节。而POST方法将参数置于请求体中,理论上是没有长度限制的。

更正(来源不详,是我抄的):

因为GET是通过URL提交数据,那么GET可提交的数据量就跟URL的长度有直接关系了。而实际上,URL不存在参数上限的问题,HTTP协议规范没有对URL长度进行限制。这个限制是特定的浏览器及服务器对它的限制。IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器,如Netscape、FireFox等,理论上没有长度限制,其限制取决于操作系统的支持。注意这是限制是整个URL长度,而不仅仅是你的参数值数据长度。

3、发送给服务器的时候,GET的参数会直接显示在地址栏里面;而POST的参数不显示在地址栏里,而后台发送,想要查看只能通过浏览器的控制台。置于所谓的安全性,讲真,对黑客来说没太大区别,普通人也不会有“从URL里面找到密码”的意识。

4、后台解析方式不同。在ASP中,服务端获取GET请求参数用Request.QueryString,获取POST请求参数用Request.Form。在JSP中,用request.getParameter(\"XXXX\")来获取,虽然jsp中也有request.getQueryString()方法,但使用起来比较麻烦,比如:传一个test.jsp?name=hyddd&password=hyddd,用request.getQueryString()得到的是:name=hyddd&password=hyddd。在PHP中,可以用$_GET和$_POST分别获取GET和POST中的数据,而$_REQUEST则可以获取GET和POST两种请求中的数据。

更多的区别参见这里,还有一篇标题很二的文章也讲二者的区别,都他妈分析到TCP层面上了,我也是服了。

PUT - 向指定资源位置上传其最新内容。

DELETE - 请求服务器删除Request-URI所标识的资源。

这两个方法用的很少,理论上它们可以做到的POST都可以做到,先不多说。

OPTIONS - 返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。看个例子:

m@sys:$ telnet localhost 8080

Trying 127.0.0.1...

Connected to localhost.

Escape character is '^]'.

OPTIONS * HTTP/1.1

host: localhost:8080

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS

Content-Length: 0

Date: Sat, 29 Oct 2016 23:59:08 GMT

HEAD- 向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。该方法常用于测试超链接的有效性,是否可以访问,以及最近是否更新。栗子:

m@sys:$ telnet localhost 8080

Trying 127.0.0.1...

Connected to localhost.

Escape character is '^]'.

HEAD / HTTP/1.1

host: localhost:8080

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Content-Type: text/html;charset=ISO-8859-1

Transfer-Encoding: chunked

Date: Sun, 30 Oct 2016 14:52:37 GMT

TRACE- 回显服务器收到的请求,主要用于测试或诊断。这个用的也很少,略。

然后是常见的请求头的属性:

Host:客户端IP和端口,发送请求时,该报头域是必需的。我试过不加这个,真不行。。浏览器发送的请求消息中,就会包含Host请求报头域,如下:

Host:www.guet.edu.cn

此处使用缺省端口号80,若指定了端口号,则变成:Host:www.guet.edu.cn:指定端口号

User-Agent:浏览器信息

Content-Length:表示请求消息正文的长度。

Cookie:最重要的请求头之一, 将cookie的值发送给HTTP服务器。

Accept:浏览器能接收的数据类型

Accept-encoding:浏览器能够进行解码的数据编码方式,比如gzip。

Accept-charset:浏览器可接受的字符集。

Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。

Authorization:请求报头域主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证。

Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝;在HTTP/1.1版本中,它和Cache-Control:no-cache作用一模一样。Pargma只有一个用法, 例如: Pragma: no-cache