第二章——探索操作系统中网络控制软件(协议栈)和网络硬件(网卡)
在有感1最后简单的讲了准备好http消息和IP地址后要委托操作系统协议栈来传达消息,今天来具体讲一下,里面的的内容其实原比我想象的严谨且复杂的,分为6个小节来理解一下:
(1)创建套接字
(2)连接服务器
(3)收发数据
(4)从服务器断开并删除套接字
(5)IP与以太网的包收发操作
(6)UPD协议的收发操作
上图中上层会向下层逐层委派工作
应用程序调用Socket库中的解析器,向DNS服务器查询IP地址,在有感1已经说过了….
我们基于这个图来讲解一下:下面先来说下协议栈TCP的部分:
1.1创建套接字
在协议栈中有着一块通信时用于存放控制信息的内存空间,例如通信对象的IP地址,端口号,通信时的状态信息。套接字本来就是一个概念不存在实体,如果非要给套接字定义的话,那么就是这块存放控制信息的内存空间。所谓的控制信息,例如客户端给服务器发送了请求消息,,因为可能消息会丢失,需要服务器响应说:“我收到了消息”,这就是控制信息,套接字必须要记录是否收到了响应,并记录响应的时间,如果丢失才能根据这些信息重新发送。
在创建套接字的时候,给他分配相应的内存空间,向其写入初始状态比如控制信息:“即将开始通信”,如分配用于临时收发数据的缓冲区空间。还有就是他会有一个标号:描述符,需要将这个描述符告知应用程序,应用程序委托协议栈进行收发数据的时候告诉协议栈你应该用这个套接字,由于套接字存放各种控制信息,协议栈就知道了该和谁通信了,也就是说协议栈是通过套接字中记录的信息来工作的。
1.2连接服务器
创建完套接字后,应用程序调用Socket中的connect,随后双方的套接字就会连接,连接的本质就是双方控制信息的交换,在连接这个阶段,首先需要的申请进行数据收发时的内存空间,这块内存空间叫做缓冲区;既然要进行控制信息的交换,客户端应用程序需要把对方的IP地址端口号等信息给协议栈,数据收发的时候才知道是谁进行数据的收发。
1.2.1控制信息
既然连接的时候讲到了控制信息,那就来具体的讲一下,控制信息大致分为两类:
1.第一类控制信息:客户端和服务器相互联络时的控制信息,这些控制信息在整个通信的过程中都都需要,不仅是连接过程,包括后面的数据收发和断开操作,这些控制信息都会放在通信传递的网络包的开头,因此被称为头部。
在连接阶段,还没有进行数据的收发,意思就是说没有数据,只有头部(上述的第一类控制信息);
第一类控制信息(TCP头部)具体就是下表的这些。
不仅是发送方有头部,接收方也有头部,在整个通信过程中,这种控制信息全局存在用来通信的,比如说:
发送方:我有你的IP地址了,我要开始通讯了
接收方:我准备好了
发送方:现在发送的是1号数据
接收放:1号数据我已经收到
头部的信息非常重要,知道了头部的信息,其实就理解了通信的整个过程。
2.第二类控制信息:就是前面1.1创建套接字中讲到的保存在套接字里的控制信息,这些信息包括接收方发过来的消息,收发数据执行操作的状态信息等,这些套接字里的信息控制着协议栈的操作。
1.2续
接着连接阶段讲,来讲点具体是怎么发送消息的,首先客户端方会建立含有对方的信息的TCP头部,打包成小块小块的网络包接下来TCP模块会委托IP模块来发送信息给服务器端,然后服务器端的IP模块收到消息将消息又传送到TCP模块,TCP模块根据对方TCP头部传来的端口找到对应的套接字,之后套接字会更新状态信息“正在连接”。以上过程完成,服务器TCP模块会发出收到信息的响应发送给客户端,与上面的过程相反,基本相同,需要在头部写上对方的IP和端口号还有SNY比特,SYN为1表示连接成功;再将ACK控制位设为1,这表示收到了你发送的包了;然后委托IP模块发送给客户端的IP模块,又到客户端的TCP模块,如果连接成功,客户端的套接字会存入服务器端的IP地址,端口号,将状态置为连接完毕,到这里还有最后一个步骤,将自己的ACK号置为1发送给服务器端,表示我接收到了你的响应啦,到这里连接操作全部完成,在调用close之前,这个无形的连接管道一直存在。
1.3收发数据
协议栈是不会管发送的什么数据的,应用程序给数据他就发送,但是不会马上发送,而是堆积在连接阶段申请的缓冲区里一段时间在发送,那要堆积到什么程度发送呢,有两个判断时机,一个是时间,如果应用程序老半天也不发送数据过来,就是说发送数据的频率太低了,这是协议栈有一个计时器,到点了就必须发送了;还有一个就是网络包能容纳的最大数据长度,协议栈会根据一个叫做MTU的参数来判断,MTU表示一个网络包的最大数据长度,MTU是含头部的最大长度,MSS表示减去头部长度的最大长度,当应用程序发送数据的长度接近MSS的长度或者大于MSS时,就发送出去,这样就能避免发送大量的小包数据了。
其实这两个判断时间是互相矛盾的,如果长度优先,网络的传输效率会提高,但是数据在缓冲区等待时也就造成了响应的延迟,如果时间优先,等待时间的延迟可能会降低,但是网络的传输效率就会变低,因此协议栈给了应用程序保留了控制发送数据的时机,像浏览器这种会话型的应用程序向服务器发送数据时,如果等待缓冲区填满就会造成严重的延迟,因此一般就会直接发送数据。
1.3.1对较大数据进行划分
如果发送的数据超过了MSS的长度,就不会等待后面的数据,划分为以MSS为单位的包直接发送就行了,当然也要在每一个小数据快的前面也加上相应的头部。
1.3.2 ACK与SYN的响应机制
既然上面讲到了ACK号和SYN,又来细讲一下。
首先TCP模块进行数据划分的时候,会计算每一块数据相当于从头开始的第几个字节,将这个序号放在TCP头部,发送数据的长度也需要告诉服务器,但是不用存在头部当中,接收方可以通过传过来的参数用整个网络包的长度减去头部的长度就可以的到数据的长度。有了以上的两个数值,就知道数据发送的数据是从第几个字节发送的,长度是多少。通过这些信息就知道信息有没有遗漏,通过ACK号来返回响应,返回响应的时候初了设置ACK号以外,还要将控制位中的ACK比特设置为1,这代表ACK号字段有效,客户端就知道这个网络包是是来告知ACK号的。
在实际的通信中,其实序号不是从1开始的,是算出一个随机数作为初始值,这是因为如果初始都为1的话,通信过程就非常容易被预测,有人就会利用这一点来发起攻击。在上面讲连接的时候,有一个将SYN控制位置为1的操作,就是在这一步将序号的初始值告知服务器的,因为将SYN置为1的操作仅在连接过程中出现,所以SYN为1的时候就代表着连接完成。实际上,SYN是Synchronize(同步)的缩写,意思是告知初始序号是通信双方保持步调一直,以便完成后续的数据收发检查,这才是它最初的本意。
上面讲过,响应并不是单方面的是,是需要双向奔赴的,客户端收到响应后,也会给出对服务器端给出响应,也会给出初始序号,ACK号等,其实相应操作都差不多,刚好相反了而已。
TCP通过采用这样的方式判断对方是否收到了数据,在对方确认收到之前,这些数据都会放在缓冲区,如果对方没有返回对应的ACK号,那就重新发送。这一机制非常的强大,有了这一机制,就不需要在别的地方判断数据包是否传送成功,实施补救了。
1.3.3根据网络包平均往返时间调整ACK号等待时机
上面讲了基本的原理,其实这里面是大有文章的。首先当网路非常拥堵的时候ACK号总是姗姗来迟,然而接收方没有收到ACK号的响应,就会将包重新发送,这时应该延长接收方的ACK号等待时间。其实乍一想重新发送一个包也没什么大不了的,但是在网络就非常拥堵的情况下,又不停的重发网络包,就会造成更严重的堵塞,这对于网络无疑是雪上加霜。那延长的时间太长行吗?当然是行不通的,因为这样包的发送就会有很大的延迟,也会导致网络的速度变慢,所以得出结论:必须要找一个合适的值。因此TCP必须动态调整等待的时间,根据ACK号返回的时间,这就又引出了使用窗口有效管理ACK号
1.3.4使用窗口有效管理ACK号
根据下面这两个图分析:
一来一回方式:如果不返回ACK号接收方就给我等着,不要发送下一个网络包,这样就不会出现包多处理不过来的情况,但是这样肯定会造成大量时间的浪费。
滑动窗口方式:如果不等待ACK号返回就继续发送下一个数据包,就会出现发送包的频率超过了接收方处理包的能力,怎么个处理法,下面又来细细品一下,当接收方TCP收到包后,会先将数据存储到接收缓冲区,然后需要接受方需要计算ACK号,再将数据重新组装成员数据发送给应用程序,如果在这个时间点,下一个包到了也没关系,因为下一个包也会先放在缓冲区中,如果数据到达的速率大于了将数据还原给应用程序的速率,那么缓冲区的包就会越来越多,最后缓冲区溢出,数据都不在了,后面的包就都接收不了到了。所以可以用滑动窗口的方式来避免:首先,要告诉服务器端我客户端最多能接收多少数据,发送方根据这个数据来管控数据包的发放,这就是滑动窗口的基本思路,这样就不会因为发送过多的数据,导致超出接收方的处理能力了,这些窗口信息字段存在TCP头部,能及时知道对方的接收能力。窗口的大小就是最大接收数据量,它是TCP参数调优非常有名的一个。通过下面的图片应该可以理解了。
1.3.5ACK与窗口有效合并
先来聊聊窗口更新的时间,1.当数据从发送方发给接收方时,是不需要更新发送方的窗口大小的,因为它自己其实也能计算,只需要将未发送时的窗口大小减去发送出去的数据大小就知道所剩的窗口大小。那应该在什么时候更新呢,2.应该是在接受发的协议栈缓冲区里的数据还原发送给应用程序后跟更新,因为此时发送方是不知道接收方的缓冲区是否有多的容量,如若在1的时候发送,可能就会造成缓冲区溢出,所以就应该在应用程序接收到数据后更新窗口的大小。而接收方的ACK什么时候返回给发送方呢,就相当于是在说:“我知道你返回我数据啦”,应该是一收到数据并通过之前发送方发来的ACK号判断数据无误后就及时返回给发送方ACK号。
结合以上更新ACK和窗口的时机,首先,发送方的数据到达接收方,在接收操作完成之后就需要向发送方返 回 ACK 号,而再经过一段时间 ,当数据传递给应用程序之后才需要更新窗口大小。但如果根据这样的设计来实现,每收到一个包,就需要向发送方分别发送 ACK 号和窗口更新这两个单独的包 。这样一来,接收方发给发送方的包就太多了,导致网络效率下降。
所以应该把他们结合在一个包中来返回更新的时机。当数据发送都无误时,ACK的本质就是告诉发送方已收到的数据量,就是告诉发送方数据量到了那里,不用每次都返回响应,只需返回最后一个就可以,因为发送是无误的,所以中间的那些信息是没有必要每次跟新的;窗口的本质是告诉发送方我的缓冲区有空余,当连续更新窗口大小时,其实也不用每次都更新窗口大小,因为连续更新说明数据已经到了应用程序,缓冲区剩余量逐渐增加,其实中间过程也是可以省略的,只要发送最终结果就行了。所以得出结论,ACK和窗口更新的时间应该是一致的,应放在同一个包中传递响应。
这里对接收数据总结一下:首先协议栈会检查数据块和TCP头部的内容,判断是否有数据的丢失,有问题就重新发送请求,没有问题就返回ACK号,然后协议栈将数据存在缓冲区,并还原成原来的数据,最后将数据复制到应用程序的内存地址中后,向服务器端发送窗口更新,协议栈并将控制流程交给应用程序。
1.3.6接收HTTP的响应
在最开始的时候,接收缓冲区是没有数据的,因为这时候服务器端可能还没有响应,这时从缓冲区传递给应用程序的工作暂时挂起,这时对于这项工作来说,协议栈的这部分工作区进入了暂停状态,但是协议栈还有别的工作。
1.4从服务器端断开并删除套接字
协议栈在设计上是允许任何一方发起断开过程,确认接收到数据之后,将控制流程交给了应用程序,应用程序调用Socket中的close程序,协议栈会生成包含断开信息的TCP头部,具体来说就是将控制位中的FIN比特置为1
假设是服务器端发起的断开信息,客户端收到了含FIN比特1的信息的TCP头部,随后客户端返回服务器端接收到消息的ACK号。收到了这个FIN为1的信号,协议栈可以等待应用程序来读数据了,他只会告诉应用程序,你要的数据服务器已经发来了,并且那边已经断开,这时客户端也要调用close程序,也是委托IP模块给服务器端发送含FIN比特置为1信息的TCP头部,最后应用程序空就可以开始调用read程序读取数据了,过一段时间,服务器端返回表示接收到确认断开响应的ACK号,到这里,客户端与服务器端的通信就全部结束了。这时候需要将套接字删除,但不是服务器端返回ACK号就删除,需要等待一段时间,因为如果表示接收到确认断开响应的这个ACK号的包丢失了,需要重新发送,但是如果此时套接字已经删除,就没法重新发送包了。协议中对于这个等待时间没有明确的规定, 一般来说会等待几分钟之后再删除套接字。
1.5 IP 与以太网的包收发操作
这些包在网络中是怎样跳转到服务器端的呢?先来了解一下包的只是,包是由头部和数据两部分构成,头部包含一些控制信息。TCP头部(用于TCP协议),IP头部(用于IP协议),MAC头部(用于以太网协议)。
在网络中存在很多的转发设备,其实就是大量的路由器和集线器,发送方的网络设备将包准备好后,会发到最近的一个网络转发设备,转发设备会根据头部的信息判断接下来发往哪里,转发设备里存有一个表,记录了每一个目的地的地址。路由器和集线器是两种不同的转发设备,工作也不同:路由器根据目标地址判断的是下一个路由器的位置,集线器是负责在子网中将网络包传到路由器,实际上集线器是按照以太网协议工作,路由器是按照IP协议工作,因此也可以是说成是IP协议根据目标地址判断下一个IP转发设备,子网中的以太网协议将包发送到下一个IP转发设备。
其实路由器根据IP协议跳到下一个路由器是委托以太网协议跳过去的。从头来捋一下,客户端将网络包准备好之后,发给最近的一个转发设备,假设那个转发设备是集线器,集线器里有一张表(用于以太网协议的表),可根据MAC头部的信息查出传输的方向,跳到下一个转发设备。现在已经跳出了子网的区域,该从路由器1跳到路由器2了,包现在在路由器1上,此时包里的IP头部存的依然是目的地地址,根据IP头部存的目的地地址查询出下一个转发设备,并委托以太网协议来转发,这时就需要将MAC头部的地址更新成下一个转发设备的地址,准确的说当1收到包的时候就会将MAC头部舍弃,再次发送的时候又加上包含新MAC地址的MAC头部。这时包就从1跳到2上了,在网络中肯定存在很多的路由器,就反复重复上面的过程直至跳到目的地为止。
1.5.1包的收发操作
包收发操作的起点是TCP模块委托IP模块发送包的操作,IP模块会添加IP头部(包含IP地址)和MAC头部(包含MAC地址),前面讲到这两种头部的分工,其中以太网的部分也可以替换成其他东西,例如无线局域网,ADSL,FTTH,他们都可以替代以太网来传送包,这种将将IP部分和负责传输网络包的部分,可以更好的根据需要使用各种通讯技术,在架构上保证了灵活性,这就是这样设计这种分共方式的原因。
由客户端封装好的包会交给网路硬件(网卡)转发出去,传递给网卡的网络包是一连串的01组成的数字信息,网卡会将这些数字信息转换为电信号或者光信号,并通过网线或者光纤发送出去,这些信号就会到达集线器或者路由器。再由转发设备一步步的送达目的地。
包到达对方处理的过程刚好相反,由网卡将电信号或者光信号转化位数字信息并传递给IP模块,IP模块会将MAC头部和IP头部后面的内容(相当于IP模块的数据区,这个数据区也就是TCP头部和数据)传递给TCP模块。在这里IP模块的职责就是将包送到TCP模块,仅此而已,别的一律不关心,比如出没出错一概不管。
1.5.2生成包含接收方 IP 地址的 IP 头部
IP头部会存有IP地址,这个IP地址是TCP头部告知它的,而又是在连接阶段应用程序通过查询DNS服务器后告知TCP的,即便应用程序指了一个错的IP地址,IP模块也只能照做,当热这样做肯定会出错,这个责任应该由应用程序来承担,下图是IP头部存的一些控制信息:
每一块网卡都有自己的IP地址,当发送方有多个网卡时,在填写发送方的IP地址时,就应该判断应该使用哪一个IP也就是那一块网卡来发送,只要确定了目标路由器,也就确定了哪一个网卡,就确定了发送方的IP地址。那到底是如何来却来确定网卡的呢?
1.5.2确定网卡
确定网卡其实就是确定发送方的哪一个IP,其实和在网络中判断下一个路由器是一个原理,都是根据IP协议来判断将包发给谁,下图就表达了一切!
1.5.3生成以太网用的MAC头部
生成IP头部之后,IP模块还需在IP头部前加上MAC头部,MAC头部式以太网使用的头部,它包含了接收方和发送方的MAC地址等信息。MAC地址长度为48比特,被看作一个整体,不想IP地址分号。
现在来讲讲MAC头部发送方和接收方的MAC地址怎么获取:
首先是发送方的MAC地址,MAC地址在网卡生产时,写在ROM里,网卡的ROM中保存全世界唯一的MAC地址,只需要将这个值读取出来写在MAC头部就可以了,对于多块网卡的时候,确定了接收方的IP地址时,更具IP协议也就就确定了网卡,再将网卡对应的MAC地址写在MAC头部就行了。
接收方的MAC地址的获取就需要ARP(地址解析协议),在以太网中,有一种叫做广播的方法,可以把包发给连接在同一以太网中的所有设备,ARP就是利用广播对所有设备提问:“这个IP地址是谁的,请告诉我你的MAC地址”
如果每次发送包都要这样查询一次,网络中就会多了许多ARP包,因此可以把每次的查询结果放在ARP缓存中,每次查询前先去查询ARP缓存,当ARP缓存中不存在对方的MAC地址时,则发送ARP查询。下图是ARP缓存:
这里的缓存要在一段规定的时间内删除,因为如果IP地址改变了,MAC地址肯定就改变了,不能用原来缓存中的MAC地址,不然就会跳错路由器,就会通信异常。
1.5.4以太网的基础知识
以太网是一种使多台计算机就能够自由相互通信的通信技术,原型就是一根网线,将这些网线的信号连接起来的设备叫做收发器,因此,当计算机发出信号时,信号就会通过网线流过整个网络到达对应的设备,当然发出信号的时候需要知道谁是接收者,所以MAC头部就存有接收方的MAC地址,地址匹配就接收这个信号,不匹配就丢弃这个信号包。根据MAC头部的信息就能知道,包由谁发出,发给谁,发的内容是什么类型。
我们现在的以太网的结构指的是交换式集线器结构,即信号发给所有设备变成了根据MAC头部信息发送指定的设备,并不会到达别的设备了。
1.5.5数据传输
IP模块生成的网络包只是一串01数字,想要在网线上传输需要将数字信号转化成电信号或者光信号,这才是正真的数据传输。
负责执行这个操作的是网卡,但是只有网卡是不行的,还得有网卡驱动程序,网卡驱动程序内置在操作系统中,好的来介绍一下网卡是怎样将数字信号转为电信号的。
首先,网卡驱动会将IP模块准备好的信号包复制到网卡内的缓冲区,然后委托MAC模块将包发出去。具体MAC模块将包从缓冲区取出,在每一个包开头加上报头和起始帧分界符(SFD),在包尾加上用于检验错误的帧校验序列。帧就是包的意思。报头用来测定时机,SFD用来确定帧的起始位置。
报头是一串像 10101010…这样 1 和 0 交替出现的比特序列,长度为 56 比特,它的作用是确定包的读取时机。
我们用电信号表达数字信号的时候,我们需要让 0 和 1 两种比特分别对应特定的电压和电流,实际中我们是不知道每个比特的界限在哪,所以要加上一组区分比特间隔的时钟信号,如下图中的(b)示,当时钟信号从下往上变化时读取电压和电流的值,然后和 0 或 1 进行对应就可以了。但是这种方法存在问题。当距离较远,网线较长时,两条线路的长度会发生差异,数据信号和时钟信号的传输会产生时间差,时钟就会发生偏移。 要解决这个问题,可以采用将数据信号和时钟信号叠加在一起的方法。
由于时钟信号是由固定频率的,所以能计算出它的周期,就可以从接收的信号中提取出时钟信号,进而结合接收的信号就能计算出数据信号。所以重点就是判断它的周期,所以就在开头加上了报头,可以通过报头的那一段01串观察出周期,就不能直接以来就发送数据。
报头后面的起始帧分界符再上图中也表明了根据最后两个01序列的不同,作为标记,从这里开始提取网络数据。
末尾的 FCS(帧校验序列)用来检查包传输过程中因噪声导致的波形紊乱、数据错误,它是一串 32 比特的 85序列,是通过一个公式对包中从头到尾的所有内容进行计算而得出来的。具体的计算公式在此省略,它和磁盘等设备中使用的 CRC88 错误校验码是同一种东西,当原始数据中某一个比特发生变化时,计算出来的结果就会发生变化。在包传输过程中,如果受到噪声的干扰而导致其中的数据发生了变化,那么接收方计算出的 FCS 和发送方计算出的 FCS 就会不同,这样我们就可以判断出数据有没有错误。
加上报头,起始帧分节符,FCS就可以将包通过网线发送出去了。送信号的操作分为两种,一种是使用集线器的半双工模式,另一种是使用交换机的全双工模式。
在半双工模式中,为了避免信号碰撞,首先要判断网线中是否存在其他设备发送的信号。如果有,则需要等待该信号传输完毕,因为如果在有信号时再发送一组信号,两组信号就会发生碰撞。当之前的信号传输完毕,或者本来就没有信号在传输的情况下,我们就可以开始发送信号了。首先, MAC 模块从报头开始将数字信息按每个比特转换成电信号,然后由 PHY,或者叫 MAU 的信号收发模块发送出去 90 。在这里,将数字信息转换为电信号的速率就是网络的传输速率,例如每秒将 10 Mbit 的数字信息转换为电信号发送出去,则速率就是 10 Mbit/s。大家可以认为 PHY(MAU)模块的功能就是对 MAC 模块产生的信号进行格式转换。在使用集线器的半双工模式中,两个设备同时发送信号两组信号就会相互叠加,无法彼此区分出来,这就是所谓的信号碰撞。这种情况下,继续发送信号是没有意义的,因此发送操作会终止。为了通知其他设备当前线路已发生碰撞,还会发送一段时间的阻塞信号 ,然后所有的发送操作会全部停止。等待一段时间之后,网络中的设备会尝试重新发送信号。但如果所有设备的等待时间都相同,那肯定还会发生碰撞,因此必须让等待的时间相互错开。具体来说,等待时间是根据 MAC 地址生成一个随机数计算出来的。当网络拥塞时,发生碰撞的可能性就会提高,重试发送的时候可能又会和另外一台设备的发送操作冲突,这时会将等待时间延长一倍,然后再次重试。以此类推,每次发生碰撞就将等待时间延长一倍,最多重试 10次,如果还是不行就报告通信错误。
1.5.6数据包的接收
数据包的接收刚好和以上的数据传输相反,信号的开头是报头,通过报头的波形同步时钟,然后遇到起始帧分界符时开始将后面的信号转换成数字信息。这个操作和发送时是相反的,即 PHY(MAU)模块先开始工作,然后再轮到 MAC 模块。首先,PHY(MAU)模块会将信号转换成通用格式并发送给 MAC 模块,MAC 模块再从头开始将信号转换为数字信息,并存放到缓冲区中。当到达信号的末尾时,还需要检查 FCS。
如果 FCS 校验没有问题,接下来就要看一下 MAC 头部中接收方 MAC 地址与网卡在初始化时分配给自己的MAC 地址是否一致,以判断这个包是不是发给自己的。我们没必要去接收发给别人的包,因此如果不是自己的包就直接丢弃,如果接收方 MAC 地址和自己 MAC 地址一致,则将包放入缓冲区中 96 。到这里,MAC 模块的工作就完成了,接下来网卡会通知计算机收到了一个包。
通知计算机的操作会使用一个叫作中断的机制。在网卡执行接收包的操作的过程中,计算机并不是一直监控着网卡的活动,而是去继续执行其他的任务。因此,如果网卡不通知计算机,计算机是不知道包已经收到了这件事的。网卡驱动也是在计算机中运行的一个程序,因此它也不知道包到达的状态。在这种情况下,我们需要一种机制能够打断计算机正在执行的任务,让计算机注意到网卡中发生的事情,这种机制就是中断。具体来说,中断的工作过程是这样的。首先,网卡向扩展总线中的中断信号线发送信号,该信号线通过计算机中的中断控制器连接到 CPU。当产生中断信号时,CPU 会暂时挂起正在处理的任务,切换到操作系统中的中断处理程序 。然后,中断处理程序会调用网卡驱动,控制网卡执行相应的接收操作。
1.5.7将服务响应包从IP传递给TCP
假设服务器已经返回了一个网络包,网卡驱动会把它交给TCP/IP模块,显示IP模块,先看一下格式正确吗,如果格式正确,就检查IP地址,这里如果正确的话应该收到的是服务器网卡的IP地址。如果不是,就一定是发生了什么错误,但是客户端是不具备转发能力的,IP模块只会通知ICMP消息告知服务器端,下面这张表包含了包在运输的过程中遇到的各种错误,
如果IP地址正确,此时IP模块需要将分为小包的各个数据复原,分片的包会在IP头部有相同的ID号,等待所有的包到齐,就将他们还原成原始的包,这个操作叫做分片重组。
IP模块的工作结束后移交给TCP模块,TCP模块会根据IP模块和TCP头部的信息找到对应的套接字,找到套接字后,可以根据套接字记录的通信状态,执行相应的操作,例如,如果包的内容是应用程序数据,就返回响应ACK号,将数据放在接收缓冲区,等待应用程序来读取。
1.6UPD协议的收发操作
不需要重发的数据用UPD发送会高效,PCB为什么设计的如此复杂,因为我们需要将数据可靠的发给对方。为了实现高效的传输,错误的包就会重新发送,没出错的就不用重发,这就是它复杂的原因。
但是有一些数据很小,只需要一个包就能装下,就不需要TCP那用复杂的机制,就算重新发送,重新发送一个包就行了。向DNS查询等交换控制信息的操作基本都可以用一个包解决,这种场景都可以用UDP代替TCP,UDP没有接受确认,窗口等机制,不需要交换控制信息,也就说不需要建立连接和断开连接的操作,只需要将从应用程序请求数据接在UDP后面交给IP模块发送就好了,遇到错误一律不管,UDP只单纯的负责发送包,UDP不会向TCP对包进行监控,所以协议栈也不会知道出没出错,出错时就是收不到对方的回复,应用程序会注意到这个问题,重新将包发一次就好了。
比如说一些视频,音频,如果造成了卡顿,不可能在那等着数据包的重新发送,那么干脆就不发了,最多就是有一些掉帧。
到这里网络包已经从网线流出了客户端计算机,下一章就会探索网络包如何经过集线器,路由器,交换机等设备到网络里了。