我们在平时的开发过程中,网络请求一般使用HTTP,当然,为了安全性考虑,现在几乎都在使用HTTPS了。但是,我们始终是有必要去了解HTTP的连接管理,因为HTTPS只是在HTTP的基础上增加了安全层。这篇博客主要是讲解的HTTP相关的知识,如果对HTTPS的通信感兴趣的同学,可以查看我的另一篇博客【“攻城狮” 需要了解的密码知识】,里面最后的内容就是讲解的HTTPS相关的知识。
HTTP要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。TCP收到数据流之后,会将数据流分割成小的数据段,并将段封装在IP分组中,通过因特网进行传输。所有这些工作都是由 TCP/IP 软件来处理的。
大致的网络协议栈结构图如下:
从上图可以看出,HTTP是依赖于TCP进行传输的,所以HTTP的连接本质上是TCP的连接。IP地址可以将你连接到正确的计算机,而端口号则可以将你连接到正确的应用程序上去。TCP连接是通过4个值来识别的:
<源 IP 地址、源端口号、目的 IP 地址、目的端口号>
我们知道,HTTP在进行网络传输之前,有一个连接的过程,这个过程就是三次握手;而在通信完毕,不需要连接的时候,为了不浪费连接资源,会有四次挥手的操作。
三次握手的具体流程:
通过上面的三次握手,客户端就与服务端建立了连接,后面就可以进行数据的传输通信了。需要注意的是:现代的TCP栈都允许客户端在第三步确认连接的时候,同时在这个连接中发送数据。
为什么是三次握手,两次不行吗?
从上面的分析,我们可以看出,HTTP的握手连接需要进行三步操作。而其实经过前面两步握手操作之后,客户端与服务端就已经确认了通信的双方,完全可以不需要第三步的确认操作。
现假定出现一种异常情况,即客户端发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,以至于延误到释放以后的某个时间才到达服务端。本来这是一个早已失效的报文段,但服务端收到以后,误以为是客户端又发出一次新的连接请求。于是就向客户端发出确认报文段,同意建立连接。假定不采用三次握手,那么只要服务端发出确认,新的连接就建立了。由于现在客户端并没有发出建立连接的请求,因此不会理睬服务端的确认,也不会向服务端发送数据。但服务端却以为新的连接建立了,并一直等客户端发来数据,服务端的许多资源就浪费了。
上图中的
FIN_WAIT_1:客户端自己发出FIN请求至得到服务端的ACK响应这段时间的等待。
FIN_WAIT_2:客户端在收到服务端的ACK和FIN两个响应这段时间的等待。
CLOSE_WAIT:服务端连发了两个响应(ACK、FIN)给客户端。
FIN_WAIT:服务端自己发出FIN请求到得到客户端的ACK响应这段时间的等待。
CLOSED: 表示连接彻底关闭了。
四次挥手的具体流程:
HTTP请求是 “无记忆” 的,也就是说下一次请求不知道上一次请求发生了什么;所以出现了session,cookie等技术来实现状态保持。默认情况下,一次请求响应结束之后,就会断开连接了;下次进行请求的话,又会重新建立HTTP连接。而实际情况下,连接所耗费的时间比数据通讯的时间更长,所以出现了长连接(Keep-Alive)机制。也就是这次请求完成之后,并不会立刻断开连接,保持一段时间,如果在保持的时间范围内,下次过来的请求就不会重新建立连接过程,而是直接的进行数据通讯,这样就大大的提升了网络请求的效率。
实现Keep-Alive连接的客户端可以通过包含Connection: Keep-Alive首部请求将一条连接保持在打开状态。如果服务端愿意为下一条请求将连接保持在打开状态,就在响应中包含相同的首部。如果响应中没有Connection: Keep-Alive首部,客户端就认为服务端不支持Keep-Alive,会在发回响应报文之后关闭连接。
Keep-Alive 首部只是请求将连接保持在活跃状态。发出Keep-Alive请求之后,客户端和服务器并不一定会同意进行Keep-Alive会话。它们可以在任意时刻关闭空闲的Keep-Alive连接,并可随意限制Keep-Alive连接所处理事务的数量。