锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

《网络是怎样连接的》学习(二、协议栈和网卡)

时间:2022-09-25 20:30:00 卡连接器的固定装置卡用连接器装置

文章目录

  • 二、用电信号传输TCP/IP数据
    • 2.1 创建套接字
      • 2.1.1 协议栈的内部结构
    • 2.2.连接服务器
      • 2.2.什么是连接?
      • 2.2.2.负责保存控制信息的头部
      • 2.2.3.连接操作的实际过程
    • 2.3、收发数据
      • 2.3.1、将HTTP请求信息交给协议栈
      • 2.3.2.拆分较大的数据
      • 2.3.3、使用ACK确认已收到网络包
        • 2.3.3.1.数据单向传输
        • 2.3.3.二、数据双向传输
      • 2.3.4.根据网络包的平均往返时间调整 ACK 号等待时间
      • 2.3.5.有效管理窗口 ACK 号
      • 2.3.6、ACK与窗户合并
      • 2.3.7、接受HTTP响应消息
    • 2.4.从服务器中断开并删除套接字
      • 2.4.1.数据发送后断开
      • 2.4.2.删除套接字
      • 2.4.3.数据收发总结
    • 2.5、IP以太网包收发操作
      • 2.5.1.包的基本知识
      • 2.5.2.包收发操作概述
      • 2.5.生成包括接收方IP地址的IP头部
      • 2.5.4.生成以太网MAC头部
      • 2.5.5、通过 ARP 查询目标路由器 MAC 地址
      • 2.5.6.以太网的各种基本知识
      • 2.5.7、将IP将包转换成光或电信号并发送出去
      • 2.5.8.在网络包中添加3个控制数据
      • 2.5.9.将网络包发送到集线器
      • 2.5.10.接收回包
      • 2.5.11.从服务器响应包IP传递给TCP
    • 2.6、UDP协议的收发操作
      • 2.6.1.数据不需要重发UDP更高效
      • 2.6.2.控制短数据
      • 2.6.3.音频和视频数据

二、用电信号传输TCP/IP数据

2.1 创建套接字

2.1.1 协议栈的内部结构

协议栈网卡(网络硬件)
协议栈内部结构如下
在这里插入图片描述

  • 浏览器、邮件等应用程序收发数据时使用 TCP
  • DNS 查询等收发较短使用控制数据 UDP
  • IP协议控制网络包收发操作
  • IP协议包括ICMP(通知网络包传输过程中的错误和错误控制信息(这里的控制信息类似于我们在笔记本上记录的日程表和备忘录))协议和ARP(根据IP查询相应的以太网地址MAC地址)协议
  • MAC地址(符合 IEEE 规格的局域网设备采用相同格式的地址)
  • 协议栈是根据套接字中记录的控制信息来工作的。

①步操作:协议栈创建套接字首先是套接字开辟然后写入内存空间初始状态

2.2.连接服务器

2.2.什么是连接?

创建套接字之后,将调用应用程序(浏览器)connect,随后协议栈会将本地的套接字和服务器端的套接字进行连接。
实际上,连接是通信双方交换控制信息,在套接字记录这些必要的信息并准备一系列的数据收发操作。就像上面提到的客户端IP地址和端口号告知服务器的过程属于交换控制信息一个具体的例子。控制信息,就是用来控制数据收发操作所需的一些信息,IP 地址和端口号就是典型的例子。除此之外还有其他一些控制信息,我们后面会逐一进行介绍。连接操作中所交换的控制信息是根据通信规则来确定的,只要根据规则执行连接操作,双方就可以得到必要的信息从而完成数据收发的准备。此外,当执行数据收发操作时,我们还需要一块用来临时存放要收发的数据的内存空间,这个内存空间叫缓冲区,它也分布在连接操作过程中。**“连接”** 这个词代表有意义。

2.2.2.负责保存控制信息的头部

  • 第一类客户端服务器相互联络时交换控制信息头部是用来记录交换控制信息。

  • 第二类控制信息,那就是保存套接字用于控制协议栈操作的信息 在连接由于数据收发尚未开始,图2.4(b)在网络包中显示没有实际数据,只有控制信息.


通信在操作中使用控制信息分为两类。
(1)?头部中记录的信息
(2)?套接字记录在协议栈中的信息

2.2.3.连接操作的实际过程

  1. 连接操作的第一步是在 TCP 创建模块表示连接控制信息头部
  2. 通过 TCP 头部中的发送方接收方 端口号要连接的可以找到套接字

2.3、收发数据

2.3.1、将HTTP请求信息交给协议栈

协议栈会收到应用程序发送过来的数据包,这个数据包是一定长度的二进制字节数组。并且协议栈收到了消息不会而是马上发出去等待应用程序的下一段数据。在这样的情况下,如果一收到数据就马上发送出去,就可能会发送大量的小包,导致网络效率下降,因此需要在数据积累到一定量时再发送出去。

  1. 第一个判断要素是每个网络包能容纳的数据长度协议栈会根据一个叫作 MTUA 的参数来进行判断。
    MTU 表示一个网络包最大长度,在以太网中一般是 1500 字节(图 2.5)。MTU包含头部的总长度,因此需要从MTU 减去头部的长度,然后得到的长度就是一个网络包中所能容纳的最大数据长度,这一长度叫作 MSSC。当从应用程序收到的数据长度超过或者接近 MSS时再发送出去,就可以避免发送大量小包的问题了。
  2. 另一个判断要素是时间
    当应用程序发送数据的频率不高的时候,如果每次都等到长度接近 MSS 时再发送,可能会因为等待时间太长而造成发送延迟,这种情况下,即便缓冲区中的数据长度没有达到 MSS也应该果断发送出去。为此,协议栈的内部有一个计时器,当经过一定时间之后,就会把网络包发送出去 。
  3. 判断要素就是这两个,但它们其实是互相矛盾的。如果长度优先,那么网络的效率会提高,但可能会因为等待填满缓冲区而产生延迟;相反地,如果时间优先,那么延迟时间会变少,但又会降低网络的效率。因此,在进行发送操作时需要综合考虑这两个要素以达到平衡

2.3.2、对较大的数据进行拆分

  • HTTP 请求消息一般不会很长,一个网络包就能装得下,但如果其中要提交表单数据,长度就可能超过一个网络包所能容纳的数据量,比如在博客或者论坛上发表一篇长文就属于这种情况。
  • 这种情况下,发送缓冲区中的数据就会超过 MSS 的长度,这时我们当然不需要继续等待后面的数据了。发送缓冲区中的数据会被以 MSS 长度为单位进行拆分,拆分出来的每块数据会被放进单独的网络包中。
  • IP 模块会在网络包前面添加 IP 头部和以太网的 MAC 头部后发送网络包。

2.3.3、使用ACK确认网络包已收到

2.3.3.1、数据单向传递

  • 首先,TCP 模块在拆分数据时,会先算好每一块数据相当于从头开始的第几个字节,接下来在发送这一块数据时,将算好的字节数写在 TCP 头部中,“序号”字段就是派在这个用场上的。然后,发送数据的长度也需要告知接收方,不过这个并不是放在TCP 头部里面的,因为用整个网络包的长度减去头部的长度就可以得到数据的长度,所以接收方可以用这种方法来进行计算。有了上面两个数值,我们就可以知道发送的数据是从第几个字节开始,长度是多少了。
  • 通过这些信息,接收方还能够检查收到的网络包有没有遗漏。例如,假设上次接收到第 1460 字节,那么接下来如果收到序号为 1461 的包,说明中间没有遗漏;但如果收到的包序号为 2921,那就说明中间有包遗漏了。像这样,如果确认没有遗漏,接收方会将到目前为止接收到的数据长度加起来,计算出一共已经收到了多少个字节,然后将这个数值写入 TCP头部的 ACK 号中发送给发送方 A。简单来说,发送方说的是“现在发送的是从第 ×× 字节开始的部分,一共有 ×× 字节哦!”而接收方则回复说, “到第 ×× 字节之前的数据我已经都收到了哦!”这个返回 ACK 号的操作被称为确认响应,通过这样的方式,发送方就能够确认对方到底收到了多少数据

2.3.3.2、数据双向传递

首先客户端先计算出一个序号,然后将序号数据一起发送给服务器,服务器收到之后会计算 ACK(acknowledge character)确认符 号并返回给客户端;相反地,服务器也需要先计算出另一个序号,然后将序号和数据一起发送给客户端客户端收到之后计算 ACK 号并返回给服务器。此外,如图所示,客户端和服务器双方都需要各自计算序号,因此双方需要在连接过程中互相告知自己计算的序号初始值

  • 首先,客户端在连接时需要计算出与从客户端到服务器方向通信相关的序号初始值,并将这个值发送给服务器(图 2.9 ①)。接下来,服务器会通过这个初始值计算出 ACK 号并返回给客户端(图 2.9 ②)。初始值有可能在通信过程中丢失,因此当服务器收到初始值后需要返回 ACK 号作为确认。同时,服务器也需要计算出与从服务器到客户端方向通信相关的序号初始值,并将这个值发送给客户端(图 2.9②)。接下来像刚才一样,客户端也需要根据服务器发来的初始值计算出 ACK 号并返回给服务器(图 2.9 ③)。到这里,序号ACK 号都已经准备完成了,接下来就可以进入数据收发阶段了。数据收发操作本身是可以双向同时进行的,但 Web 中是先由客户端向服务器发送请求,序号也会跟随数据一起发送(图 2.9 ④)。然后,服务器收到数据后再返回 ACK 号(图 2.9 ⑤)。从服务器向客户端发送数据的过程则正好相反(图 2.9 ⑥⑦)。
  • TCP 采用这样的方式确认对方是否收到了数据,在得到对方确认之前,发送过的包都会保存在发送缓冲区中。如果对方没有返回某些包对应的 ACK 号,那么就重新发送这些包。
  • 这一机制非常强大。通过这一机制,我们可以确认接收方有没有收到某个包,如果没有收到则重新发送,这样一来,无论网络中发生任何错误,我们都可以发现并采取补救措施(重传网络包)。反过来说,有了这一机制,我们就不需要在其他地方对错误进行补救了。
  • 因此,网卡、集线器、路由器都没有错误补偿机制,一旦检测到错误就直接丢弃相应的包。应用程序也是一样,因为采用 TCP 传输,即便发生一些错误对方最终也能够收到正确的数据,所以应用程序只管自顾自地发送这些数据就好了。不过,如果发生网络中断、服务器宕机等问题,那么无论 TCP 怎样重传都不管用。这种情况下,无论如何尝试都是徒劳,因 此 TCP 会在尝试几次重传无效之后强制结束通信,并向应用程序报错。
  • 通过“序号”和“ACK 号”可以确认接收方是否收到了网络包。

2.3.4、根据网络包平均往返时间调整 ACK 号等待时间

2.3.5、使用窗口有效管理 ACK 号

如图 2.10(a)所示,每发送一个包就等待一个 ACK 号的方式是最简单也最容易理解的,但在等待 ACK 号的这段时间中,如果什么都不做那实在太浪费了。为了减少这样的浪费,TCP 采用图 2.10(b)这样的滑动窗口方式来管理数据发送和 ACK 号的操作。所谓滑动窗口,就是在发送一个包之后,不等待 ACK 号返回,而是直接发送后续的一系列包。这样一来,等待 ACK 号的这段时间就被有效利用起来了。

  • 关于滑动窗口的具体工作方式,还是看图更容易理解(图 2.11)。在这张图中,接收方将数据暂存到接收缓冲区中并执行接收操作。当接收操作完成后,接收缓冲区中的空间会被释放出来,也就可以接收更多的数据了,这时接收方会通过 TCP 头部中的窗口字段将自己能接收的数据量告知发送方。这样一来,发送方就不会发送过多的数据,导致超出接收方的处理能力了。
  • 此外,单从图上看,大家可能会以为接收方在等待接收缓冲区被填满之前似乎什么都没做,实际上并不是这样。这张图是为了讲解方便,故意体现一种接收方来不及处理收到的包,导致缓冲区被填满的情况。实际上,接收方在收到数据之后马上就会开始进行处理,如果接收方的性能高,处理速度比包的到达速率还快,缓冲区马上就会被清空,并通过窗口字段告知发送方。
  • 还有,图 2.11 中只显示了从右往左发送数据的操作,实际上和序号ACK 号一样,发送操作也是双向进行的。
  • 前面提到的能够接收的最大数据量称为窗口大小 ,它是 TCP 调优参数中非常有名的一个。

2.3.6、ACK和窗口的合并

  • 提高收发数据的效率,还需要考虑另一个问题,那就是返回 ACK号和更新窗口的时机。
  • 当收到的数据刚刚开始填入缓冲区时,其实没必要每次都向发送方更新窗口大小,因为只要发送方在每次发送数据时减掉已发送的数据长度就可以自行计算出当前窗口的剩余长度。因此,更新窗口大小的时机应该是接收方从缓冲区中取出数据传递给应用程序的时候。这个操作是接收方应用程序发出请求时才会进行的,而发送方不知道什么时候会进行这样的操作,因此当接收方将数据传递给应用程序,导致接收缓冲区剩余容量增加时,就需要告知发送方,这就是更新窗口大小的时机
  • 。当需要连续发送多个 ACK 号时,也可以减少包的数量,这是因为 ACK 号表示的是已收到的数据量,也就是说,它是告诉发送方目前已接收的数据的最后位置在哪里,因此当需要连续发送 ACK 号时,只要发送最后一个 ACK 号就可以了,中间的可以全部省略。当需要连续发送多个窗口更新时也可以减少包的数量,因为连续发生窗口更新说明应用程序连续请求了数据,接收缓冲区的剩余空间连续增加。这种情况和 ACK 号一样,可以省略中间过程,只要发送最终的结果就可以了。

2.3.7、接受HTTP响应消息

  • 首先,浏览器在委托协议栈发送请求消息之后,会调用 read 程序(之前的图 2.3 ④)来获取响应消息。然后,控制流程会通过 read 转移到协议栈 ,然后协议栈会执行接下来的操作。和发送数据一样,接收数据也需要将数据暂存到接收缓冲区中,这里的操作过程如下。首先,协议栈尝试从接收缓冲区中取出数据并传递给应用程序,但这个时候请求消息刚刚发送出去,响应消息可能还没返回。响应消息的返回还需要等待一段时间,因此这时接收缓冲区中并没有数据,那么接收数据的操作也就无法继续。这时,协议栈会将应用程序的委托,也就是从接收缓冲区中取出数据并传递给应用程序的工作暂时挂起 ,等服务器返回的响应消息到达之后再继续执行接收操作
  • 协议栈接收数据的具体操作过程:首先,协议栈会检查收到的数据块TCP 头部的内容,判断是否有数据丢失,如果没有问题则返回 ACK 号。然后,协议栈将数据块暂存接收缓冲区中,并将数据块按顺序连接起来还原出原始的数据,最后将数据交给应用程序。具体来说,协议栈会将接收到的数据复制到应用程序指定内存地址中,然后将控制流程交回应用程序。将数据交给应用程序之后,协议栈还需要找到合适的时机向发送方发送窗口更新

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

2.4.1、数据发送完毕后断开

  • 毫无疑问,收发数据结束的时间点应该是应用程序判断所有数据都已经发送完毕的时候。
  • 以 Web 为例,浏览器向 Web 服务器发送请求消息,Web 服务器再返回响应消息,这时收发数据的过程就全部结束了,服务器一方会发起断开过程 。当然,可能也有一些程序是客户端发送完数据就结束了,不用等服务器响应,这时客户端会先发起断开过程。这一判断是应用程序作出的,协议栈在设计上允许任何一方先发起断开过程。
  • 无论哪种情况,完成数据发送的一方会发起断开过程
  1. 首先,服务器一方的应用程序会调用 Socket 库的 close 程序。然后,服务器的协议栈会生成包含断开信息的 TCP 头部,具体来说就是将控制位中的 FIN 比特设为 1。接下来,协议栈会委托 IP 模块向客户端发送数据(图 2.12 ①)。同时,服务器的套接字中也会记录下断开操作的相关信息
  2. 客户端接收到服务器发送的FIN 为 1 的 TCP 头部时,客户端的协议栈会将自己的套接字标记为断开状态,为了告诉服务器接收到了FIN为1的包,会返回一个ACK号给服务器。这些操作完成之后,协议栈就可以等待应用程序来取数据了。
  3. 过了一会儿,应用程序调用read来读取数据,这时,协议栈 不会向应用程序传递数据,而是会告知应用程序来自服务器的数据已经全部收到。根据规则,服务器返回请求之后,Web通信操作就全部结束了,只要客户端收到服务器的响应数据,客户端的操作也随之结束了。因此,客户端应用程序会调用 close结束数据收发操作,这时客户端的协议栈也会和服务器一样,生成一个 FIN 比特为 1 的 TCP 包,然后委托 IP 模块发送给服务器(图 2.12 ③)。一段时间之后,服务器就会返回ACK 号(图 2.12 ④)。到这里,客户端和服务器的通信就全部结束了。

2.4.2、删除套接字

和服务器通信结束后,用来通信的套接字也不会再使用了,这时我们就可以删除该套接字了,不过套接字并不直接删除,而是过一段时间之后删除,等待这段时间是为了防止误操作。

2.4.3、数据收发小结

  1. 一般来说,服务器的一方应用程序在启动的时候就会创建套接字并进入等待状态,而客户端则需要等待用户触发特定操作,需要访问服务器的时候,才会创建套接字。在这阶段,还没有开始传输网络包。
  2. 创建套接字完成之后,客户端会生成一个SYN为1TCP包发给服务端,TCP包的头部包含客户端要传给服务端的数据的初始序列号,以及客户端发送数据时要用到的窗口大小(如图 2.11 所示,窗口大小是由接收方告知发送方的,因此,在最初的这个包中,客户端告诉服务器的窗口大小是服务器向客户端发送数据时使用的。窗口大小的更新和序号以及 ACK 号一样,都是双向进行的。图 2.13显示了窗口的双向交互。),当这个包到达服务器之后,服务器会返回一个 SYN 为 1 的 TCP 包(图 2.13 ②)。和图 2.13 ①一样,这个包的头部中也包含了序号窗口大小,此外还包含表示确认已收到包①的ACK 号 B。当这个包到达客户端时,客户端会向服务器返回一个包含表示确认的 ACK号的 TCP 包(图 2.13 ③)。到这里,连接操作就完成了,双方进入数据收发阶段
  3. 数据收发阶段的操作根据应用程序的不同而有一些差异,以 Web 为例,首先客户端会向服务器发送请求消息。TCP 会将请求消息切分成一定大小的块,并在每一块前面加上 TCP 头部,然后发送给服务器(图 2.13 ④)TCP 头部中包含序号,它表示当前发送的是几个字节的数据。当服务器收到数据时,会向客户端返回 ACK 号(图 2.13 ⑤)。在最初的阶段,服务器只是不断接收数据,随着数据收发的进行,数据不断传递给应用程序,接收缓冲区就会被逐步释放。这时,服务器需要将新的窗口大小告知客户端。当服务器收到客户端的请求消息后,会向客户端返回响应消息,这个过程和刚才的过程正好相反(图 2.13 ⑥⑦)
  4. 服务器的响应消息发送完毕之后,数据收发操作就结束了,这时就会开始执行断开操作。以 Web 为例,服务器会先发起断开过程 。在这个过程中,服务器先发送一个 FIN 为 1 的 TCP 包(图 2.13 ⑧),然后客户端返回一个表示确认收到的 ACK 号(图 2.13 ⑨)。接下来,双方还会交换一组方向相反的 FIN 为 1 的 TCP 包(图 2.13 ⑩)和包含 ACK 号的 TCP 包(图 2.13k)。最后,在等待一段时间后,套接字会被删除

2.5、IP与以太网的包收发操作

2.5.1、包的基本知识

  • TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成发送给通信对象。
  • 包是由头部数据两部分构成的:头部包含目的地址等控制信息,大家可以把它理解为快递包裹的面单;头部后面就是委
    托方要发送给对方的数据,也就相当于快递包裹里的货物
  • 首先,发送方的网络设备会负责创建包,创建包的过程就是生成含有正确控制信息的头部,然后再附加上要发送的数据。接下来,包会被发往最近的网络转发设备。当到达最近的转发设备之后,转发设备会根据头部中的信息判断接下来应该发往哪里。这个过程需要用到一张,这张表里面记录了每一个地址对应的发送方向,也就是按照头部里记录的目的地址在表里进行查询,并根据查到的信息判断接下来应该发往哪个方向
  • (1)路由器根据目标地址判断下一个路由器的位置
    (2)集线器在子网中将网络包传输到下一个路由
  • 集线器是按照以太网规则传输包的设备
  • 而路由器是按照 IP规则传输包的设备
  • (1)IP 协议根据目标地址判断下一个 IP 转发设备的位置
    (2)子网中的以太网协议将包传输到下一个转发设备
  1. (a)MAC 头部(用于以太网协议) (b)IP 头部(用于 IP 协议)
  2. 两个头部分别具有不同的作用。首先,发送方将包的目的地,也就是要访问的服务器的 IP 地址写入 IP 头部中。这样一来,我们就知道这个包应该发往哪里,IP 协议就可以根据这一地址查找包的传输方向,从而找到下一个路由器的位置,也就是图 2.16 中的路由器 R1。接下来,IP 协议会委托以太网协议将包传输过去。这时,IP 协议会查找下一个路由器的以太网地址(MAC 地址),并将这个地址写入 MAC 头部中。这样一来,以太网协议就知道要将这个包发到哪一个路由器上了。
  3. 网络包在传输过程中(图 2.16 ①)会经过集线器,集线器是根据以太网协议工作的设备。为了判断包接下来应该向什么地方传输,集线器里有一张表(用于以太网协议的表),可根据以太网头部中记录的目的地信息查出相应的传输方向。这张图中只有一个集线器,当存在多个集线器时,网络包会按顺序逐一通过这些集线器进行传输。
  4. 接下来,包会到达下一个路由器(图 2.16 ②)。路由器中有一张 IP 协议的表,可根据这张表以及 IP 头部中记录的目的地信息查出接下来应该发往哪个路由器。为了将包发到下一个路由器,我们还需要查出下一个路由器的 MAC 地址,并记录到 MAC 头部中,大家可以理解为改写了 MAC 头部 。这样,网络包就又被发往下一个节点了。

2.5.2、包收发操作概览

  • 包收发操作的起点是 TCP 模块委托 IP 模块发送包的操作(图 2.17 中 的“①发送”)。这个委托的过程就是 TCP 模块在数据块的前面加上 TCP头部,然后整个传递给 IP 模块,这部分就是网络包的内容。与此同时,TCP 模块还需要指定通信对象的 IP 地址,也就是需要写清楚“将什么内容发给谁”。收到委托后,IP 模块会将包的内容当作一整块数据,在前面加上包含控制信息的头部。刚才我们讲过,IP 模块会添加 IP 头部MAC 头部这两种头部。IP 头部中包含 IP 协议规定的、根据 IP 地址将包发往目的地所需的控制信息;MAC 头部包含通过以太网的局域网将包传输至最近的路由器所需的控制信息
  • 接收的过程和发送的过程是相反的,信息先以电信号的形式从网线传输进来,然后由网卡将其转换为数字信息并传递给 IP 模块(图 2.17 中的“③接收”)。接下来,IP 模块会将 MAC 头部和 IP 头部后面的内容,也就是 TCP 头部加上数据块,传递给 TCP 模块。接下来的操作就是我们之前讲过的 TCP 模块负责的部分了。
  • 无论要收发的包是控制包还是数据包,IP 对各种类型的包的收发操作都是相同的。

2.5.3、生成包含接收方IP地址的IP头部

  • IP 模块接受 TCP 模块的委托负责包的收发工作,它会生成 IP 头部并附加在 TCP 头部前面。IP 头部包含的内容如表 2.2 所示,其中最重要的内容就是 IP 地址,它表示这个包应该发到哪里去。这个地址是由 TCP 模块告知的,而 TCP 又是在执行连接操作时从应用程序那里获得这个地址的,因此这个地址的最初来源就是应用程序。IP 不会自行判断包的目的地,而是将包发往应用程序指定的接收方,即便应用程序指定了错误的 IP 地址,IP 模块也只能照做。
  • IP 头部中还需要填写发送方的 IP 地址,大家可以认为是发送方计算机的 IP 地址 ,实际上“计算机的 IP 地址”这种说法并不准确。一般的客户端计算机上只有一块网卡,因此也就只有一个 IP 地址,这种情况下我们可以认为这个 IP 地址就是计算机的 IP 地址,但如果计算机上有多个网卡,情况就没那么简单了。IP 地址实际上并不是分配给计算机的,而是分配网卡的,因此当计算机上存在多块网卡时,每一块网卡都会有自己的 IP 地 址。很多服务器上都会安装多块网卡,这时一台计算机就有多个 IP 地址,在填写发送方 IP 地址时就需要判断到底应该填写哪个地址。这个判断相当于在多块网卡中判断应该使用哪一块网卡来发送这个,也就相当于判断应该把包发往哪个路由器,因此只要确定了目标路由器,也就确定了应该使用哪块网卡,也就确定了发送方的 IP 地址
  • IP 头部的“接收方 IP 地址”填写通信对象的 IP 地址。
  • 发送方 IP 地址需要判断发送所使用的网卡,并填写该网卡的 IP地址。

例如,TCP 模块告知的目标 IP 地址为 192.168.1.21,那么就对应图 2.18 中的第 6 行,因为它和 192.168.1 的部分相匹配。如果目标 IP 地址为 10.10.1.166,那么就和 10.10.1 的部分相匹配,所以对应第 3 行。以此类推,我们需要找到与 IP 地址左边部分相匹配的条目 B,找到相应的条目之后,接下来看从右边数第 2 列和第 3 列的内容。右起第 2 列,也就是Interface 列,表示网卡等网络接口,这些网络接口可以将包发送给通信对象。此外,右起第 3 列,即 Gateway 列表示下一个路由器的 IP 地址,将包发给这个 IP 地址,该地址对应的路由器 A 就会将包转发到目标地址 B。路由表的第 1 行中,目标地址和子网掩码 A 都是 0.0.0.0,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行 B。这样一来,我们就可以判断出应该使用哪块网卡来发送包了,然后就可以在 IP 头部的发送方 IP 地址中填上这块网卡对应的 IP 地址。接下来还需要填写协议号,它表示包的内容是来自哪个模块的。例如,如果是 TCP 模块委托的内容,则设置为 06(十六进制),如果是 UDP 模块委托的内容,则设置为 17(十六进制),这些值都是按照规则来设置的。在现在我们使用的浏览器中,HTTP 请求消息都是通过 TCP 来传输的,因此这里就会填写表示 TCP 的 06(十六进制)

2.5.4、生成以太网用的MAC头部

  • 生成了 IP 头部之后,接下来 IP 模块还需要在 IP 头部前面加上MAC 头部(表 2.3)。IP 头部中的接收方 IP 地址表示网络包的目的地,通过这个地址我们就可以判断要将包发到哪里,但在以太网的世界中,TCP/IP 的这个思路是行不通的。以太网在判断网络包目的地时和 TCP/IP 的方式不同,因此必须采用相匹配的方式才能在以太网中将包发往目的地,而MAC 头部就是干这个用的。
  • 在生成 MAC 头部时,只要设置表 2.3 中的 3 个字段就可以了。方便起见,我们按照从下往上的顺序来对表进行讲解。首先是“以太类型”,这里填写表示 IP 协议的值 0800(十六进制)。接下来是发送方 MAC 地址,这里填写网卡本身的 MAC 地址。MAC 地址是在网卡生产时写入 ROM 里的,只要将这个值读取出来写入 MAC 头部就可以了 。对于多块网卡的情况,请大家回想一下设置发送方 IP 地址的方法 。设置发送方 IP 地址时,我们已经判断出了从哪块网卡发送这个包,那么现在只要将这块网卡对应的 MAC 地址填进去就好了。
  • 前面这些还比较简单,而接收方 MAC 地址就有点复杂了。只要告诉以太网对方的 MAC 的地址,以太网就会帮我们把包发送过去,那么很显然这里应该填写对方的 MAC 地址。然而,在这个时间点上,我们还没有把包发送出去,所以先得搞清楚应该把包发给谁,这个只要查一下路由表就知道了。在路由表中找到相匹配的条目,然后把包发给 Gateway 列中的IP 地址就可以了。
  • 既然已经知道了包应该发给谁,那么只要将对方的 MAC 地址填上去就好了,但到这里为止根本没有出现对方的 MAC 地址,也就是说我们现在根本不知道对方的 MAC 地址是什么。因此,我们还需要执行根据 IP 地址查询 MAC 地址的操作。

2.5.5、通过 ARP 查询目标路由器的 MAC 地址

ARP 就是利用广播所有设备提问:“×× 这个 IP 地址是谁的?请把你的 MAC 地址告诉我。”然后就会有人回答:“这个 IP 地址是我的,我的 MAC 地址是××××。”A(图 2.19)

  • 如果对方和自己处于同一个子网中,那么通过上面的操作就可以得到对方的 MAC 地址 。然后,我们将这个 MAC 地址写入 MAC 头部,MAC头部就完成了。不过,如果每次发送包都要这样查询一次,网络中就会增加很多 ARP包,因此我们会将查询结果放到一块叫作 ARP 缓存的内存空间中留着以后用。也就是说,在发送包时,先查询一下 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址,而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 查询。显 示 ARP 缓存的方法和 MAC 地址的写法如图 2.20 和图 2.21 所示
  • 有了 ARP 缓存,我们可以减少 ARP 包的数量,但如果总是使用 ARP缓存中保存的地址也会产生问题。例如当 IP 地址发生变化时,ARP 缓存的内容就会和现实发生差异。为了防止这种问题的发生,ARP 缓存中的值在经过一段时间后会被删除,一般这个时间在几分钟左右。这个删除的操作非常简单粗暴,不管 ARP 缓存中的内容是否有效,只要经过几分钟就全部删掉,这样就不会出问题了。当地址从 ARP 缓存中删除后,只要重新执行一次 ARP 查询就可以再次获得地址了。
  • 将 MAC 头部加在 IP 头部的前面,整个包就完成了。到这里为止,整个打包的工作是由 IP 模块负责的。有人认为,MAC 头部是以太网需要的内容,并不属于 IP 的职责范围,但从现实来看,让 IP 负责整个打包工作是有利的。如果在交给网卡之前,IP 模块能够完成整个打包工作,那么网卡只要将打好的包发送出去就可以了。对于除 IP 以外的其他类型的包也是一样,如果在交给网卡之前完成打包,那么对于网卡来说,发送的操作和发送 IP 包是完全相同的。这样一来,同一块网卡就可以支持各种类型的包。至于接收操作,我们到后面会讲,但如果接收的包可以原封不动直接交给 IP 模块来处理,网卡就只要负责接收就可以了。这样一来,一块网卡也就能支持各种类型的包了。与其机械地设计模块和设备之间的分工,导致网卡只能支持 IP 包,不如将分工设计得现实一些,让网卡能够灵活支持各种类型的包。

2.5.6、以太网的各种基本知识

从图上不难看出,这种网络的本质其实就是一根网线

  • 因此,当一台计算机发送信号时,信号就会通过网线流过整个网络,最终到达所有的设备。这就好像所有人待在一个大房间里,任何一个人说话,所有人都能够听到,同样地,这种网络中任何一台设备发送的信号所有设备都能接收到。不过,我们无法判断一个信号到底是发给谁的,因此需要在信号的开头加上接收者的信息,也就是地址。这样一来就能够判断信号的接收者了,与接收者地址匹配的设备就接收这个包,其他的设备则丢弃这个包,这样我们的包就送到指定的目的地了。为了控制这一操作,我们就需要使用表 2.3 中列出的 MAC 头部。通过 MAC 头部中的接收方 MAC 地址,就能够知道包是发给谁的;而通过发送方 MAC 地址,就能够知道包是谁发出的;此外,通过以太类型就可以判断包里面装了什么类型的内容。以太网其实就这么简单 。
  • 这个原型后来变成了图 2.22(b)中的结构。这个结构是将主干网线替换成了一个中继式集线器 ,将收发器网线替换成了双绞线 。不过,虽然网络的结构有所变化,但信号会发送给所有设备这一基本性质并没有改变
  • 后来,图 2.22(c)这样的使用交换式集线器 的结构普及开来,现在我们说的以太网指的都是这样的结构。这个结构看上去和(b)很像,但其实里面有一个重要的变化,即信号会发送给所有设备这一性质变了,现在信号只会流到根据 MAC 地址指定的设备,而不会到达其他设备了。当然,根据MAC地址来传输包这一点并没有变,因此 MAC 头部的设计也得以保留
  • 尽管以太网经历了数次变迁,但其基本的 3 个性质至今仍未改变,即将包发送到 MAC 头部的接收方 MAC 地址代表的目的地,用发送方 MAC地址识别发送方,用以太类型识别包的内容。因此,大家可以认为具备这3 个性质的网络就是以太网 。
  • 此外,以太网和 IP 一样,并不关心网络包的实际内容,因此以太网的收发操作也和 TCP 的工作阶段无关,都是共通的

2.5.7、将IP包转换成光或电信号发送出去

网卡并不是通上电之后就可以马上开始工作的,而是和其他硬件一样,都需要进行初始化。也就是说,打开计算机启动操作系统的时候,网卡驱动程序会对硬件进行初始化操作,然后硬件才进入可以使用的状态。这些操作包括硬件错误检查、初始设置等步骤,这些步骤对于很多其他硬件也是共通的,但也有一些操作是以太网特有的,那就是在控制以太网收发操作的 MAC 模块中设置 MAC 地址。网卡的 ROM 中保存着全世界唯一的 MAC 地址,这是在生产网卡写入的,将这个值读出之后就可以对 MAC 模块进行设置,MAC 模块就知道自己对应的 MAC 地址了。也有一些特殊的方法,比如从命令或者配置文件中读取 MAC 地址并分配给 MAC 模块 。这种情况下,网卡会 忽略 ROM 中的 MAC 地址。有人认为在网卡通电之后,ROM 中的 MAC 地址就自动生效了,其实不然,真正生效的是网卡驱动进行初始化时在 MAC模块中设置的那个 MAC 地址 。在操作系统启动并完成这些初始化操作之后,网卡就可以等待来自 IP 的委托了。

  • 网卡的 ROM 中保存着全世界唯一的 MAC 地址,这是在生产网卡时写入的。
  • 网卡中保存的 MAC 地址会由网卡驱动程序读取并分配给 MAC模块。

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

  • 好了,下面来看一看网卡是如何将包转换成电信号并发送到网线中的。网卡驱动从 IP 模块获取包之后,会将其复制到网卡内的缓冲区中,然后向MAC 模块发送发送包的命令。接下来就轮到 MAC 模块进行工作了。
  • 首先,MAC 模块会将包从缓冲区中取出,并在开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列(图 2.24)。

    报头是一串像 10101010…这样 1 和 0 交替出现的比特序列,长度为 56比特,它的作用是确定包的读取时机。当这些 1010 的比特序列被转换成电信号后,会形成如图 2.25 这样的波形。接收方在收到信号时,遇到这样的波形就可以判断读取数据的时机。关于这一块内容,我们得先讲讲如何通过电信号来读取数据。
  • 用电信号来表达数字信息时,我们需要让 0 和 1 两种比特分别对应特定的电压电流,例如图 2.26(a)这样的电信号就可以表达数字信息。通过电信号来读取数据的过程就是将这种对应关系颠倒过来。也就是说,通过测量信号中的电压和电流变化,还原出 0 和 1 两种比特的值。然而,实际的信号并不像图 2.26 所示的那样有分隔每个比特的辅助线,因此在测量电压和电流时必须先判断出每个比特的界限在哪里。但是,像图 2.26(a)右边这种 1 和 0 连续出现的信号,由于电压和电流没有变化,我们就没办法判断出其中每个比特到底应该从哪里去切分。
  • 要解决这个问题,最简单的方法就是在数据信号之外再发送一组用来区分比特间隔时钟信号。如图 2.26(b)所示,当时钟信号从下往上变化时 A 读取电压和电流的值,然后和 0 或 1 进行对应就可以了。但是这种方法存在问题。当距离较远,网线较长时,两条线路的长度会发生差异,数据信号和时钟信号的传输会产生时间差,时钟就会发生偏移
  • 要解决这个问题,可以采用将数据信号时钟信号叠加在一起的方法。这样的信号如图 2.26(c)所示,发送方将这样的信号发给接收方。由于时钟信号是像图 2.26(b)这样按固定频率进行变化的,只要能够找到这个变化的周期,就可以从接收到的信号(c)中提取出时钟信号(b),进而通过接收信号(c)和时钟信号(b)计算出数据信号(a),这和发送方将数据信号和时钟信号进行叠加的过程正好相反。然后,只要根据时钟信号(b)的变化周期,我们就可以从数据信号(a)中读取相应的电压和电流值,并将其还原为 0 或 1 的比特了。
  • 这里的重点在于如何判断时钟信号的变化周期。时钟信号是以 10 Mbit/s或者 100 Mbit/s 这种固定频率进行变化的,就像我们乘坐自动扶梯一样,只要对信号进行一段时间的观察,就可以找到其变化的周期。因此,我们不能一开始就发送包的数据,而是要在前面加上一段用来测量时钟信号的特殊信号,这就是报头的作用 。
  • 以太网根据速率和网线类型的不同分为多种派生方式,每种方式的信号形态也有差异,并不都是像本例中讲的这样,单纯通过电压和电流来表达 0 和 1 的。因此,101010…这样的报头数字信息在转换成电信号后,其波形也不一定都是图 2.25 中的那个样子,而是根据方式的不同而不同。但是,报头的作用和基本思路是一致的。
  • 报头后面的起始帧分界符在图 2.25 中也已经画出来了,它的末尾比特排列有少许变化。接收方以这一变化作为标记,从这里开始提取网络包数据。也就是说,起始帧分界符是一个用来表示包起始位置的标记。末尾的 FCS(帧校验序列)用来检查包传输过程中因噪声导致的波形紊乱、数据错误,它是一串 32 比特的序列,是通过一个公式对包中从头到尾的所有内容进行计算而得出来的。具体的计算公式在此省略,它和磁盘等设备中使用的 CRCA 错误校验码是同一种东西,当原始数据中某一个比特发生变化时,计算出来的结果就会发生变化。在包传输过程中,如果受到噪声的干扰而导致其中的数据发生了变化,那么接收方计算出的 FCS发送方计算出的 FCS 就会不同,这样我们就可以判断出数据有没有错误

2.5.9、向集线器发送网络包

  • 加上报头起始帧分界符FCS 之后,我们就可以将包通过网线发送出去了(图 2.24)。发送信号的操作分为两种,一种是使用集线器的半双工模式,另一种是使用交换机的全双工模式
  • 发送和接收同时并行的方式叫作“全双工”,相对地,某一时刻只能进行发送或接收其中一种操作的叫作“半双工”。
  • 半双工模式中,为了避免信号碰撞,首先要判断网线中是否存在其他设备发送的信号。如果有,则需要等待该信号传输完毕,因为如果在有信号时再发送一组信号,两组信号就会发生碰撞。当之前的信号传输完毕,或者本来就没有信号在传输的情况下,我们就可以开始发送信号了。首先,MAC 模块从报头开始将数字信息按每个比特转换成电信号,然后由 PHY,或者叫 MAU 的信号收发模块发送出去。在这里,将数字信息转换为电信号的速率就是网络的传输速率,例如每秒将 10 Mbit 的数字信息转换为电信号发送出去,则速率就是 10 Mbit/s。
  • 根据以太网信号方式的不同,有些地方叫 MAU(Medium Attachment Unit,介质连接单元),有些地方叫 PHY(Physical Layer Device,物理层装置)。在速率为 100 Mbit/s 以上的以太网中都叫 PHY。
  • 接下来,PHYMAU)模块会将信号转换为可在网线上传输的格式,并通过网线发送出去。以太网规格中对不同网线类型速率以及其对应的信号格式进行了规定,但 MAC 模块并不关心这些区别,而是将可转换为任意格式的通用信号发送给 PHY(MAU)模块,然后 PHY(MAU)模块再将其转换为可在网线上传输的格式。大家可以认为 PHY(MAU)模块的功能就是对 MAC 模块产生的信号进行格式转换。当然,以太网还有很多不同的派生方式,网线传输的信号格式也有各种变化。此外,实际在网线中传输的信号很复杂,我们无法一一介绍,但是如果一点都不讲,大家可能对此难以形成一个概念,所以就举一个例子,大家感受一下就好。 图 2.27 就是这样一个例子,我们这里就不详细解释了,总之,网线中实际传输的信号就是这个样子的。
  • 网卡的MAC模块生成通用信号,由PHY(MAU)模块转化成可以在网线中传输的格式,并通过网线发送出去。
  • PHY(MAU)的职责并不是仅仅是将 MAC 模块传递过来的信号通过网线发送出去,它还需要监控接收线路中有没有信号进来。在开始发送信号之前,需要先确认没有其他信号进来,这时才能开始发送。如果在信号开始发送到结束发送的这段时间内一直没有其他信号进来,发送操作就成功完成了。以太网不会确认发送的信号对方有没有收到。根据以太网的规格,两台设备之间的网线不能超过 100 米 ,在这个距离内极少会发生错误,万一 B 发生错误,协议栈的 TCP 也会负责搞定,因此在发送信号时没有必要检查错误。
  • 在发送信号的过程中,接收线路不应该有信号进来,但情况并不总是尽如人意,有很小的可能性出现多台设备同时进行发送操作的情况。如果有其他设备同时发送信号,这些信号就会通过接收线路传进来。
  • 在使用集线器的半双工模式中,一旦发生这种情况,两组信号就会相互叠加,无法彼此区分出来,这就是所谓的信号碰撞。这种情况下,继续发送信号是没有意义的,因此发送操作会终止。为了通知其他设备当前线路已发生碰撞,还会发送一段时间的阻塞信号 ,然后所有的发送操作会全部停止
  • 等待一段时间之后,网络中的设备会尝试重新发送信号。但如果所有设备的等待时间都相同,那肯定还会发生碰撞,因此必须让等待的时间相互错开。具体来说,等待时间是根据 MAC 地址生成一个随机数计算出来的。
  • 网络拥塞时,发生碰撞的可能性就会提高,重试发送的时候可能又会和另外一台设备的发送操作冲突,这时会将等待时间延长一倍,然后再次重试。以此类推,每次发生碰撞就将等待时间延长一倍最多重试 10次,如果还是不行就报告通信错误
  • 全双工模式中,发送和接收可以同时进行不会发生碰撞。因此,全双工模式中不需要像半双工模式这样考虑这么多复杂的问题,即便接收线路中有信号进来,也可以直接发送信号。

2.5.10、接收返回包

  • 在使用集线器的半双工模式以太网中,一台设备发送的信号会到达连接在集线器上的所有设备。这意味着无论是不是发给自己的信号都会通过接收线路传进来,因此接收操作的第一步就是不管三七二十一把这些信号全都收进来再说。
  • 信号的开头是报头,通过报头的波形同步时钟,然后遇到起始帧分界符时开始将后面的信号转换成数字信息。这个操作和发送时是相反的,即PHY(MAU)模块先开始工作, 然后再轮到 MAC 模 块。 首 先,PHY(MAU)模块会将信号转换成通用格式并发送给 MAC 模块,MAC 模块再从头开始将信号转换为数字信息,并存放到缓冲区中。当到达信号的末尾时,还需要检查 FCS。具体来说,就是将从包开头结尾的所有比特套用到公式中计算出 FCS,然后和包末尾FCS 进行对比,正常情况下两者应该是一致的,如果中途受到噪声干扰而导致波形发生紊乱,则两者的值会产生差异,这时这个包就会被当作错误包而被丢弃。
  • 如果 FCS 校验没有问题,接下来就要看一下 MAC 头部接收方MAC 地址与网卡在初始化时分配给自己的 MAC 地址是否一致,以判断这个包是不是发给自己的。我们没必要去接收发给别人的包,因此如果不是自己的包就直接丢弃,如果接收方 MAC 地址和自己 MAC 地址一致,则将包放入缓冲区中。到这里,MAC 模块的工作就完成了,接下来网卡会通知计算机收到了一个包。
  • 通知计算机的操作会使用一个叫作中断的机制。在网卡执行接收包的操作的过程中,计算机并不是一直监控着网卡的活动,而是去继续执行其他的任务。因此,如果网卡不通知计算机,计算机是不知道包已经收到了这件事的。网卡驱动也是在计算机中运行的一个程序,因此它也不知道包到达的状态。在这种情况下,我们需要一种机制能够打断计算机正在执行的任务,让计算机注意到网卡中发生的事情,这种机制就是中断
  • 具体

相关文章