《网络是怎样连接的》——第二章:用电信号传输TCP/IP数据

2.1创建套接字
2.2连接服务器
2.3收发数据
2.4从服务器断开并删除套接字
2.5IP与以太网的包收发操作
2.6UDP协议的收发操作

2.1创建套接字

2.1.1协议栈的内部结构

协议栈采用分层模式,其内部结构如图所示:
在这里插入图片描述
总体来说,上层将工作委派给下层,下层接受并执行。
图中最上面的部分时网络应用程序,也就是浏览器,电子邮件客户端,we b服务器,电子邮件服务器等程序。它们会把收发数据的工作委派给下层来完成。

应用程序下面的是Socket库,,其中包括解析器,解析器用来向DNS服务器发出查询。

再下面是操作系统的内部了,其中协议栈部分包括三部分,用于收发数据的TCP/UDP协议,其中浏览器,邮件等一般的应用程序使用TCP,像DNS查询等收发较短的控制数据则使用UDP。接着是IP协议,ICMP,ARP等协议。

协议栈下面是网卡驱动程序,负责控制网卡硬件,最下面的网卡负责完成实际的收发操作,也就是对网线中的信号执行发送和接收的操作。

2.1.2套接字的本质

在协议栈内部有一块用于存放控制信息的内存空间,这里记录了用于控制通信操作的控制信息,例如通信对象的IP地址,端口号,通信操作的进行状态等。其实,套接字的实体(本质):就是存放控制信息的内存空间。

协议栈在执行操作时需要参阅这些控制信息。例如:在发送数据时,需要看一看套接字中的通信对象IP地址和端口号,以便向指定的IP地址和端口号发送数据。在发送数据之后,协议栈需要等待对方返回收到的数据的响应信息,但数据可能中途丢失,因此,需要在套接字中记录是否已经收到了响应以及发送数据后经过了多长的时间,才能根据这些信息按照需要重发。

套接字中记录了用于控制通信操作的各种控制信息,协议栈根据这些信息判断下一步的行动。

windows中显示套接字内容的命令:netstat-ano,其显示的具体内容如下图所示:
在这里插入图片描述
图中第8行表示:PID为4的程序正在使用IP地址为10.10.1.16的网卡与IP地址为10.10.1.18的对象进行通信。此外还可以看出,本机使用1031端口号,对方使用139端口,而139端口时windows文件服务器使用的端口,因此,我们可以知道这个套接字是连接到一台文件服务器的。
图中第1行表示PID为984的程序正在135端口等待另一方的连接,其中本地IP地址和远程IP地址都是0.0.0.0,这表示通信还没开始,IP地址不确定。

2.1.3调用socket时的操作

浏览器调用Socket库中的socket和connect等程序组件时委托协议栈中TCP协议收发数据的过程如下图所示:
在这里插入图片描述
首先是创建套接字的阶段,如上图的(1)所示。应用程序调用socket申请创建套接字,协议栈根据应用程序的申请执行创建套接字的操作。

在这个过程中,协议栈首先会分配用于存放一个套接字的所需的内存空间,用于记录套接字控制信息的内存空间并不是一开始就存在的,因此在创建套接字时,需要在套接字的内存空间中写入表示这一初始状态的控制信息,至此,套接字创建完成。(为套接字分配内存,并写入初始状态)

接着,将表示这个套接字的描述符告知应用程序。(描述符相当于用来区分协议栈中的多个套接字的号码牌)

收到描述符之后,应用程序在协议栈进行收发数据时就需要通过这个描述符。由于套接字中记录了通信双方的信息以及通信处于怎样的状态,所以只要通过描述符确定了相应的套接字,协议栈就能够获取所有的相关信息,应用程序也就知道每次应该和谁通信。

2.2连接服务器

2.2.1连接的含义

本质:连接实际上通信双方交换控制信息,在套接字中记录这些必要信息并准备数据收发的一连串操作。
含义:
(1):把服务器的IP地址和端口号等信息告知协议栈
(2):客户端将必要的信息告知服务器端(即客户端向服务器传达开始通信的请求)
(3)分配一块用于缓存收发数据的内存空间——缓冲区

2.2.2负责控制信息的头部

控制信息大概分为两类:一类保存在协议的首部,一类保存在套接字中。

第一类是客户端和服务器相互联络时交换的控制信息,这些信息不仅连接时需要,包括数据收发和断开连接操作在内,整个通信过程中都需要。这些内容保存在TCP头部,如下图所示:
在这里插入图片描述
在连接阶段,由于数据收发还没有开始,网络包中没有实际的数据,只有控制信息,这些控制信息位于网络包的头部,在这里,即称为TCP头部。如下图所示:在这里插入图片描述

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

2.2.3连接操作的实际过程

这个过程是从应用程序调用Socket库的connect开始的,即:connect(<描述符>,<服务器IP地址和端口号>,…)

这个调用提供了服务器的IP地址和端口号,这些信息会传递给协议栈中的TCP模块。然后TCP模块会与该IP地址对应的对象,也就是与服务器的TCP模块交换控制信息。这一交互过程包括以下几个步骤;

首先:客户端会创建一个表示包含表示开始数据收发操作的控制信息的头部。这样,客户端(发送方)的套接字就准确找到了服务器(接收发)的套接字,也就是搞清了应该连接哪个套接字。
然后,将头部中的控制位的SYN比特设置为1,表示连接。此外还需设置适当的序号和窗口大小。

TCP头部创建好之后,TCP模块将信息委托给IP模块发送,服务器上的IP模块会将接收的数据传递给TCP模块,服务器的TCP模块根据TCP头部中的信息找到端口号对应的套接字,也就是说,从处于等待连接状态的套接字中找到与TCP头部记录的端口号相同的套接字。找到后对应的套接字后,套接字中会写入相应的信息,并将状态改为正在连接。

接着,服务器的TCP模块会返回响应,即在TCP头部中设置发送方和接收方端口号以及SYN比特,此外,在返回响应时还需将ACK控制位设为1,表示已经收到相应的网络包。这时服务器TCP模块会将TCP头部传递给IP模块,并委托IP模块向客户端返回响应。

然后,网络包就会返回到客户端,通过IP模块到达TCP模块,并通过TCP头部的信息确认连接服务器的操作是否成功。如果SYN为1则表示连接成功,这时会向套接字中写入服务器的IP地址,端口号等信息,同时还会将状态改为连接完毕。

最后,之前服务器返回响应时将ACK比特设置为1,相应地,客户端也需要将ACK比特设置为1并发回服务器,告诉服务器刚才的响应的包已经收到,当服务器收到这个返回包后,连接操作才算全部完成。此时,套接字就进入了随时可以收发数据的状态了。

2.3收发数据

2.3.1将THHP请求消息交给协议栈

数据收发操作就是应用程序调用write将要发送的数据交给协议栈开始的,协议栈收到数据后执行发送操作,主要包括以下几点:

首先,协议栈不关心应用程序发送来的数据是什么内容,在协议栈看来,要发送的数据就是一定长度的二进制字节序列而已。应用程序在发送时会指定发送数据的长度。

其次:协议栈收到数据后不一定马上发送,而是将数据存放在内部的发送缓冲区并等待应用程序的下一段数据。当数据积累到一定量时再发送,具体积累到多少才发送根据以下两个要素判断:

第一个要素是每个网络包能够容纳的数据长度,即MTU值。MTU表示网络包的最大长度,在以太网中一般是1500字节。MTU是包含头部的总长度,因此需要从MTU减去头部的长度,然后得到的长度就是一个网络包中所能容纳的最大数据长度,叫做MSS。当从应用程序收到的数据长度超过了或者接近MSS时再发送可以避免发送大量表包的问题。
第二个要素时间:当应用程序发送数据的频率不高的时候,如果每次都等到长度接近MSS时再发送,可能会因为等待时间太长而造成发送延迟,这种情况下,即便缓冲区中的数据长度没有达到MSS,也应该果断发送。为此,协议栈的内部有一个计时器,当经过一定时间之后,就会把网络包发送出去。

这两个要素相互矛盾。如果长度优先,网络的效率就会提高,但可能因为等待填满缓冲区而产生延迟,相反地,如果时间优先,延迟就会减少,但又会降低网络的效率。

因此数据的发送时间不仅协议栈可以控制,而且应用程序也可进行干预。即在发送数据时指定一些选项,比如指定:“不等待填满缓冲区直接发送”,则协议栈就会按照要求直接发送数据。
注:MTU与MSS如下图所示:
在这里插入图片描述

2.3.2对较大的数据进行拆分

发送缓冲区的数据的数据超过MSS长度时,会被以MSS长度为单位进行拆分,拆分出来的每块数据都会加上TCP头部,并根据套接字中记录的控制信息标记发送方和接收方的端口号,然后交给IP模块来执行发送数据的操作。其过程如下图所示:
在这里插入图片描述

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

TCP具备确认对方是否成功收到网络包,以及当对方没收到进行重发的功能。确认原理如下图所示:
在这里插入图片描述
首先,TCP模块在拆分数据时,会先算好每一块数据相当于从头开始的第几个字节,接下来在发送这一块数据时,将算好的字节数写在TCP头部中的“序号”字段。
然后,发送数据的长度也需要告诉接收方,不过这个并不是放在TCP头部中,因为用整个网络包的长度减去头部的长度就可以得到数据的长度,所以接收方通过这种方式来计算。

上图与实际情况有些微差别,在实际通信中,序号不是从1开始的,而是用随机数计算出的初始值,因为从1开始容易猜测,不安全。在开始发送数据之前,需要将该初始值告诉通信对象。

在之前连接过程中,将SYN设置为1的同时,还需要将该序号的初始值。

有时候,TCP数据收发是双向的,在客户端向服务器发送数据的同时,服务器也会向客户端发送数据。其过程如下图所示:
在这里插入图片描述
首先,客户端先计算出一个序号,然后将序号和数据一起发送给服务器,服务器收到之后会计算ACK号并返回给客户端;相反地,服务器也需要先计算出另一个序号,然后将序号和数据一起发送给客户端,客户端收到之后计算ACK号并返回给服务器。此外,客户端和服务器双方都需要各自计算序号,因此双方都需要在连接过程中互相告知自己计算的初始值。

数据双向发送的流程如下图所示:
在这里插入图片描述
首先:客户端在连接时需要计算出与从客户端到服务器方向通信相关的序号初始值,并将这个值发送给服务器(如上图所示1)。

接着,服务器会通过这个初始值计算出ACK号并返回给客户端(如上图所示2)。初始值可能在通信过程中丢失,因此当服务器收到初始值后需要返回ACK号做为确认。同时,服务器也需要计算出与从服务器到客户端方向通信相关的序号初始值,并将这个值发送给客户端(如上图所示2)。

然后,客户端也需要根据服务器发来的初始值计算出ACK号返回给服务器(如上图所示3)。

至此,进入数据收发阶段。

数据收发操作本身是可以双向同时进行的,但在web中是先由客户端向服务器发送请求,序号也会跟随数据一起发送(如上图所示4)。

然后服务器收到数据后再返回ACK号(如上图所示5)。

从服务器向客户端发送数据的过程则正好相反(如上图所示6,7)。

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

一句话:通过序号和ACK号可以确认接收方是否收到了网络包。

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

首先:返回ACK号的等待时间叫超时时间。

当网络传输繁忙时会发生拥塞,ACK号的返回会变慢,这时需要将等待时间设置得稍微长一些,否则可能会发生已经重传了包之后,才收到前面的ACK号。这种重传会加重拥塞。但等待时间设置得过长会出现延迟,也会导致网络速度变慢。

因此,TCP采用了动态调整等待时间的方法,这个等待时间是根据ACK号返回所需的时间来判断的。具体来说,TCP会在发送数据的过程中持续测量ACK号的返回时间,如果ACK号返回变慢,则相应延长等待时间,相对地,如果ACK号马上就能返回,则相应缩短等待时间。

2.3.5使用窗口有效管理ACK号

每发送一个包就等待一个ACK号的一来一回的方式,等待的时间什么都不做会造成浪费,因此TCP采用滑动窗口方式管理数据发送和ACK号的操作。如下图所示:
在这里插入图片描述
所谓滑动窗口,就是在发送一个包之后,不等待ACK号返回,而是直接发送后续的一系列包。由于发送方不再等待接收方发送的ACK号就连续发送包,可能会造成发送包的频率超过接收发处理能力的情况。

下面具体解释一下:
当接收方的TCP收到包后,会先将数据存放到缓冲区,然后,接收方需要计算ACK号,将数据块组装起来还原成原本的数据并传递给应用程序,如果这些操作还没完成下一个包就到了则这个包会被暂存在接收缓冲区。如果数据到达的速率比处理这些数据并传递给应用程序的速率还要快,那么接收缓冲区中的数据就会越来越多,最后溢出。缓冲区溢出之后,后面的数据就不来了,接收方就收不到后面的数据包了,也就是超出了接收方的处理能力。

解决方式是:接收方需要告诉发送方自己最多能接收多少数据,然后发送方根据这个值对数据发送操作进行控制,这就是滑动窗口的基本思路。

滑动窗口的具体工作方式如下图所示:
在这里插入图片描述
这张图中,接收方将数据暂存到接收数据缓冲区并执行接收操作。当接收操作完成后,接收缓冲区中的空间会被释放出来,也就可以接收更多的数据了。这时接收方会通过TCP头部的窗口字段将自己能接收的数据量告知发送方,这样一来,发送方就不会发送过多的数据吗,导致超出接收方的处理能力了。

2.3.6ACK和窗口的合并

为了提高收发数据的效率,需要考虑返回ACK号和更新窗口的时机。

当收到的数据刚开始填入缓冲区,不必每次都向发送方更新窗口大小,因为只要发送方在每次发送数据时减掉已发送的数据长度就可以计算出当前窗口的剩余长度。因此,更新窗口大小的时机应该时接收方从缓冲区取出数据传递给应用程序的时候。这个操作是接收方应用程序发出请求才会进行的,而发送方不知道什么时候回进行这样的操作,因此当接收方将数据传递给应用程序,导致接收缓冲区剩余量增加时,才需要告知发送方,这就是窗口大小更新的时机。

当接收方收到数据时,如果确认内容没有问题,就应该向发送方返回ACK号,因此我们认为接收到数据之后就是返回ACK号的时机。

结合前面两个因素,首先,发送方的数据到达接收方,在接收操作完成之后就需要向发送方返回ACK号,而再经过一段时间,当数据传递给应用程序之后才需要更新窗口大小,这样,每收到一个包,就需要向发送方分别发送ACK号和窗口更新这两个单独的包,这样一来,接收方发给发送方的包就太多了,导致网络效率下降。

解决方法:接收方在发送ACK号和窗口更新时,并不会马上把包发送出去,而是等待一段时间,在这个等待期间很可能会出现其他的通知操作,这样就可以把两种通知合并在一个包里面发送了。

例如:在等待发送ACK号的时候正好需要更新窗口,这时候就可以把ACK号和窗口更新放在一个包里发送,从而减少包的数量。当需要连续发送多个ACK号时,也可以减少包的数量。因为ACK号表示的是已经收到的数据量,也就是说,它是告诉发送发目前已经接收到的数据的最后位置在哪里,因此当需要连续发送ACK号时,只要发送最后一个ACK号就可以了,中间的可以全部省略。当需要连续发送多个窗口时也可以减少包的数量,因为连续发送窗口更新说明应用程序连续请求了数据,接收缓冲区的剩余空间连续增加,与ACK号类似,可以省略中间过程,只要发送最后的数据量即可。

2.3.7接收HTTP响应

首先,浏览器在委托协议栈发送请求消息之后,会调用read程序来获取响应消息。然后,控制流程会通过read转移到协议栈,然后协议栈会执行接下来的操作。第一步:协议栈会检查收到的数据块和TCP头部的内容,判断是否有数据丢失,如果没有问题则返回ACK号。第二步:协议栈将数据块暂存到接收缓冲区中,并将数据块按顺序连接起来还原称原始的数据,将数据交给应用程序。具体来说,协议栈会讲接收到的数据复制到应用程序指定的内存地址中,然后将控制流程教回给应用程序。将数据交给应用程序之后,协议栈还需要找到合适的时机向发送方发送窗口更新。

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

2.4.1数据发送完毕后断开连接

完成数据发送后,客户端或服务器中的一端会发起断开操作。这里以服务器一方发起断开过程为例。其断开交互过程如图所示:
在这里插入图片描述
首先,服务器一方的应用程序会调用Socket库的close程序,然后,服务器的协议栈会生成包含断开信息的TCP头部,具体来说就是将控制位的FIN比特设置为1,接下来,协议栈会委托IP模块向客户端发送数据(如上图所示1)。同时,服务器的套接字中也会记录下断开操作相关的信息。

接下来时客户端。当收到服务器发来的FIN为1的TCP头部时,客户端的协议栈会将自己的套接字标记为进入断开操作状态。然后,为了告知服务器已收到FIN为1的包,客户端会向服务器返回一个ACK号(如上图所示2)。此时,协议栈就可以等待应用程序来取数据了。

然后,应用程序会调用read来读取数据,这时,协议栈不会向应用程序传递数据,而是告知应用程序来自服务器的数据已经全部收到了。根据规则,服务器返回请求之后,web通信操作就会全部结束了,因此只要收到服务器返回的所有数据,客户端的操作也就随之结束了。因此,客户端应用程序会调用close来结束数据收发操作,然后客户端的协议栈也会和服务器一样,生成一个FIN比特为1的TCP包,然后委托IP模块发送给服务器(如上图所示3)。一段时间后,服务器就会返回ACK号(如上图所示4)。至此,通信结束。

2.4.2删除套接字

和服务器的通信结束后,等待几分钟之后就会删除套接字。

2.4.3数据收发操作小结

TCP收发数据的整体流程图如下所示:
在这里插入图片描述
(1)数据收发的第一步是创建套接字。一般来说,服务器一方的应用程序在启动时就会创建号套接字并进入等待连接的状态,客户端则一般是在用户触发特定动作,需要访问服务器的时候创建套接字。
(2)创建套接字后,客户端会向服务器发起连接操作。首先,客户端会生成了SYN为1的TCP包并发送给服务器(如上图所示1)。这个TCP包的头部还包含了客户端向服务器发送数据时使用的初始序号,以及服务器向客户端发送数据时需要用到的窗口大小。当这个包到达服务器会返回一个SYN为1的TCP包(如上图所示2),这个包的头部也包含了序号和窗口大小,此外还包含表示确认收到上一个包的ACK号。当这个包到达客户端时,客户端向服务器返回一个包含表示确认ACK号的TCP包(如上图所示3)。——三次握手。
(3)数据收发阶段,以web为例。
首先客户端会向服务器发送请求消息,TCP会将请求消息切分称一定大小的块,并在每一块前面加上TCP头部,然后发送给服务器(如上图所示4)。TCP头部中包含序号,它表示当前发送的是第几个字节的数据。当服务器收到数据时,会向客户端返回ACK号(如上图所示5)。在最初的阶段,服务器只是不断的接收数据,随着数据收发的进行,数据不断传递给应用程序,接收缓冲区就会被逐步释放,这时服务器需要将新的窗口大小告知客户端。当服务器收到客户端的请求消息后,会向客户端返回响应消息(如上图所示6,7)。
(4)服务器的响应消息发送完毕之后,数据收发操作结束,开始执行断开操作。以web为例,服务器先发起断开过程,这个过程中,服务器先发送一个FIN为1的TCP包(如上图所示8),然后客户端返回一个表示确认收到的ACK号(如上图所示9)。接下来。双方还会交换一组方向相反的FIN为1的TCP包(如上图所示10)和包含ACK号的TCP包(如上图所示11)。最后,等待一段时间,套接字会被删除。——四次挥手。

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

2.5.1包的基本知识

网络包的结构如下图所示:
在这里插入图片描述
网络包主要由头部和数据两部分构成。

一个包被发往目的地的过程如下图所示:
在这里插入图片描述

常用的转发设备包括路由器和集线器两种。

路由器根据目标地址判断下一个路由器的位置。
集线器在子网中将网络包传输到下一个路由。

实际上,集线器是按照以太网规则传输包的设备,而路由器是按照IP规则传输包的设备,因此也可这样理解:

IP协议根据目标地址判断下一个IP转发设备的位置。
子网中的以太网协议将包传输到下一个转发设备。

TCP/IP包中有MAC头部(用于以太网协议)和IP头部(用于IP协议)(如上图所示b)。首先,发送方将包的目的地(要访问的服务器的IP地址)写入IP头部中,这样,IP协议就可以根据这一地址查找包的传输方向,从而找到下一个路由器的位置。接下来,IP协议会委托以太网协议将包传输过去,这时,IP协议会查找下一个路由器的以太网地址(MAC地址),并将这个地址写入MAC头部,这样以太网协议就知道将这个包发往哪一个路由器了。

网络包在传输过程中会经过集线器,集线器是根据以太网协议工作的设备,为了判断包接下来应该向什么地方传输,集线器里有一张表,可根据以太网头部中记录的目的地信息查出相应的传输方向。

接下来,包会到达下一个路由器。路由器中有一张路由表,可根据这张表以及IP头部中记录的目的地信息查出接下来应该发往哪搁路由器。为了将包发到下一个路由器,我们还需要查处下一个路由器的MAC地址,并记录到MAC头部中。这样网络包又被发往下一个节点了。不断重复上述过程,网络包最终被发往目的地。其整个过程如下图所示:
在这里插入图片描述

2.5.2包收发操作概览(IP协议)

尽管我们说IP模块负责将包发给对方,但实际上将包从发送方传到接收方的工作是由集线器,路由器等网络设备完成的。因此IP模块仅仅是整个包传输过程的入口而已。

包收发操作的整体过程如下图所示:
在这里插入图片描述
包收发操作的起点是TCP模块委托IP模块发送包的操作(如上图所示1)。这个委托过程就是TCP模块在数据块的前面加上TCP头部,然后装个传递给IP模块,这部分就是网络包的内容。

收到委托后,IP模块会将包的内容当作一整块数据,在前面加上包含控制信息的头部,即加上IP头部和MAC头部。IP头部包含IP协议规定的,根据IP地址将包发往目的地所需的控制信息,MAC头部包含通过以太网的局域网将包传输至最近的路由器所需的控制信息。

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

接收的过程和发送的过程是相反的。信息先以电信号的形式从网线传输进来,然后由网卡将其转换为数字信息并传递给IP模块(如上图所示3)。接下来,IP模块会将MAC头部和IP头部后面的内容,也就是TCP头部加上数据块,传递给TCP模块。

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

IP头部格式如下图所示:
在这里插入图片描述
IP模块的工作就是生成IP头部附加在TCP头部前面,IP头部中最重要的内容就是IP地址,他表示这个包应该发往哪里去,这个地址是由TCP模块告知的,而TCP又是在执行连接时从应用程序哪里获得这个地址的,因此这个地址的最初来源就是应用程序。

IP地址实际上并不是分配给计算机的,而是分配费网卡的,因此当计算机上存在多块网卡时,每一块网卡都有自己的IP地址,很多服务器上都会安装多块网卡,这时一台计算机就有多个IP地址,在填写发送方IP地址时就需要判断到底应该填写哪个地址。这个判断相当于在多块网卡中判断应该使用哪一块网卡来发送这个包,也就相当于判断应该把包发往哪个路由器,因此只要确定了目标路由器,也就确定了应该使用哪块网卡,也就确定了发送方的IP地址。

如何判断应该把包交给哪个网卡呢?这里需要用到路由表。通过命令route print可以显示路由表的详细内容,如下图所示:
在这里插入图片描述
首先,我们对套接字中记录的目的IP地址与路由表左侧的NetWork Destination栏进行比较,找到对应的一行。例如:TCP模块告知的目标IP地址为192.168.1.21,那么就对应上图中的第六行,因为它和192.168.1相匹配。
接着来看从右边数第二列和第三列的内容。右起第二列也就是interface列,表示网卡等网络接口,这些网络接口可以将包发送给通信对象。此外,右起第三列,即Gateway列表示下一个路由器的IP地址,将包发给这个IP地址,该地址对应的路由器就会将包转发到目标地址。

上图路由表中第一行,目标地址和子网掩码都是0.0.0.0,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行。

这样就可以判断出使用哪块网卡来发送包了,然后就可以在IP头部的发送方IP地址中填上这块网卡对应的IP地址。

接下来还需要填写协议号,它表示包的内容来自哪个模块。例如,如果是TCP模块委托的内容,则设置为06(十六进制),如果是UDP模块委托的内容,则设置为17.

2.5.4生成以太网用的MAC头部

当通过以太网将包发送出去时只知道IP地址是不行的,它需要与IP地址对应的MAC地址。其MAC地址的格式如下图所示:
在这里插入图片描述
MAC头部一共14个字节,主要包括接收方MAC地址(48位),发送方MAC地址(48位),以太网类型(表示后面内容的类型)三个字段。

生成MAC头部时,只要设置这3个字段就可以了。其中以太网类型表示:使用的协议类型,一般在TCP/IP通信中只使用0800和0806这两种。接下来是发送方MAC地址,这里填写网卡本身的MAC地址,MAC地址是网卡生产时写入ROM里的,只要将这个值读出来写入MAC头部就可以了。对于接收方的地址,此刻我们只知道其IP地址,因此,需要查询该IP地址对应的MAC地址。

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

这里我们需要使用ARP。在以太网中,有一种叫广播的方法,可以把包发给连接在同一以太网中的所有设备。ARP就是利用广播对所有设备提问:“x x这个IP地址是谁的?请把你的MAC地址告诉我”。然后就会有人回答“这个IP地址是我的,我的MAC地址是xxxx”。这个过程如下图所示:
在这里插入图片描述

如果对应和自己处于同一个子网中,那么通过上面的操作就可以得到对方的MAC地址,然后将这个MAC地址写入MAC头部,MAC头部就完成了。

为了提高效率我们建立了ARP缓存,每次将查询结果放在其中。在发送包时,先查询一下ARP缓存,如果其中已经保存了对方的MAC地址,就不需要发送ARP查询了,直接使用ARP缓存中的地址,而当缓存中不存在对方的MAC地址时,则发送ARP查询。同时,为了防止ARP缓存的内容与时机不符,一段时间后就需要删除ARP缓存。
用ARP缓存的命令arp -a显示ARP缓存内容如下图所示:
在这里插入图片描述

2.5.6以太网的基本知识

以太网是一种为多台计算机能够彼此自由和廉价地相互通信而设计的通信技术。它的原型就是如下图所示的a。这种网络的本质就是一根网线,其上还有一种叫做收发器的小设备,它的功能知识将不同网线之间的信号连接起来而已。因此,当一台计算机发送信号时,信号就会通过网线流过整个网络,最终到达所有的设备。不过我们无法判断一个信号到底是发给谁的,因此需要在信号的开头加上接收者的地址,这样,与接收者地址相匹配的设备则接收这个包,其他的设备则丢弃这个包,这样就可以把包发送到目的地了。为了控制这一操作,我们需要使用之前的MAC头部。通过MAC头部中接收方的MAC地址,就能够知道包是发给谁的,而通过发送方MAC地址,就能够知道包是谁发出的,此外,通过通过以太网类型就可以判断包里面装了什么类型的内容。

这个原型后来变成了下图所示中b的结构。这个结构是将主干网替换成一个中继式集线器,将收发器网线替换成了双绞线。虽然网络结构有所变化,但信号会发送给所有设备这一基本性质并没有改变。

后来,这个原型变为交换式集线器结构,现在我们所说的以太网都是这样的结构。与之前结构的一个重要变化是:信号会发送给所有设备这一性质变了,现在信号只会流到根据MAC地址指定的设备,而不会到达其他设备了。在这里插入图片描述

以太网的3个基本性质是:
将包发送到MAC头部的接收方MAC地址代表的目的地;用发送方MAC地址识别发送方;用以太网类型识别包的内容。

只要具备这3个性质的网络就是以太网。

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

IP生成的网络包知识存放在内存中的一串数字信息,不能直接发送给对方。因此,需要将数字信息转换成电或光信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。
负责执行这一操作的是网卡,要控制网卡则需要网卡驱动程序。网卡的内部如下图所示:
在这里插入图片描述
网卡并不是通电之后马上工作,而是和其他硬件一样需要进行初始化。也就是说,打开计算机启动操作系统的时候,网卡驱动程序会对硬件进行初始化操作,然后硬件才能进入可以使用的状态,这些操作包括,硬件错误检查,初始设置等步骤。在以太网中,初始化就是z哎控制以太网收发操作的MAC模块中设置MAC地址。

网卡的ROM中保存着全世界唯一的MAC地址,这是在生产网卡时写入的,将这个值读出来之后就可以对MAC模块进行设置,MAC模块就知道自己对应的MAC地址了。

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

网卡驱动程序从IP模块获得包之后,会将其复制到网卡内的缓冲区中,然后向MAC模块发送发送包的命令。接下来,MAC模块开始工作。

首先,MAC模块会将包从缓冲区中取出,并在开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。其结构如下图所示:
在这里插入图片描述
报头是一串像1010101…这样1和0交替出现的比特序列,长度为56比特,它的作用是确定包的读取时机,当这些1010的比特序列被转换成电信号后,会形成如下图所示的波形。接收方在收到信号时,遇到这样的波形就可以判断读取数据的时机。
在这里插入图片描述
用电信号来表达数字信息时,我们需要让1和0两种比特分别对应特定的电压和电流,如下图所示的a中这样的电信号就可以表达数字信息。通过电信号来读取数据的过程就是将这种对应关系颠倒过来。也就是说,通过测量信号中的电压和电流的变化,还原出0和1两种比特的值。然而,实际的信号并不像书上那样有分隔每个比特的辅助线,因此在测量电压和电流时必须先判断出每个比特的界限在哪里,但是,像图中右边这种1和0连续出现的信号,由于电压和电流没有变化,就难以判断应该从哪里划分每个比特。在这里插入图片描述
为了解决上述问题,最简单的方法就是在数据信号之外再发送一组用来区分比特间隔的时钟信号,如上图b所示。所以当时钟信号从下往上变化时读取电压和电流的值,然后和0或1进行对应就可以了。该方法的问题时,当距离较远,网线较长,两条线路的长度会发生差异,数据信号和时钟信号的传输会产生时间差,时钟就会发生偏移。

要解决这个问题,可以采用将数据信号和时钟信号叠加在一起的方法。这样的信号如上图c所示,发送方将这样的信号发给接收方。由于时钟信号是像上图中b这样按固定频率进行变化的,只要能够找到这个变化的周期,就可以从接收到的信号(c)中提取出时钟信号(b),进而通过接收信息(c)和时钟信号(b)计算出数据信号(a),这和发送方将数据信号和时钟信号进行叠加的过程正好相反。然后,只要根据时钟信号(b)的变化周期,就可以从数据信号(a)中读取相应的电压和电流值,并将其还原为0或1的比特了。

这里的重点在于如何判断时钟信号的变化周期,时钟信号是以10Mbit/s或者100Mbit/s这种固定频率进行变化的,只要对信号进行一段时间的观察,就可以找到其变化的周期,因此,我们不能一开始就发生包的数据,而是要在前面加上一段用来测量时钟信号的特殊信号,这就是报头的作用。

报头后面是起始分界符,接收方以这一变化作为编辑,从这里开始提取网络包数据,也就是说,起始帧分界符是一个用来表示包起始位置的标记。

末尾的FCS(帧校验序列)用来检查包传输过程中因噪声导致的波形紊乱,数据错误,它是一串32比特的序列,是通过一个公式对包中从头到尾的所有内容进行计算得出来。如果数据传输正确,接收发计算的FCS和发送方计算的FCS是相同的,否则表示出错。

2.5.9向集线器发送网络包

加上报头,起始帧分界符和FCS之后,就可以将包通过网线发送出去,发送信号的操作分为两种,一种是使用集线器的半双工模式,另一种是使用交换机的全双工模式。

在半双工模式中,为了避免信号碰撞,首先要判断网线中是否存在其他设备发送的信号,如果有,则需要等待该信号传输完毕后再开始发送信号。首先MAC模块从报头开始将数字信息按每个比特转换成电信号,然后由PHY,或者叫MAU的信号收发模块发送出去。接下来,PHY(MAU)模块会将信号转换为可在网线上传输的格式并通过网线发送出去。

PHY(MAU)的职责并不仅仅是将MAC模块传递过来的信号通过网线发送出去,它还需要监控接收线路中有没有信号进来。以免发生碰撞。另外,当网络拥塞时,发送碰撞的可能性会提高。

2.5.10接收返回包

使用集线器的半双工模式以太网中,一台设备发送的信号会到达连接在集线器上的所有设备,因此即使不是发给自己的信号也活通过接收线路传进来,所以接收操作的第一步就是把这些信号全部收进来。
信号的开头时报头,通过报头的波形同步时钟,然后遇到起始帧分界符时开始将后面的信号转换成数字信息。这个操作和发送时是相反的,即PHY(MAU)先开始工作,然后是MAC模块。首先,PHY(MAU)会将信号转换成通用格式并发送给MAC模块,MAC模块再从头开始将信号转换为数字信息,并存放到缓冲区中。当到达信号的末尾时,还需要检查FCS。如果FCS检验没有问题,接下来就要看一下MAC头部中接收方MAC地址与网卡在初始化时分配给自己的MAC地址是否一致,一致则将包放入缓冲区。到这里,MAC模块的工作完成,网卡会通过中断机制告知计算机收到了一个包。
具体来说,中断的工作过程是这样的。首先,网卡向扩展总线中的中断信号发送信号,该信号通过计算机中的中断控制器连接到CPU,当产生中断信号时,CPU会暂时挂起正在处理的任务,切换到操作系统中的中断处理程序。然后,中断处理程序会调用网卡驱动,控制网卡执行相应的接收操作。
网卡驱动被中断处理程序调用后,会从网卡的缓冲区中取出收到的包并通过MAC头部中的以太网类型字段判断协议的类型。如果是0080(十六进制)代表的则是IP协议,网卡驱动就会把这样的包交给TCP/IP协议栈。

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

我们假设web服务器返回了一个网络包,那么协议栈会进行哪些处理呢?服务器返回的包的以太网类型应该是0800,因此网卡驱动会将其交给TCP/IP协议栈来进行处理。
第一步是检查IP头部,确认格式是否正确,如果格式没有问题,下一步就是查看接收方IP地址。如果IP地址不是自己的,则发送ICMP错误报告。ICMP规定了各种类型的消息,详情如下图所示:
在这里插入图片描述
如果接收方IP地址正确,则这个包被接收下来。此时还需要对包进行判断,看其是否本身是否是一个分片,这些经过分片的包会在IP头部的标志字段中进行标记,如果是则需要将其缓存起来,等待IP头部中具有相同标记的包全部到达,再根据IP头部中的分片偏移量,判断出这些分片在整个包中所处的位置,根据这些信息,将这些分片还原成原始的包。
至此,IP模块的工作结束了,接下来包会被交给TCP模块。TCP模块会根据IP头部中的接收方和发送方IP地址,以及TCP头部中的接收方和发送方端口号来查找对应的套接字。找到对应的套接字之后,就可以根据套接字中记录的通信状态,执行相应的操作了。例如,如果包的内容是应用程序数据,则返回确认接收的包,并将数据放入缓冲区,等待应用程序来读取;如果是建立或断开连接的控制包,则返回相应的响应控制包,并告知应用程序建立和断开连接的操作状态。

2.6UDP协议的收发操作

2.6.1不需要重发的数据使用UDP更高效

TCP为了实现数据的可靠传输,需要每次发送数据之后确认对方是否收到,如果没有收到,则进行重发。这样虽然可靠但是效率不高。一种简单的实现方式是,数据全部发送完毕之后让接收方返回一个接收确认,这样一来,如果没有收到直接全部重新发送一遍就好了,不必像TCP那样管理发送和确认的进度。但是,如果漏掉了一个包就全部重发,效率也不高。因此为了提高效率,出错时只是重发出错或未到达的包即可。另外,如果数据很短,一个包就能装下,即使出错也只需重发一个包,这样就不再需要发送那些用来建立和断开连接的控制包了,我们发送了数据,对方一般都会给出回复,只要将回复的数据当作接收确认就行了,也不需要专门的接收确认了。

2.6.2控制用的短数据

UDP没有TCP的接收确认,窗口等机制,因此在收发数据之前也不需要交换控制信息,也就是不需要建立和断开连接的步骤,只要在从应用程序获取的数据前面加上UDP头部,如下图所示,然后交给IP进行发送就可以了。接收也很简单,只要根据IP头部中的接收方和发送方IP地址,以及UDP头部中的接收方和发送方端口号,找到相应的套接字并将数据交给相应的应用程序就可以了。
在这里插入图片描述

2.6.3音频和视频数据

在一些无需重发数据,或者重发了也没什么意义的情况下,使用UDP发送数据的效率会更高些。一个使用UDP的典型场景就是发送音频和视频数据的时候,这些数据中缺少了某些包并不会产生严重的问题,只是会产生一些卡顿或者失真而已,一般是可以接受的。

参考书籍:《网络是怎样连接的》[日] 户根勤 / 著 周自恒 / 译

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值