在协议栈中,HTTP 属于第一层应用层,它的作用就是商定客户端与服务端之间的信息交互协定。
如果我们想要访问某个网站,那么一般我们要输入这个网站的网址,也就是 URL。URL 一般由如下几个部分组成:
- 协议头:标注协议的类型;
- 主机名:表明主机所在的地址(通过 DNS 转译为 ip 地址);
- 路径:所访问文件所在地址;
- 端口号:当前协议走的端口号(用来识别不同应用,一般在 URL 地址中不明文显示);
- 查询字符串:以 ?作为标识,?后的内容为查询内容 (一般都以值对的形式标识);
- 定位符:以 # 标识,与 html 中的 id 属性结合,可直接跳转至指定 id 处;
- 安全编码:如 / 等字符将被识别为不安全字符并被转译为 US-ASCII 16进制字符;
示例:
https://developer.mozilla.org/zhCN/docs/Learn/Common_questions/What_is_a_domain_name
https:协议头,表明当前所用协议为 https,一般默认端口为 443; developer.mozilla.org : 主机地址; zhCN... : 路径;
再如:
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=http&fenlei=256&rsv_pq=eacf4b2100113fe9&rsv_t=3faa%2FIrY9yB7qijAalZK2yeqz5H4gzb%2FBuSUWbO73VOTHdoHAjm98HdDZYo&rqlang=cn&rsv_dl=tb&rsv_enter=1&rsv_sug3=7&rsv_sug1=4&rsv_sug7=101&rsv_sug2=0&rsv_btype=i&inputT=2640&rsv_sug4=3741&rsv_sug=2
这是一则搜索 http 的百度 URL,?后键值对就是查询内容,看得出百度加入了许多自己的限制。
如果是搜索 \, 则会转译成 %2F ;
如果是想要定位页面中某一元素,直接跳转,则可以在 URL 最后加上 # 和该元素的 id。
当我们输入 URL ,点击回车后,浏览器会向服务器发送 request 请求,根据请求的内容,浏览器会有不同的回应,称之为 response。
当然,在发送请求之前,浏览器和服务器之间要建立连接,而连接靠的是 TCP。TCP 和服务器建立联系要经过三次握手:
- 浏览器发包;
- 服务器发回应包,验证了浏览器的发包能力和自己的接收能力;
- 浏览器再次发包,验证了服务器的发包能力和自己的接收能力;
三次握手后,TCP 连接建立,浏览器可以发送自己的 request 请求了。
上图是一则 request 消息内容,包含:
- 请求方式 URL 协议版本;
- 报头;
- 主体(response 会返回 HTML 文件,request 一般没有多的内容);
报头则包含:
- 主机地址;
- 连接状态(指的是 TCP 连接);
- 用户代理(一般指的是当前所用的浏览器);
- 接受的文件格式;
- 其他 URL 的索引;
- 接受的编码形式;
- 接受的语言;
- 接受的字符集;
当然,除了上述内容,一般的报头还包含:
- cookie
- 创建时间等等
在 HTTP 2 之前,报头都是明文显示,所以一般都能读懂。
现在来聊一下这里面比较重要的几个内容。
请求方式
常见的请求方式有四种:
- get
- post
- put
- delete
get 是请求返回某些内容,但不会改变这些内容; post 是请求某些内容,并更新它; put 是上传某些新的内容; delete 是删除服务器上已存在的某些内容;
cookie 也是值得聊的,但这跟后面要讲的连接有关,所以放到后面。现在我们再来看一下 response 。
response 由如下部分组成:
- 协议版本 状态 理由
- 报头
- 主体
报头一般包括:
- 缓存控制
- 文件类型
- 服务器地址
- 创建日期
- 连接状态
- 内容长度
状态是 response 中非常重要的存在,它表示服务器对请求的结果。比如我们常见的 404,就是一种状态。
状态一共有以下几种:
- 100 - 199,表示已接收请求,需要继续处理,属于过渡状态;
- 200 - 299,表示处理成功;
- 300 - 399,表示重定向(服务器将 URL 地址重定向到新的地址);
- 400 - 499,客户端错误(浏览器发送的请求错误);
- 500 - 599,服务器错误;
常见的状态码及理由包括:
- 200 OK ,表示一切正常;
- 301 Move Permanently ,表示该 URL 指向的内容已经被永久移动了;
- 302 Move temporarily,表示该 URL 指向的内容被暂时移动了;
更全的参见下表(没必要记,遇到的时候查一下就行):
现在我们知道了 request 和 response 的基本内容,需要来详细了解一下连接了。HTTP 靠 TCP 来连接,TCP 具有的好处就是不会丢包(数据包),并且 TCP 还提供了流控制,以确保发送方不会发送过快的数据使得接收方无法接收。
TCP 好是好,但建立连接需要三次握手,断开也是个冗长的过程,而 HTTP 又是无状态协议,不储存服务端发过来的任何状态。
试想,一个网页,每次请求都发起一个 TCP 连接,请求结束,连接断开。下一个请求得等上一个请求结束后才能继续,这样效率太低。而一个网页,需要 html 文件,CSS文件,JS 文件,图片,视频,要是都建立连接,未免太慢。
于是,在连接方式上,大家就想了各种方法:
并行连接
这种方法是向服务器发送多个连接,所以同一时间段内可以发送多份信息。但一个服务器的容量是有限的,多份连接容易把服务器挤爆。
持续连接
这种方法的好处是不断开(当然使用报头中的 connection 标识可以让连接断开),这样避免了断开再重连的时间耽搁,但持续连接仍然占用服务器位置,而且很容易受到攻击(因为连接不断开)。
流水线连接
这种连接方式不等待前一个连接应答就发送第二个连接,但服务器回复时仍然需要新建连接。
最新的 HTTP2 协议采用的是==双向连接==,这种连接方式只建立一条 TCP 连接,但允许该连接承载任意梳理的双向消息(request 和 response)。这样无论从速度上还是服务器的承受能力上,都有很大改善。
另外, HTTP2 还允许服务端主动推送消息给客户端(以前必须由客户端主动发起),这又省了很多功夫(主动判断服务端需要什么)。
现在我们知道了 URL,request,response 和 connection(连接),当我们输入 URL 时,浏览器发送 request,通过 TCP 协议与服务器建立连接,最后服务器返回它的 response。
但其实浏览器并不是直接把 request 甩给服务器的,这中间包括了很多 proxy(代理服务器),代理服务器不一定是真的物理设备,也可能是软件,这些代理服务器包括:
- 访问控制
- 身份验证
- 压缩响应体
- 负载均衡
- SSL 加密
- 缓存
- ...
这里我们聊一下缓存。缓存分为公有缓存和私有缓存,顾名思义,公有缓存大家都可以用,私有缓存就只有自己能用。
为什么要缓存呢?因为浏览器每次从服务器取资源耗时太长,也浪费资源,不如建一份复制资源,从缓存上取。
缓存涉及到一个问题,就是什么时候用缓存,什么时候重新取?为什么要重新取?因为资源有可能更新,而缓存的内容就成了旧版。
一般来说,可以根据更新时间来判断缓存是否过期,比如 浏览器发送的 header(报头)里就会问if modified-since ,后面跟着之前缓存文件的创建时间,意思是距离上次缓存后,文件有没有被修改。浏览器会根据真实情况作出反应,没变就返回 304(not changed),内容返回为空;如果变了就返回 200,重新返回新的文件。
另外,缓存一般会有一个 max-age 属性,用来标注该缓存自创建起最多缓存多少秒,只要超过该秒数缓存就作废,重新取。
还有一种方式是利用 ETag,该数值利用哈希算法生成,每次服务器传 response 的时候就在 header 里附带 ETag。如果文件更新了,那么 ETag 的数值就会发生变动,这样服务器就会返回新的文件。
到此,HTTP 的内容就聊的差不多了,最后我们再聊一下安全性的问题。
很多网站,是不允许随便访问的,需要输入用户密码,这就需要对用户进行验证。
常用的验证手段是 cookie,又称 HTTP 状态管理机制。浏览器每次访问时会在 header 中带上 cookie,cookie 中带有唯一标识符 (GUID) ,服务器验证。
理论上 cookie 中什么都能塞,它在 header 中用 set-cookie 来表示。如果 cookie 要想长期存在(服务器长期用 cookie 验证),就得设置 expire,期满时间。不同网站的 cookie 是不一样的。
但 cookie 没有办法验证用户,如果我获取了你的 cookie,那么我可以用 cookie 登录某些私人网站,这是有风险的。
所以常用的方法是 authentication(认证),就是我们登录网站时要输入的账户密码。
当用户登录某个网站时,如果网站需要认证,就会重定向到一个登录页面,用户输入账户密码验证成功后,会重定向回来(这时候会设置一个 cookie,以后就不用每次都要登录了)。
还有一种方式是 openID ,就是把检验的责任交给其他人,比如我们常用的微信、QQ 登录,登录其他网站时也可以用这些来登录。
好处就是我们不必申请那么多的账户密码了。
需要注意的是,认证只是服务器确认我们的信息是否正确的方式,但服务器本身也需要被验证,比如一些钓鱼网站,误入就不好了。
这时候就需要 HTTPS 协议的出现,这个协议是在应用层和传输层之间插了一层 SSL/TLS 的加密层,这可以保证网站的安全性。
想要采用 HTTPS 协议,网站需要安装安全证书(由权威机构发放),相当于一种凭证。
最后,简单讲一下 HTTP2 与前面版本的区别:
- 消息由明文改为二进制;
- 报头压缩(采用了新的压缩算法);
- 双向连接;
- 允许服务端推送消息;