一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
往期文章
- 网络是怎样连接的(一)—— 浏览器访问 Web 服务器过程概览
- 网络是怎样连接的(二)—— 浏览器生成 HTTP 消息
- 网络是怎样连接的(三)—— 通过 DNS 服务器查询 IP 地址
- 网络是怎样连接的(四)—— DNS 服务器工作介绍
- 网络是怎样连接的(五)—— 委托操作系统进行收发消息过程概览
- 网络是怎样连接的(六)—— 协议栈内部探索步骤
- 网络是怎样连接的(七)—— 协议栈的内部结构
- 网络是怎样连接的(八)—— 探索套接字
- 网络是怎样连接的(九)—— 连接连的到底是啥
- 网络是怎样连接的(十)—— 连接操作的实际过程
前言
在上一篇文章中,我们介绍了连接操作的过程,接下来就到了数据发送阶段。在介绍数据发送过程之前,我们先来学习一个知识点,协议栈数据发送的一些特点,即:
- 不关心数据内容,都看做是二进制数据;
- 数据不会立即被发送,而是会被发送到发送缓冲区中;
- 大数据会被拆分;
接下来,我们来详细介绍一下这些内容;
概念说明
- MTU:最大传输单元(Maximum Transmission Unit),MTU 是一个网络包的最大长度,以太网中一般为 1500 字节;
- MSS:最大分段大小(Maximum Segment Size),出去头部之后,一个网络包所能容纳的 TCP 数据的最大长度。TCP 和 IP 的头部加起来一般是 40 字节,因此 MTU 减去这个长度就是 MSS。例如,在以太网中,MTU 为 1500,因此 MSS 就是 1460。TCP/IP 可以使用一些可选参数(Protocol option),如加密等,这时头部的长度会增加,那么 MSS 就会随着头部长度增加而相应缩短;
1. 不关心数据内容
首先,协议栈并不关心应用程序传来的数据是什么内容。应用程序在调用 write 时会指定发送数据的长度,在协议栈看来,要发送的数据就是一定长度的二进制字节序列而已。
2. 数据放入缓冲区中
其次,协议栈并不是一收到数据就马上发送出去,而是会将数据存放在内部的发送缓冲区中,并等待应用程序的下一段数据。
这样做的原因是:应用程序交给协议栈发送的数据长度是由应用程序本身来决定的,不同的应用程序在实现上有所不同,有些程序会一次性传递所有的数据,有些程序会逐字节或者逐行传递数据。总之,一次将多少数据交给协议栈是由应用程序自行决定的,协议栈并不能控制这一行为。
在这样的情况下,如果一收到数据就马上发送出去,就可以会发送大量的小包,导致网络效率下降,因此需要在数据积累到一定量时再发送出去。
至于要累计多少数据才能发送,不同种类和版本的操作系统会有所不同,不能一概而论,但都是根据下面两个要素来判断的:
- 网络包容纳数据的长度:第一个判断要素是每个网络包能容纳的数据长度,协议栈会根据一个叫做 MTU 的参数来进行判断。MTU 表示一个网络包的最大长度,在以太网中一般是 1500 字节,MTU 是包含头部的总长度,因此需要从 MTU 减去头部的长度,然后得到的长度就是一个网络包中所能容纳的最大数据长度,这一长度叫做 MSS。当从应用程序收到的数据长度超过或者接近 MSS 时再发送过去,就可以避免发送大量小包的问题了。
- 时间:另外一个判断要素是时间。当应用程序发送数据的频率不高的时候,如果每次都等到长度接近 MSS 时在发送,可能会因为等待太长而造成发送延迟,这种情况下,即便缓冲区中的数据长度没有达到 MSS,也应该发送出去,为此,协议栈的内部有一个计时器,当经过一定时间之后,就会把网络包发送出去;
然而这两个要素其实是相互矛盾的。如果长度优先,那么网络的效率会提高,但可能会因为等待填满缓冲区而产生延迟;相反的,如果时间优先,那么延迟时间会变少,但又会降低网络的效率。 因此,在进行发送操作时需要综合考虑这两个要素以达到平衡。不过,TCP 协议规格并没有告诉我们怎样才能平衡,因此实际如何判断是由协议栈的开发者来决定的,也正是由于这个原因,不同种类和版本的操作在相关系统上也存在差异。
如果仅靠协议栈来判断发送的实际可能会带来一些问题,因此协议栈也给应用程序保留了控制发送实际的余地。应用程序在发送数据时可以指定一些选项,比如如果指定“不等待填满缓冲区直接发送”,则协议栈就会按照要求直接发送数据。像浏览器这种会话型的应用程序在向服务器发送数据时,等待填满缓冲区导致延迟会产生很大影响,因此一般会使用直接发送的选项。
3. 对较大的数据进行拆分
前面我们提到过,一个网络包中所能容纳的网络包最大是 1460 字节。如果一个请求消息超过这个大小怎么办呢。答案是协议栈会对这个网络包进行拆分。
通常情况下 HTTP 请求消息一般不会很长,一个网络包就能装得下,但如果其中要提交表单数据,长度就可能超过一个网络包所能容纳的数据量,比如在博客或者论坛上发表一篇长文就属于这种情况。
这种情况下,发送缓冲区中的数据就会超过 MSS 的长度,这是我们当然不需要继续等待后面的数据了。发送缓冲区中的数据会被以 MSS 长度为单位进行拆分,拆分出来的每块数据会被放进单独的网络包中。
根据发送缓冲区中的数据拆分的情况,当判断需要发送这些数据时,就在每一块数据前面加上 TCP 头部,并根据套接字中记录的控制信息标记发送方和接收方的端口号,然后交给 IP 模块来执行发送数据的操作。
应用程序的数据一般都比较大, 因此 TCP 会按照网络包的大小对数据进行拆分。
参考文章
- 《网络是怎样连接的》—— 户根勤