深入解析Http请求背后的秘密(下)

前言

这边是承接上文深入解析Http请求背后的秘密(上)而写的,主要讲解在一次HTTP请求过程中,我们客户端已经创建好和服务器的连接通道之后,正式发送数据的过程。主要分两步:请求的发送和应答、断开连接

由于篇幅有限,我尽可能抓我们平时都会接触到的内容来做解析,一些不太需要了解的,就简单说明一下了,如果对你有用,记得一键三连哟~

一、请求的发送和应答

当我们创建好Http的连接时,接下来就是开始真正的发送我们的请求数据了,应用程序将数据发送到协议栈后,协议栈会暂时将数据放到发送缓冲区,等待应用程序的下一段数据。然后到达一定的数量之后,一次性发送出去,这样是为了充分利用每个网络包的容量。但是这样就会加长每次发送的等待时间,最终导致请求的延时,所以需要综合考虑,不同的操作系统对此的处理方式不同,主要就是考虑两点因素的平衡:网络包能容纳的最大长度(MTU)以及等待时间。像浏览器这种对时间要求较高的,一般发送缓冲区之后就需要即刻发送出去,其中发送的整个包的大致结构如下。
在这里插入图片描述
数据发送出去之后,并不代表这一次发送就是成功的,需要服务器返回一个已收到的应答,让客户端知道数据已经完整无误的发送成功,否则如果发送失败,则触发TCP的数据重发机制。这里如何去保证数据发送成功呢?这就需要依赖我们上篇的报文截图了,如下:
在这里插入图片描述
上篇曾经介绍过32位的序列号和确认号,序列号用于告诉对方这次发送的数据,是整个数据从头开始的第几个字节,而发送数据的长度则可以由服务器自己通过整个包的长度减去包头的长度得到。服务器在接收到数据后,需要返回一个确认应答,就会将目前已经接受到多少个字节写入32位的确认号(ACK号)中。客户端接收到之后,通过ACK号就能确认对方是否将全部的数据接收到,以此考虑是否重发或者发送下一个数据包。具体的流程大致如下:
在这里插入图片描述

注意:序列号并不是从初始位置开始的,而是一个随机位置,这样是为了保证数据传输的安全,避免网络攻击。在网络创建连接的时候,在第一次握手的时候,会同时设置序列号字段的值,这个值就是序号的初始值

需要注意的是,服务器或者客户端在接收到对方发来的数据包的时候,都会先暂存到接收缓冲区,然后由对应的应用程序来读取这个缓冲区中的数据。

在实际的交互过程中,为了降低等待时间,提高传输的效率,当客户端还没有收到服务器返回的ACK号时,也可以继续发下一段的数据包,这样就能减少等待ACK号应答的时间。但是这样也可能导致服务器的接收缓冲区出现内存溢出的情况,所以客户端需要在一开始就知道服务器的接收缓冲区的大小,然后每次在发送请求数据的时候计算下服务器的接收缓冲区还剩多少空间,防止内存溢出的情况。由于服务器端的应用程序可能会读取接收缓冲区的数据,读取完的数据会被删除,这就导致了缓冲区的空间大小其实是会变更的,所以也会需要服务器在应用程序读取缓冲区数据的时候,向客户端发送一个空间大小变更的通知。
同时,当服务器端接收到多次客户端发来的数据传输请求时,可以省略返回多次ACK号的通知,直接返回一次,说明最终已接收的长度即可,甚至当刚好缓冲区的数据被读取时,发送空间大小变更的通知可以和ACK号返回通知合并为一条,提高信息的传输效率。大致的流程如下:
在这里插入图片描述
上面就大致讲完了浏览器向服务器发送数据的过程,至于服务器返回数据给浏览器其实和浏览器发送数据是基本一致的过程和原理,这里就不再介绍。当浏览器发送完数据请求之后,就会去调用socket库中的read应用程序,控制流程会通过read转移到协议栈中并尝试从接收缓冲区中读取数据,如果接收缓冲区暂时还未收到数据,则会暂时挂起改应用程序,等服务器返回的响应消息到达之后在继续执行接收工作。至于服务器是如何发送响应数据的,这个和客户端发送请求数据的流程基本一致,就不再赘述。

二、断开连接

当所有的数据完成传输之后,就需要断开连接了。至于哪一方发起断开操作,这个在协议栈上都是允许的。这里以服务器方断开过程为例来讲解。

  1. 服务器一方的应用程序会调用Socket库中的close程序,然后服务器的协议栈就会生成断开信息的TCP头部,具体的就是将控制位中的FIN比特设为1。在服务器发送断开请求的同时,也会将服务器的套接字中记录断开操作的相关信息。
  2. 客户端接收到断开请求之后,也会将自己对应的套接字标记为断开操作状态。然后发送一个包含ACK号的消息给服务器告知已接收到断开的请求。并告知应用程序(浏览器)响应数据已经全部接收到。
  3. 客户端在接收到所有数据之后,也会调用close来结束收发操作。同样也会生成一个FIN为1的TCP包,发送给服务器,告知服务器客户端结束数据收发。
  4. 服务器返回包含ACK号的请求,告知客户端已经接收到它的断开请求。

完成上面四次操作(挥手)之后,原本用来通信的套接字就会被删除掉了。不过一般不会立即删除套接字,而是会等待一段时间,防止误操作。这个误操作针对的情况有很多,比如服务器没有接收到ACK号时,则会重发一次FIN为1的请求,如果客户端的套接字被删除了,对应的端口号也会被释放出来,如果别的应用程序刚好用这个端口创建了套接字,然后接收到了服务器发来的FIN请求,就会导致别的应用程序触发断开的操作流程。所以一般套接字在通信完之后,都会暂时保留一段时间再删除。

总的过程就大致如下,当然这其中还有很多的点值得探究和推敲,这里就不再详细去说了。
在这里插入图片描述
在这里插入图片描述
原创不易,欢迎一键三连~

参考资料:

《网络是怎样连接的》

TCP头部格式

猜你喜欢

转载自blog.csdn.net/dypnlw/article/details/114121074
今日推荐