乐趣区

关于网络:计算机网络概述协议栈和网卡

咱们将摸索操作系统中的网络控制软件(协定栈)和网络硬件(网卡)是如何将浏览器的音讯发送给服务器的。

创立套接字

协定栈的内部结构

和浏览器不同的是,协定栈的工作咱们从外表上是看不见的,可能比拟难以想象。因而,在理论摸索之前,咱们先来对协定栈做个解剖,看看外面到底有些什么。

协定栈的外部如图所示,分为几个局部,别离承当不同的性能。这张图中的高低关系是有肯定规定的,下面的局部会向上面的局部委派工作,上面的局部承受委派的工作并理论执行,这一点大家在看图时能够参考一下。当然,这一高低关系只是一个总体的规定,其中也有一部分高低关系不明确,或者高低关系相同的状况,所以也不用过于纠结。

下层会向上层逐层委派工作。图中最下面的局部是网络应用程序,也就是浏览器、电子邮件客户端、Web 服务器、电子邮件服务器等程序,它们会将收发数据等工作委派给上层的局部来实现。应用程序的上面是 Socket 库,其中包含解析器,解析器用来向 DNS 服务器收回查问。再上面就是操作系统外部了,其中包含协定栈。协定栈的上半局部有两块,别离是负责用 TCP 协定收发数据的局部和负责用 UDP 协定收发数据的局部,它们会承受应用程序的委托执行收发数据的操作。像浏览器、邮件等个别的应用程序都是应用 TCP 收发数据的,而像 DNS 查问等收发较短的控制数据的时候则应用 UDP。

上面一半是用 IP 协定管制网络包收发操作的局部。在互联网上传送数据时,数据会被切分成一个一个的网络包,而将网络包发送给通信对象的操作就是由 IP 协定来负责的。IP 上面的网卡驱动程序负责管制网卡硬件,而最上面的网卡则负责实现理论的收发操作,也就是对网线中的信号执行发送和接管的操作。

套接字的实体就是通信管制信息

咱们曾经理解了协定栈的内部结构,而对于在数据收发中表演要害角色的套接字,让咱们来看一看它具体是个怎么的货色。

在协定栈外部有一块用于寄存管制信息的内存空间,这里记录了用于管制通信操作的管制信息,例如通信对象的 IP 地址、端口号、通信操作的进行状态等。原本套接字就只是一个概念而已,并不存在实体,如果肯定要赋予它一个实体,咱们能够说这些管制信息就是套接字的实体,或者说寄存管制信息的内存空间就是套接字的实体

套接字中记录了用于管制通信操作的各种管制信息,协定栈则须要依据这些信息判断下一步的口头,这就是套接字的作用。

在 Windows 中能够用 netstat 命令显示套接字内容,图中每一行相当于一个套接字。

比方第 8 行,它示意 PID 为 4 的程序正在应用 IP 地址为 10.10.1.16 的网卡与 IP 地址为 10.10.1.80 的对象进行通信。此外咱们还能够看出,本机应用 1031 端口,对方应用 139 端口。咱们再来看第 1 行,这一行示意 PID 为 984 的程序正在 135 端口期待另一方的连贯,其中本地 IP 地址和近程 IP 地址都是 0.0.0.0,这示意通信还没开始,IP 地址不确定。

调用 socket 时的操作

咱们的探索之旅将继续前进,看一看当浏览器调用 socket、connect 等 Socket 库中的函数时,协定栈外部是如何工作的。

首先是创立套接字的阶段。应用程序调用 socket 申请创立套接字,协定栈依据应用程序的申请执行创立套接字的操作。

在这个过程中,协定栈首先会调配用于寄存一个套接字所需的内存空间。套接字刚刚创立时,数据收发操作还没有开始,因而须要在套接字的内存空间中写入示意这一初始状态的管制信息。到这里,创立套接字的操作就实现了。

接下来,须要将示意这个套接字的描述符告知应用程序。收到描述符之后,应用程序在向协定栈进行收发数据委托时就须要提供这个描述符。因为套接字中记录了通信单方的信息以及通信处于怎么的状态,所以只有通过描述符确定了相应的套接字,协定栈就可能获取所有的相干信息,这样一来,应用程序就不须要每次都通知协定栈应该和谁进行通信了。

连贯服务器

创立套接字之后,应用程序(浏览器)就会调用 connect,随后协定栈会将本地的套接字与服务器的套接字进行连贯。

连贯是什么意思

那么这里的“连贯”到底是什么意思呢?一句话概括的话,连贯实际上是通信单方替换管制信息,在套接字中记录这些必要信息并筹备数据收发的一连串操作

套接字刚刚创立实现的时候,外面并没有寄存任何数据,也不晓得通信的对象是谁。因而,咱们须要把服务器的 IP 地址和端口号等信息告知协定栈,这是连贯操作的目标之一。

那么,服务器这边又是怎么的状况呢?服务器上也会创立套接字,但服务器上的协定栈和客户端一样,只创立套接字是不晓得应该和谁进行通信的。而且,和客户端不同的是,在服务器上,连应用程序也不晓得通信对象是谁。于是,咱们须要让客户端向服务器告知必要的信息。可见,客户端向服务器传播开始通信的申请,也是连贯操作的目标之一。

此外,当执行数据收发操作时,咱们还须要一块用来长期寄存要收发的数据的内存空间,这块内存空间称为缓冲区,它也是在连贯操作的过程中调配的。下面这些就是“连贯”这个词代表的具体含意。

负责保留管制信息的头部

对于管制信息,这里再补充一些。之前咱们说的管制信息其实能够大体上分为两类。

第一类是客户端和服务器互相联系时替换的管制信息。这些信息不仅连贯时须要,包含数据收发和断开连接操作在内,整个通信过程中都须要,这些内容在 TCP 协定的规格中进行了定义。具体来说,下表中的这些字段就是 TCP 规格中定义的管制信息。

这些字段是固定的,在连贯、收发、断开等各个阶段中,每次客户端和服务器之间进行通信时,都须要提供这些管制信息。具体来说,如图(a)所示,这些信息会被增加在客户端与服务器之间传递的网络包的结尾。在连贯阶段,因为数据收发还没有开始,所以如图(b)所示,网络包中没有理论的数据,只有管制信息。这些管制信息位于网络包的结尾,因而被称为头部。

管制信息还有另外一类,那就是保留在套接字中,用来控制协议栈操作的信息。应用程序传递来的信息以及从通信对象接管到的信息都会保留在这里,还有收发数据操作的执行状态等信息也会保留在这里,协定栈会依据这些信息来执行每一步的操作。

连贯操作的理论过程

连贯操作的第一步是客户端的 TCP 模块创立示意连贯管制信息的头部,通过 TCP 头部中的发送方和接管方端口号能够找到要连贯的套接字。而后,咱们将头部中的管制位的 SYN 比特设置为 1,目前大家能够认为它是示意连贯。此外还须要设置适当的序号和窗口大小。

当 TCP 头部创立好之后,接下来 TCP 模块会将信息传递给 IP 模块并委托它进行发送。IP 模块执行网络包发送操作后,网络包就会通过网络达到服务器,而后服务器上的 IP 模块会将接管到的数据传递给 TCP 模块,服务器的 TCP 模块依据 TCP 头部中的信息找到端口号对应的套接字。也就是说,从处于期待连贯状态的套接字中找到与 TCP 头部中记录的接管方端口号雷同的套接字就能够了。当找到对应的套接字之后,套接字中会写入相应的信息,并将状态改为正在连接。

上述操作实现后,服务器的 TCP 模块会返回响应,这个过程和客户端一样,须要在 TCP 头部中设置发送方和接管方端口号以及 SYN 比特。此外,在返回响应时还须要将 ACK 管制位设为 1,这示意曾经接管到相应的网络包。接下来,服务器 TCP 模块会将 TCP 头部传递给 IP 模块,并委托 IP 模块向客户端返回响应。

而后,网络包就会返回到客户端,通过 IP 模块达到 TCP 模块,并通过 TCP 头部的信息确认连贯服务器的操作是否胜利。如果 ACK 为 1 则示意连贯胜利,这时会向套接字中写入服务器的 IP 地址、端口号等信息,同时还会将状态改为连贯结束。相应地,客户端也须要将 ACK 比特设置为 1 并发回服务器,通知服务器方才的响应包曾经收到。当这个服务器收到这个返回包之后,连贯操作才算全副实现。

收发数据

当管制流程从 connect 回到应用程序之后,接下来就进入数据收发阶段了。

将 HTTP 申请音讯交给协定栈

数据收发操作是从应用程序调用 write 将要发送的数据交给协定栈开始的,协定栈收到数据后执行发送操作。应用程序在调用 write 时会指定发送数据的长度,在协定栈看来,要发送的数据就是肯定长度的二进制字节序列而已。

协定栈并不是一收到数据就马上发送进来,而是会将数据寄存在外部的发送缓冲区中,并期待应用程序的下一段数据。一次将多少数据交给协定栈是由应用程序自行决定的,协定栈并不能管制这一行为。在这样的状况下,如果一收到数据就马上发送进来,就可能会发送大量的小包,导致网络效率降落,因而须要在数据积攒到一定量时再发送进来。至于要积攒多少数据能力发送,不同品种和版本的操作系统会有所不同,不能一概而论,但都是依据上面几个因素来判断的。

第一个判断因素是每个网络包能包容的数据长度,协定栈会依据一个叫作 MTU 的参数来进行判断。MTU 示意一个网络包的最大长度,在以太网中个别是 1500 字节。MTU 是蕴含头部的总长度,因而须要从 MTU 减去头部的长度,而后失去的长度就是一个网络包中所能包容的最大数据长度,这一长度叫作 MSS。当从应用程序收到的数据长度超过或者靠近 MSS 时再发送进来,就能够防止发送大量小包的问题了。

另一个判断因素是工夫。当应用程序发送数据的频率不高的时候,如果每次都等到长度靠近 MSS 时再发送,可能会因为等待时间太长而造成发送提早,这种状况下,即使缓冲区中的数据长度没有达到 MSS,也应该果决发送进来。为此,协定栈的外部有一个计时器,当通过肯定工夫之后,就会把网络包发送进来。

判断因素就是这两个,但它们其实是互相矛盾的。如果长度优先,那么网络的效率会进步,但可能会因为期待填满缓冲区而产生提早;相同地,如果工夫优先,那么延迟时间会变少,但又会升高网络的效率。因而,在进行发送操作时须要综合思考这两个因素以达到均衡。

协定栈也给应用程序保留了管制发送机会的余地。应用程序在发送数据时能够指定一些选项,比方如果指定“不期待填满缓冲区间接发送”,则协定栈就会依照要求间接发送数据。像浏览器这种会话型的应用程序在向服务器发送数据时,期待填满缓冲区导致提早会产生很大影响,因而个别会应用间接发送的选项。

对较大的数据进行拆分

HTTP 申请音讯个别不会很长,一个网络包就能装得下,但如果其中要提交表单数据,长度就可能超过一个网络包所能包容的数据量。这种状况下,发送缓冲区中的数据就会超过 MSS 的长度,这时咱们当然不须要持续期待前面的数据了。发送缓冲区中的数据会被以 MSS 长度为单位进行拆分,拆分进去的每块数据会被放进独自的网络包中。

依据发送缓冲区中的数据拆分的状况,当判断须要发送这些数据时,就在每一块数据后面加上 TCP 头部,并依据套接字中记录的管制信息标记发送方和接管方的端口号,而后交给 IP 模块来执行发送数据的操作。

应用 ACK 号确认网络包已收到

TCP 具备确认对方是否胜利收到网络包,以及当对方没收到时进行重发的性能,因而在发送网络包之后,接下来还须要进行确认操作。

咱们先来看一下确认的原理,如图。首先,TCP 模块在拆分数据时,会先算好每一块数据相当于从头开始的第几个字节,接下来在发送这一块数据时,将算好的字节数写在 TCP 头部中,“序号”字段就是派在这个用场上的。而后,发送数据的长度也须要告知接管方,不过这个并不是放在 TCP 头部外面的,因为用整个网络包的长度减去头部的长度就能够失去数据的长度,所以接管方能够用这种办法来进行计算。有了下面两个数值,咱们就能够晓得发送的数据是从第几个字节开始,长度是多少了。

通过这些信息,接管方还可能查看收到的网络包有没有脱漏。例如,假如上次接管到第 1460 字节,那么接下来如果收到序号为 1461 的包,阐明两头没有脱漏;但如果收到的包序号为 2921,那就阐明两头有包脱漏了。如果确认没有脱漏,接管方会将到目前为止接管到的数据长度加起来,计算出一共曾经收到了多少个字节,而后将这个数值写入 TCP 头部的 ACK 号中发送给发送方。

然而,图中的例子和理论状况还是有些出入的。在理论的通信中,序号并不是从 1 开始的,而是须要用随机数计算出一个初始值,因而须要在开始收发数据之前将初始值告知通信对象。大家应该还记得在咱们方才讲过的连贯过程中,有一个将 SYN 管制位设为 1 并发送给服务器的操作,就是在这一步将序号的初始值告知对方的。实际上,在将 SYN 设为 1 的同时,还须要同时设置序号字段的值,而这里的值就代表序号的初始值。

TCP 数据收发是双向的,在客户端向服务器发送数据的同时,服务器也会向客户端发送数据,过程也相似。

咱们来总结一下理论的工作过程,如下图。首先,客户端在连贯时须要计算出与从客户端到服务器方向通信相干的序号初始值,并将这个值发送给服务器。接下来,服务器会通过这个初始值计算出 ACK 号并返回给客户端。同时,服务器也须要计算出与从服务器到客户端方向通信相干的序号初始值,并将这个值发送给客户端。接下来像方才一样,客户端也须要依据服务器发来的初始值计算出 ACK 号并返回给服务器。到这里,序号和 ACK 号都曾经筹备实现了,接下来就能够进入数据收发阶段了。数据收发操作自身是能够双向同时进行的,但 Web 中是先由客户端向服务器发送申请,序号也会追随数据一起发送。而后,服务器收到数据后再返回 ACK 号。从服务器向客户端发送数据的过程则正好相同。

TCP 采纳这样的形式确认对方是否收到了数据,在失去对方确认之前,发送过的包都会保留在发送缓冲区中。如果对方没有返回某些包对应的 ACK 号,那么就从新发送这些包。

依据网络包均匀往返工夫调整 ACK 号等待时间

后面说的只是一些基本原理,实际上网络的谬误检测和弥补机制非常复杂。上面来说几个要害的点,首先是返回 ACK 号的等待时间(这个等待时间叫超时工夫)。

当网络传输忙碌时就会产生拥塞,ACK 号的返回会变慢,这时咱们就必须将等待时间设置得略微长一点,否则可能会产生曾经重传了包之后,后面的 ACK 号才捷足先登的状况。这样的重传是多余的,而且对于原本就很拥塞的网络来说无疑是雪上加霜。那么等待时间是不是越长越好呢?也不是。如果等待时间过长,那么包的重传就会呈现很大的提早,也会导致网络速度变慢。

等待时间须要设为一个适合的值,不能太长也不能太短。依据服务器物理间隔的远近,ACK 号的返回工夫也会产生很大的稳定,而且咱们还必须思考到拥塞带来的影响。正因为稳定如此之大,所以将等待时间设置为一个固定值并不是一个好方法。因而,TCP 采纳了动静调整等待时间的办法,这个等待时间是依据 ACK 号返回所需的工夫来判断的。具体来说,TCP 会在发送数据的过程中继续测量 ACK 号的返回工夫,如果 ACK 号返回变慢,则相应缩短等待时间;绝对地,如果 ACK 号马上就能返回,则相应缩短等待时间

应用窗口无效治理 ACK 号

每发送一个包就期待一个 ACK 号的形式是最简略也最容易了解的,但在期待 ACK 号的这段时间中,如果什么都不做那切实太节约了。为了缩小这样的节约,TCP 采纳滑动窗口形式来治理数据发送和 ACK 号的操作。所谓滑动窗口,就是在发送一个包之后,不期待 ACK 号返回,而是间接发送后续的一系列包。

尽管这样做可能缩小期待 ACK 号时的工夫节约,但有一些问题须要留神。在一来一回形式中,接管方实现接管操作后返回 ACK 号,而后发送方收到 ACK 号之后才持续发送下一个包,因而不会呈现发送的包太多接管方解决不过去的状况。但如果不等返回 ACK 号就间断发送包,就有可能会呈现发送包的频率超过接管方解决能力的状况。

当接管方的 TCP 模块收到包后,会先将数据寄存到接收缓冲区中。而后,接管方须要计算 ACK 号,将数据块组装起来还原成本来的数据并传递给应用程序,如果这些操作还没实现下一个包就到了也不必放心,因为下一个包也会被暂存在接收缓冲区中。如果数据达到的速率比解决这些数据并传递给应用程序的速率还要快,那么接收缓冲区中的数据就会越堆越多,最初就会溢出。缓冲区溢出之后,前面的数据就进不来了,因而接管方就收不到前面的包了,也就意味着超出了接管方解决能力。咱们能够通过上面的办法来防止这种状况的产生。首先,接管方须要通知发送方本人最多能接管多少数据,而后发送方依据这个值对数据发送操作进行管制,这就是滑动窗口形式的基本思路。

在这张图中,接管方将数据暂存到接收缓冲区中并执行接管操作。当接管操作实现后,接收缓冲区中的空间会被释放出来,也就能够接管更多的数据了,这时接管方会通过 TCP 头部中的窗口字段将本人能接管的数据量告知发送方。这样一来,发送方就不会发送过多的数据,导致超出接管方的解决能力了。

接管方可能接管的最大数据量称为窗口大小,它是 TCP 调优参数中十分有名的一个。

ACK 与窗口的合并

要进步收发数据的效率,还须要思考另一个问题,那就是返回 ACK 号和更新窗口的机会。如果假设这两个参数是互相独立的,别离用两个独自的包来发送,后果会如何?

首先,什么时候须要更新窗口大小?当收到的数据刚刚开始填入缓冲区时,其实没必要每次都向发送方更新窗口大小,因为只有发送方在每次发送数据时减掉已发送的数据长度就能够自行计算出以后窗口的残余长度。

因而,更新窗口大小的机会应该是接管方从缓冲区中取出数据传递给应用程序的时候。这个操作是接管方应用程序发出请求时才会进行的,而发送方不晓得什么时候会进行这样的操作,因而 当接管方将数据传递给应用程序,导致接收缓冲区残余容量减少时,就须要告知发送方,这就是更新窗口大小的机会

那么 ACK 号又是什么状况?当接管方收到数据时,如果确认内容没有问题,就应该向发送方返回 ACK 号。

如果将后面两个因素联合起来看,每收到一个包,就须要向发送方别离发送 ACK 号和窗口更新这两个独自的包。这样一来,接管方发给发送方的包就太多了,导致网络效率降落。

因而,接管方在发送 ACK 号和窗口更新时,并不会马上把包发送进来,而是会期待一段时间,在这个过程中很有可能会呈现其余的告诉操作,这样就能够把两种告诉合并在一个包外面发送了。举个例子,在期待发送 ACK 号的时候正好须要更新窗口,这时就能够把 ACK 号和窗口更新放在一个包里发送。当须要间断发送多个 ACK 号时,也能够只发送最初一个 ACK 号,从而缩小包的数量。

接管 HTTP 响应音讯

浏览器发送 HTTP 申请音讯后,接下来还须要期待 Web 服务器返回响应音讯。对于响应音讯,浏览器须要进行接管操作,这一操作也须要协定栈的参加。

协定栈接收数据的具体操作过程能够简略总结如下:首先,协定栈会查看收到的数据块和 TCP 头部的内容,判断是否有数据失落,如果没有问题则返回 ACK 号。而后,协定栈将数据块暂存到接收缓冲区中,并将数据块按程序连接起来还原出原始的数据,最初将数据交给应用程序。将数据交给应用程序之后,协定栈还须要找到适合的机会向发送方发送窗口更新。

从服务器断开并删除套接字

数据发送结束后断开连接

毫无疑问,收发数据完结的工夫点应该是应用程序判断所有数据都曾经发送结束的时候。这时,数据发送结束的一方会发动断开过程,但不同的应用程序会抉择不同的断开机会。以 Web 为例,在 HTTP1.0 时代,服务器一方会在发送完响应后发动断开过程。

无论哪种状况,实现数据发送的一方会发动断开过程,这里咱们以服务器一方发动断开过程为例来进行解说。首先,服务器一方的应用程序会调用 Socket 库的 close 程序。而后,服务器的协定栈会生成蕴含断开信息的 TCP 头部,具体来说就是将管制位中的 FIN 比特设为 1。接下来,协定栈会委托 IP 模块向客户端发送数据。同时,服务器的套接字中也会记录下断开操作的相干信息。

当收到服务器发来的 FIN 为 1 的 TCP 头部时,客户端的协定栈会将本人的套接字标记为进入断开操作状态。而后,为了告知服务器已收到 FIN 为 1 的包,客户端会向服务器返回一个 ACK 号。过了一会儿,应用程序就会调用 read 来读取数据。这时,协定栈会告知应用程序(浏览器)来自服务器的数据曾经全副收到了。

因而,客户端应用程序会调用 close 来完结数据收发操作,这时客户端的协定栈也会和服务器一样,生成一个 FIN 比特为 1 的 TCP 包,而后委托 IP 模块发送给服务器。一段时间之后,服务器就会返回 ACK 号。到这里,客户端和服务器的通信就全副完结了。

删除套接字

和服务器的通信完结之后,用来通信的套接字也就不会再应用了,这时咱们就能够删除这个套接字了。不过,套接字并不会立刻被删除,而是会期待一段时间之后再被删除。

期待这段时间是为了避免误操作,引发误操作的起因有很多,上面来举一个最容易了解的例子。在 HTTP 1.1 中,客户端先发动断开,服务器返回 ACK 号;而后服务器也发送 FIN 申请断开,客户端返回 ACK 号。

如果最初客户端返回的 ACK 号失落了,后果会如何呢?这时,服务器没有接管到 ACK 号,可能会重发一次 FIN。如果这时客户端的套接字曾经删除了,会产生什么呢?套接字被删除,那么套接字中保留的管制信息也就跟着隐没了,套接字对应的端口号就会被释放出来。这时,如果别的应用程序要创立套接字,新套接字碰巧又被调配了同一个端口号,而服务器重发的 FIN 正好达到,会怎么样呢?原本这个 FIN 是要发给刚刚删除的那个套接字的,但新套接字具备雷同的端口号,于是这个 FIN 就会谬误地跑到新套接字外面,新套接字就开始执行断开操作了。之所以不马上删除套接字,就是为了避免这样的误操作。

至于具体期待多长时间,这和包重传的操作形式无关。协定中对于这个等待时间没有明确的规定,一般来说会期待几分钟之后再删除套接字。

数据收发操作小结

到这里,用 TCP 协定收发应用程序数据的操作就全副完结了。一图胜千言,下图形容了整个过程。

IP 与以太网的包收发操作

TCP 模块在执行连贯、收发、断开等各阶段操作时,都须要委托 IP 模块将数据封装成包发送给通信对象。咱们就来讨论一下 IP 模块是如何将包发送给对方的。

包的基本知识

包是由头部和数据两局部形成的。头部蕴含目标地址等管制信息,头部前面就是委托方要发送给对方的数据。

发送方的网络设备会负责创立包,创立包的过程就是生成含有正确管制信息的头部,而后再附加上要发送的数据。接下来,包会被发往最近的网络转发设施。当达到最近的转发设施之后,转发设施会依据头部中的信息判断接下来应该发往哪里。

这个过程须要用到一张表,这张表外面记录了每一个地址对应的发送方向,也就是依照头部里记录的目标地址在表里进行查问,并依据查到的信息判断接下来应该发往哪个方向。接下来,包在向目的地挪动的过程中,又会达到下一个转发设施,而后又会依照同样的形式被发往下一个转发设施。就这样,通过多个转发设施的接力之后,包最终就会达到接管方的网络设备。

网络中有路由器和集线器两种不同的转发设施,它们在传输网络包时有着各自的分工。

  • 路由器依据指标地址判断下一个路由器的地位
  • 集线器在子网中将网络包传输到下一个路由

集线器是依照以太网规定传输包的设施,而路由器是依照 IP 规定传输包的设施,因而咱们也能够作如下了解。

  • IP 协定依据指标地址判断下一个 IP 转发设施的地位
  • 子网中的以太网协定将包传输到下一个转发设施

TCP/IP 包蕴含两个头部:MAC 头部(用于以太网协定)和 IP 头部(用于 IP 协定)。这两个头部别离具备不同的作用。发送方将包的目的地,也就是要拜访的服务器的 IP 地址写入 IP 头部中。IP 协定就能够依据这一地址查找包的传输方向,从而找到下一个路由器的地位。接下来,IP 协定会委托以太网协定将包传输过来。这时,IP 协定会查找下一个路由器的以太网地址(MAC 地址),并将这个地址写入 MAC 头部中。这样一来,以太网协定就晓得要将这个包发到哪一个路由器上了。

网络包会通过路由器达到下一个路由器。这个过程一直反复,最终网络包就会被送到目的地,当目的地设施胜利接管之后,网络包的传输过程就完结了。

包收发操作概览

包收发操作的终点是 TCP 模块委托 IP 模块发送包的操作。这个委托的过程就是 TCP 模块在数据块的后面加上 TCP 头部,而后整个传递给 IP 模块,这部分就是网络包的内容。与此同时,TCP 模块还须要指定通信对象的 IP 地址。

收到委托后,IP 模块会将包的内容当作一整块数据,在后面加上蕴含管制信息的头部。方才咱们讲过,IP 模块会增加 IP 头部和 MAC 头部这两种头部。IP 头部中蕴含 IP 协定规定的、依据 IP 地址将包发往目的地所需的管制信息;MAC 头部蕴含通过以太网的局域网将包传输至最近的路由器所需的管制信息。

接下来,封装好的包会被交给网络硬件,例如以太网、无线局域网等。传递给网卡的网络包是由一连串 0 和 1 组成的数字信息,网卡会将这些数字信息转换为电信号或光信号,并通过网线(或光纤)发送进来,而后这些信号就会达到集线器、路由器等转发设施,再由转发设施一步一步地送达接管方。

包送达对方之后,对方会作出响应。返回的包也会通过转发设施发送回来,而后咱们须要接管这个包。接管的过程和发送的过程是相同的。

生成蕴含接管方 IP 地址的 IP 头部

IP 模块承受 TCP 模块的委托负责包的收发工作,它会生成 IP 头部并附加在 TCP 头部后面。IP 头部蕴含的内容如图所示,其中最重要的内容就是 IP 地址,它示意这个包应该发到哪里去。这个地址是由 TCP 模块告知的,而 TCP 又是在执行连贯操作时从应用程序那里取得这个地址的,因而这个地址的最后起源就是应用程序。

IP 头部中还须要填写发送方的 IP 地址。IP 地址实际上并不是调配给计算机的,而是调配给网卡的,因而当计算机上存在多块网卡时,每一块网卡都会有本人的 IP 地址。很多服务器上都会装置多块网卡,这时一台计算机就有多个 IP 地址,在填写发送方 IP 地址时就须要判断到底应该填写哪个地址。这个判断相当于在多块网卡中判断应该应用哪一块网卡来发送这个包,也就相当于判断应该把包发往哪个路由器,因而只有确定了指标路由器,也就确定了应该应用哪块网卡,也就确定了发送方的 IP 地址。

那么,咱们应该如何判断应该把包交给哪块网卡呢?其实和路由器应用 IP 表判断下一个路由器地位的操作是一样的。

这个“IP 表”叫作路由表,这里先简略介绍一下。如图所示,咱们能够通过 route print 命令来显示路由表。首先,咱们对套接字中记录的目的地 IP 地址与路由表左侧的 Network Destination 栏进行比拟,找到对应的一行。例如,TCP 模块告知的指标 IP 地址为 192.168.1.21,那么就对应图中的第 6 行,因为它和 192.168.1 的局部相匹配。

找到相应的条目之后,接下来看从左边数第 2 列和第 3 列的内容。右起第 2 列,也就是 Interface 列,示意网卡等网络接口,这些网络接口能够将包发送给通信对象。右起第 3 列,即 Gateway 列示意下一个路由器的 IP 地址,将包发给这个 IP 地址,该地址对应的路由器就会将包转发到指标地址。路由表的第 1 行中,指标地址和子网掩码都是 0.0.0.0,这示意默认网关,如果其余所有条目都无奈匹配,就会主动匹配这一行。

这样一来,咱们就能够判断出应该应用哪块网卡来发送包了,而后就能够在 IP 头部的发送方 IP 地址中填上这块网卡对应的 IP 地址。

生成以太网用的 MAC 头部

IP 模块在生成 IP 头部之后,会在它后面再加上 MAC 头部。MAC 头部是以太网应用的头部,它蕴含了接管方和发送方的 MAC 地址等信息。

在生成 MAC 头部时,只有设置上图中的 3 个字段就能够了。首先是“以太类型”,这里填写示意 IP 协定的值 0800。接下来是发送方 MAC 地址,这里填写网卡自身的 MAC 地址。MAC 地址是在网卡生产时写入 ROM 里的,只有将这个值读取进去写入 MAC 头部就能够了。

后面这些还比较简单,而接管方 MAC 地址就有点简单了,因为咱们还须要执行依据 IP 地址查问 MAC 地址的操作。

通过 ARP 查问指标路由器的 MAC 地址

这里咱们须要应用 ARP,它其实非常简单。在以太网中,有一种叫作播送的办法,能够把包发给连贯在同一以太网中的所有设施。ARP 就是利用播送对所有设施发问:“××这个 IP 地址是谁的?请把你的 MAC 地址通知我。”而后就会有人答复:“这个 IP 地址是我的,我的 MAC 地址是××××。”

如果对方和本人处于同一个子网中,那么通过下面的操作就能够失去对方的 MAC 地址。而后,咱们将这个 MAC 地址写入 MAC 头部,MAC 头部就实现了。

不过,如果每次发送包都要这样查问一次,网络中就会减少很多 ARP 包,因而 咱们会将查问后果放到一块叫作 ARP 缓存的内存空间中留着当前用。也就是说,在发送包时,先查问一下 ARP 缓存,如果其中曾经保留了对方的 MAC 地址,就间接应用 ARP 缓存中的地址,而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 查问。显示 ARP 缓存的办法如下。

有了 ARP 缓存,咱们能够缩小 ARP 包的数量,但如果总是应用 ARP 缓存中保留的地址也会产生问题。例如当 IP 地址发生变化时,ARP 缓存的内容就会和事实产生差别。为了避免这种问题的产生,ARP 缓存中的值在通过一段时间后会被删除,个别这个工夫在几分钟左右。这个删除的操作非常简单粗犷,不论 ARP 缓存中的内容是否无效,只有通过几分钟就全副删掉。当地址从 ARP 缓存中删除后,只有从新执行一次 ARP 查问就能够再次取得地址了。

下面这个策略可能在几分钟后打消缓存和事实的差别,但 IP 地址刚刚产生扭转的时候,ARP 缓存中仍然会保留旧的地址,这时就会产生通信的异样。

以太网的基本知识

实现 IP 模块的工作之后,上面就该轮到网卡了,不过在此之前,咱们先来理解一些以太网的基本知识。

以太网是一种为多台计算机可能彼此自在和廉价地互相通信而设计的通信技术,它的原型如图(a)所示。这种网络的实质其实就是一根网线,图上还有一种叫作收发器的小设施,它的性能只是将不同网线之间的信号连接起来而已。因而,当一台计算机发送信号时,信号就会通过网线流过整个网络,最终达到所有的设施。

不过,咱们无奈判断一个信号到底是发给谁的,因而须要在信号的结尾加上接收者的信息,也就是地址。这样一来就可能判断信号的接收者了,与接收者地址匹配的设施就接管这个包,其余的设施则抛弃这个包,这样咱们的包就送到指定的目的地了。为了管制这一操作,咱们就须要应用 MAC 头部。通过 MAC 头部中的接管方 MAC 地址,就可能晓得包是发给谁的;而通过发送方 MAC 地址,就可能晓得包是谁收回的;此外,通过以太类型就能够判断包外面装了什么类型的内容。

这个原型起初变成了图(b)中的构造。这个构造是将主干网线替换成了一个中继式集线器,将收发器网线替换成了双绞线。不过,尽管网络的构造有所变动,但信号会发送给所有设施这一根本性质并没有扭转。起初,图(c)这样的应用交换式集线器的构造遍及开来,当初咱们说的以太网指的都是这样的构造。这个构造看上去和(b)很像,但其实外面有一个重要的变动,即信号会发送给所有设施这一性质变了,当初信号只会流到依据 MAC 地址指定的设施,而不会达到其余设施了。

将 IP 包转换成电或光信号发送进来

上面来看看以太网的包收发操作。IP 生成的网络包只是寄存在内存中的一串数字信息,咱们须要将数字信息转换为电或光信号,能力在网线上传输。负责执行这一操作的是网卡,要管制网卡还须要网卡驱动程序。上面是一张网卡次要形成因素的概念图。

网卡并不是通上电之后就能够马上开始工作的,而是和其余硬件一样,都须要进行初始化。也就是说,关上计算机启动操作系统的时候,网卡驱动程序会对硬件进行初始化操作,而后硬件才进入能够应用的状态。其中包含在 MAC 模块中设置 MAC 地址。

给网络包再加 3 个控制数据

网卡驱动从 IP 模块获取包之后,会将其复制到网卡内的缓冲区中,而后向 MAC 模块发送发送包的命令。接下来就轮到 MAC 模块进行工作了。

首先,MAC 模块会将包从缓冲区中取出,并在结尾加上报头和起始帧分界符,在开端加上用于检测谬误的帧校验序列

报头是一串像 10101010…这样 1 和 0 交替呈现的比特序列,长度为 56 比特,它的作用是确定包的读取机会,SFD 则是用来确定帧的起始地位。

开端的 FCS(帧校验序列)用来查看包传输过程中因噪声导致的波形错乱、数据谬误,它是一串 32 比特的序列,是通过一个公式对包中从头到尾的所有内容进行计算而得进去的。在包传输过程中,如果受到噪声的烦扰而导致其中的数据产生了变动,那么接管方计算出的 FCS 和发送方计算出的 FCS 就会不同,这样咱们就能够判断出数据有没有谬误。

向集线器发送网络包

加上报头、起始帧分界符和 FCS 之后,咱们就能够将包通过网线发送进来了。发送信号的操作分为两种,一种是应用集线器的半双工模式,另一种是应用交换机的全双工模式。半双工模式须要思考信号的碰撞问题,不过当初所应用的基本上都是全双工模式。

首先,MAC 模块从报头开始将数字信息按每个比特转换成电信号,而后由 PHY,或者叫 MAU 的信号收发模块发送进来。将数字信息转换为电信号的速率就是网络的传输速率,例如每秒将 10Mbit 的数字信息转换为电信号发送进来,则速率就是 10 Mbit/s。

接下来,PHY(MAU)模块会将信号转换为可在网线上传输的格局,并通过网线发送进来。以太网规格中对不同的网线类型和速率以及其对应的信号格局进行了规定,但 MAC 模块并不关怀这些区别,而是将可转换为任意格局的通用信号发送给 PHY(MAU)模块,而后 PHY(MAU)模块再将其转换为可在网线上传输的格局。大家能够认为 PHY(MAU)模块的性能就是对 MAC 模块产生的信号进行格局转换。

接管返回包

咱们持续看看接管网络包时的操作过程。

信号的结尾是报头,通过报头的波形同步时钟,而后遇到起始帧分界符时开始将前面的信号转换成数字信息。首先,PHY(MAU)模块会将信号转换成通用格局并发送给 MAC 模块,MAC 模块再从头开始将信号转换为数字信息,并存放到缓冲区中。当达到信号的开端时,还须要查看 FCS。如果计算得出的 FCS 和包开端的 FCS 不统一,这个包就会被当作谬误包而被抛弃。

如果 FCS 校验没有问题,接下来就要看一下 MAC 头部中接管方 MAC 地址与网卡在初始化时调配给本人的 MAC 地址是否统一。如果不统一就间接抛弃,统一的话则将包放入缓冲区中。到这里,MAC 模块的工作就实现了,接下来网卡会通过中断机制告诉计算机收到了一个包。

将服务器的响应包从 IP 模块传递给 TCP

服务器返回的包的以太类型应该是 0800,因而网卡驱动会将其交给 TCP/IP 协定栈来进行解决。接下来就轮到 IP 模块先开始工作了,第一步是查看 IP 头部,确认格局是否正确。如果格局没有问题,下一步就是查看接管方 IP 地址。

如果接管方 IP 地址不是本人的地址,那肯定是产生了什么谬误。客户端计算机不负责对包进行转发,因而不应该收到不是发给本人的包。当产生这样的谬误时,IP 模块会通过 ICMP 音讯将谬误告知发送方。ICMP 规定了各种类型的音讯,如下图所示。当咱们遇到这个谬误时,IP 模块会通过 Destination unreachable 音讯告诉对方。

如果接管方 IP 地址正确,则这个包会被接管下来,这时还须要实现另一项工作。IP 协定有一个叫作分片的性能,如果接管到的包是通过分片的,那么 IP 模块会将它们还原成原始的包。

到这里,IP 模块的工作就完结了,接下来包会被交给 TCP 模块。TCP 模块会依据 IP 头部中的接管方和发送方 IP 地址,以及 TCP 头部中的接管方和发送方端口号来查找对应的套接字。找到对应的套接字之后,就能够依据套接字中记录的通信状态,执行相应的操作了。

UDP 协定的收发操作

不须要重发的数据用 UDP 发送更高效

大多数的应用程序都像之前介绍的一样应用 TCP 协定来收发数据,但当然也有例外。有些应用程序不应用 TCP 协定,而是应用 UDP 协定来收发数据。向 DNS 服务器查问 IP 地址的时候咱们用的也是 UDP 协定。上面就简略介绍一下 UDP 协定。

TCP 的工作形式十分复杂,为什么要设计得如此简单呢?因为咱们须要将数据高效且牢靠地发送给对方。为了实现可靠性,咱们就须要确认对方是否收到了咱们发送的数据,如果没有还须要再发一遍。

要实现下面的要求,最简略的办法是数据全副发送结束之后让接管方返回一个接管确认。这样一来,如果没收到间接全副从新发送一遍就好了,基本不必像 TCP 一样要治理发送和确认的进度。然而,如果漏掉了一个包就要全部重发一遍,怎么看都很低效。为了实现高效的传输,咱们要防止重发曾经送达的包,而是只重发那些出错的或者未送达的包。

不过,在某种状况下,即使没有 TCP 这样简单的机制,咱们也可能高效地重发数据,这种状况就是数据很短,用一个包就能装得下。如果只有一个包,就不必思考哪个包未送达了,因为全部重发也只不过是重发一个包而已。此外,咱们发送了数据,对方个别都会给出回复,只有将回复的数据当作接管确认就行了,也不须要专门的接管确认包了。

管制用的短数据

这种状况就适宜应用 UDP。像 DNS 查问等替换管制信息的操作基本上都能够在一个包的大小范畴内解决,这种场景中就能够用 UDP 来代替 TCP。UDP 没有 TCP 的接管确认、窗口等机制,因而在收发数据之前也不须要替换管制信息,也就是说不须要建设和断开连接的步骤,只有在从应用程序获取的数据后面加上 UDP 头部,而后交给 IP 进行发送就能够了。

接管也很简略,只有依据 IP 头部中的接管方和发送方 IP 地址,以及 UDP 头部中的接管方和发送方端口号,找到相应的套接字并将数据交给相应的应用程序就能够了。除此之外,UDP 协定没有其余性能了,遇到谬误或者丢包也一律不论。因为 UDP 只负责单纯地发送包而已,并不像 TCP 一样会对包的送达状态进行监控,所以协定栈也不晓得有没有产生谬误。但这样并不会引发什么问题,因而出错时就收不到来自对方的回复,应用程序会留神到这个问题,并从新发送一遍数据。

音频和视频数据

还有另一个场景会应用 UDP,就是发送音频和视频数据的时候。音频和视频数据必须在规定的工夫内送达,一旦送达晚了,就会错过播放机会,导致声音和图像卡顿。

如果像 TCP 一样通过接管确认响应来查看谬误并重发,重发的过程须要耗费肯定的工夫,因而重发的数据很可能曾经错过了播放的机会。一旦错过播放机会,重发数据也是没有用的,因为声音和图像曾经卡顿了,这是无法挽回的。

此外,音频和视频数据中短少了某些包并不会产生重大的问题,只是会产生一些失真或者卡顿而已,个别都是能够承受的。在这些无需重发数据,或者是重发了也没什么意义的状况下,应用 UDP 发送数据的效率会更高。

退出移动版