一、前言
http协议是当今世界互联网的基石,在你浏览器中所能看到的几乎一切内容,都是建立于http协议之上的。
“协议”两个字虽然看上去很高大上,但实际上只是一套公认的规则而已,你当然可以不遵守这套规则,但其代价就是你所公布的内容可能无法得到有效的传播。
因为我们所使用的浏览器其默认就只支持少数的几种公认的协议,你自己定义的协议(规则),浏览器不认识,别人就无法通过浏览器来获取到我们的内容。
所以可以这样说:学习协议本身的目的,其实是为了让我们可以站在前人的肩膀上。
http协议是建立在tcp之上的,也就是基本的网络编程之上,如果你对此不甚了解,那么我推荐你先看看基本的网络编程逻辑:网络编程详解。
更进一步,如果你想要知道如何在tcp之上实现一个web服务器、与浏览器打交道,那么你可以再看看文章:手写一个WebServer。
本文主要介绍的是理论层面上http协议的基本工作原理,并不会涉及到编程实现。
二、http协议工作原理
在有了基本的网络编程基础之后,我们知道tcp链接的建立是需要两个东西的:
- 服务器:固定在机器上,等待客户端来连接
- 客户端:主动去连接服务器
只有当服务器与客户端建立起来连接、两者之间可以互相发送数据之后,才轮到我们http协议登场。
其中实现了http协议的服务器也被称为web服务器,也就是网站服务器,比如本站就是一个web服务器,并且搭建在云服务器上。
而客户端,直观来说就是你现在所使用的浏览器,就是一个客户端,它同时实现了http协议。
一旦你用浏览器访问本网站的网址:www.kucoding.com
,你的浏览器第一步就会自动去查询这个网址所对应的ip地址。
拿到了ip地址,然后用http协议默认的80端口(https协议默认用的443端口)就能将你的浏览器连接到我的服务器上。
注意这里的默认,实际上就可以看到一种协议(规则),如果你不遵守这个规则,比如我可以让本网站不搭建在80或443端口,那么通过浏览器的默认规则,你就无法直接访问我的网站,这对于我而言就是减少用户数量、体验的损失,肯定是得不偿失的。
完成了连接这一步骤,第一步就是你的浏览器向服务器发送数据了。
比如你之所以能在浏览器上看到我网站的主页,其原因就是你的浏览器向我的服务器发送了一个类似下面这样的请求:
GET / HTTP/1.1
Host: www.kucoding.com
你没看错,浏览发送就是这样一个纯文本、我们人可以直接识别的内容,这就是http协议的规则。
http协议主要分为“请求”、“响应”这两种,请求指的是客户端向服务器发送的内容,而响应就是服务器根据客户端的请求、所发回来的内容。
其中请求的格式就像上面所展示出来的一样,主要分为三个部分。
第一个部分就是第一行的内容:
GET / HTTP/1.1
第一行又分为三个部分:
GET
:又曾为GET方法,用来标识这个请求要做什么,GET方法默认作为“获取”的含义,也就是要从服务器获取内容,除此之外你还可以填POST
用于上传数据、DELETE
用于删除数据等等。/
:路径,这个路径和我们电脑上的文件夹路径是一样的,只不过采用的是linux系统文件路径格式,用的反斜杠/
,/
代表的就是根目录,一般也就是网站的主页。HTTP/1.1
:协议版本,http协议也是有很多版本的,只不过目前最广泛使用的是1.1
,所以这个基本不用更改。
完成了第一部分内容的介绍,我们就可以来到第二部分了,也就是“标头”,要注意,所有紧跟第一行之后的内容都属于第二部分“标头”的内容,虽然我这里只写了一个:
Host: www.kucoding.com
比如这里Host
字段就是http协议中的一部分,用来指明该请求要发送的主机,这里就是域名。
这样的字段有很多,比如我直接从浏览器的控制台中截取下来的请求:
这里就有非常多的标头字段,每个字段用:
分隔,前面是名字、后面是它的值,每个字段都有自己的含义。
但你可以自定义属于自己的字段,然后在你的服务器上拿到该字段的值做一些事情、判断之类的。
然后是第二部分过后就是第三部分,也就是“请求体”,但GET请求不带请求体,所以就是空白的。
标头与请求体之间用的是两个换行符进行分隔的,所以你可以看到前面我在代码最后留下了两行空白。
如果将换行符显示出来,那么实际上请求的协议内容如下:
GET / HTTP/1.1\r\n
Host: www.kucoding.com\r\n
其它标头... \r\n
\r\n
请求体
同时注意,所有换行都是使用的\r\n
这两个字符实现的,也就是一个换行就需要这两个字符,如果你想要自己实现http协议的话,那么这一点就尤其需要注意,
只管来说,标头后面空一行之后的内容,就是请求体,但由于GET请求是请求服务器的内容,而不是向服务器上传内容,所以它的请求体就是空的。
而POST请求一般用于向服务器上传数据,所以它的请求体一般就会携带一些数据,这个数据可以为任何格式的数据,但前提是相应的服务器能够识别,最常用的其实是json格式的数据。
上面便是客户端向服务器发送请求的过程,完成了这一过程客户端就会开始等待服务器发送该请求的回应,也就是“响应”。
一个基本http响应的格式如下,其实基本与请求格式相同:
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
响应体,比如html文档、json数据、图片数据等等
响应同样有三个部分,并且同样使用\r\n
作为换行符,且标头与响应体之间同样用一行空白进行分隔。
甚至可以说,除了第一部分外,后面两部分其实在“请求”“响应”是完全相同的。
其中第一部分、也就是第一行,同样分为三个部分:
HTTP/1.1
:http协议版本200
:状态码,200代表正常响应OK
:状态码200
对应的字符串含义,也就是OK
本身的意思,代表客户端发送的请求没问题、并且服务器发送回来了正确的响应!
而在第二部分标头中,同样是一堆键值对,唯一不同的是,服务器发送的标头的值一般和客户端不同,它里面更多描述的是服务器的状态信息、以及该响应的信息等等。
至于最后的响应体就是请求体是完全相同的了,这完全取决于请求的方式。
比如,当客户端发送GET请求,那么请求体就是空的,但此时响应体就有内容,发送回客户端要请求的内容。
而客户端发送POST请求时,那么请求体就是要客户端要上传到服务器的内容,而响应体可能就是空的,因为服务器并没有什么东西要发送回来。
三、实战分析
为了能够更加清晰的理解上面这一过程,我们直接来到本网站的主页,按F12
打开浏览器的开发者模式,打开“网络”标签:
如果你发现“网络”标签内是空的,那么就先点击左上角的按钮、刷新页面即可,然后你就能看到你的浏览器向我的服务器发送的所有请求、响应了。
我们这里只需要注意第一条请求即可,点开它可以看到下面这样的内容:
首先是标头,当你点击“原始”时,就能看到你的浏览器发送的http原生请求内容了,和我们前面介绍的格式是完全一样的,只不过这里标头中的内容更多而已。
同样的,我们还可以看到我的服务器根据你的这条请求发送回了什么响应:
这里展示的仅仅只是响应、请求的前面两个部分,由于get请求没有请求体,所以这里我们可以先看响应体:
点击上方的响应,就能看到当你发送GET请求后,我的服务器就给你发送了这样的http文档,当你的浏览器解析了该文档之后,就能呈现出你所看到的内容了。
至于请求体,会出现在一个叫做“负载”的标签中,对于没有请求体的内容,该页面不会显示。
比如当我打开一个知乎页面,点击其中一个链接时,就能看到:
但显然,它这里发送的请求应该是经过加密、或者压缩过的,我们无法直接看出来它向服务器发送了什么内容。
四、更多
了解了上面http基本的工作过程了之后,你如果想要更加深入的理解http应该会更加的容易。
无非就是了解各种标头的默认含义、各种方法的基本功能(比如get、post、put、delete、patch等等)。
这些内容你都可以在官方文档中找到:HTTP 概述 - HTTP | MDN (mozilla.org)