《网络是怎样连接的》阅读

文章首发于个人blog

欢迎指正补充,可联系lionsom_lin@qq.com

原文地址:《网络是怎样连接的》阅读整理

目录

  • 前序
  • 一、生成HTTP请求消息
  • 二、向DNS服务器查询Web服务器的IP地址
  • 三、浏览器委托协议栈发送消息
  • 四、IP与以太网的包收发操作
  • 五、集线器、交换机、路由器
  • 六、探索接入网和网络运营商(没有深入了解)
  • 七、服务器端的局域网中有什么玄机
  • 八、请求到达Web服务器,响应返回浏览器


macOS Sierra: 网络实用工具帮助



前序

为什么会读这本书?作为一名普通的iOS开发者,在iOS开发过程中一般使用三方库AFNetworking来进行网络请求,很少使用到系统的网络请求接口NSURLSession,顶多顶多使用过socket套接字进行网络请求,这里是我用socket与FastDFS文件服务器进行交互的一个demo。所以我们作为上层开发者一般只会使用规定好的接口,根本不会在意网络请求底层是如何实现或者说是如何传递数据的,基于这种情况,我也就很好奇网络请求底层是如何如何的。

作者:[日]户根勤

于是我也就在亚马逊上挑选了这本《网络是怎样连接的》书籍来进行阅读,希望能从中得到些许启发,借此文章来记录一些我对网络的理解。

整体结构

整体结构

快速温习

总纲1
总纲2

一、生成HTTP请求消息

1.1、浏览器输入URL

http://www.baidu.com

1.2、首先根据HTTP的规则,解析URL

URL各种格式
HTTP格式URL解析

1.3、URL省略文件名的几种情况

a)、http://www.baidu.com/dir/

/dir/后面省略了访问的文件名,服务器已经默认设置在省略文件名情况下要访问的默认文件名,一般都会访问/dir/index.html或者/dir/default.html

b)、http://www.baidu.com/dir

一般处理方案:如果web服务器上存在dir文件,则将dir当作文件名来处理;如果web服务器上存在名为dir的目录,则将dir当作目录来处理。

c)、http://www.baidu.com/

这个URL表示:它访问一个名叫/的目录。由于省略了文件名,所以根据上一种情况,它访问的文件也就是/index.html或者/default.html
注释:/目录表示的是目录层级中最顶级的『根目录』。

d)、http://www.baidu.com

没有路径时,表示访问根目录下事先设置的默认文件夹,也就是/index.html或者/default.html

1.4、HTTP的基本思路

HTTP协议:它定义了客户端与服务器之间交互信息的内容和步骤
HTTP基本思路

HTTP请求消息包含『对什么』和『进行怎样的操作』两个部分。
其中相当于『对什么』的部分称为URI。一般来说URI的内容是一个存放网页的文件名或者是一个CGI程序的文件名,例如"/dir/file.html"、"/dir/program.cgi"等。
其中『进行怎样的操作』的部分称为方法。方法表示需要让web服务器完成怎样的工作,其中典型的例子包括读取URI表示的数据、将客户端输入的数据传递给CGI程序等。

URI:Uniform Resource Identifier,统一资源标识符

CGI程序:对web服务器程序调用其他程序的规则所做的定义就是CGI,安装这个规则来工作的程序就是CGI程序。

HTTP的主要方法

方法 含义
GET 获取URI指定的信息,如果URI指定的文件,则返回文件的内容;如果URI指定的是CGI程序,则返回该程序的输出数据
POST 从客户端向服务器发送数据。一般用于发送表单中填写的数据等情况下。

1.5、生成HTTP请求消息

HTTP消息在格式上是有严格规定的,因此浏览器会按照规定的格式来生成请求消息。

HTTP请求消息

1.6、发送请求后收到响应消息

HTTP响应消息

响应状态码

响应状态码

1.7、请求网页中包含图片

1条请求消息中只能写一个URI,如果需要获取多个文件,必须对每个文件单独发送1条请求
复杂网页请求流程01
复杂网页请求流程02
复杂网页请求流程03

二、向DNS服务器查询Web服务器的IP地址

浏览器能够解析网址并生成HTTP消息,但它本身不具备将消息发送到网络的功能,因此这一功能需要委托操作系统来实现。在委托操作系统发送消息时,必须提供通讯对象的IP地址,而不是域名,因此在HTTP消息之后,我们需要根据域名查询IP地址。

2.1、TCP/IP网络

互联网和公司内部的局域网都是基于TCP/IP的思路来设计的,TCP/IP的结构就是由一些小的子网,通过路由器连接起来组成一个大的网络,这里的子网可以理解为用集线器连接起来的几台计算机,我们将它看成一个单位,称为子网,将子网通过路由器连接起来,就形成一个网络。

TCP/IP网络结构

2.2、IP地址

在网络中,所有的设备都会被分配一个地址,这个地址就相当于显示中某一条路上的"XX号XX室",其中"号"对应的号码是分配给整个子网的,而"室"对应的号码是分配给子网中的计算机的,这就是网络中的地址。"号"称为网络号,"室"称为主机号,整个地址称为IP地址
IP地址 = 网络号 + 主机号

预热:数据传递过程

通过IP地址我们可以判断出访问对象服务器的位置,从而将消息发送到服务器。消息传送的具体过程在后面的章节有详细讲解,不过现在我们先简单了解一下。发送者发出的消息首先经过子网中的集线器,转发到距离发送者最近的路由器上(上图①)。接下来,路由器会根据消息的目的地判断下一个路由器的位置,然后将消息发送到下一个路由器,即消息再次经过子网内的集线器被转发到下一个路由器(上图②)。前面的过程不断重复,最终消息就被传送到了目的地。

2.3、IP地址结构

实际的IP地址是一串32比特(bit)的数字,按照8bit=1byte(字节)为一组分成4组,分别用十进制表示。这就是我们常见的IP地址格式,但仅凭这一串数字我们无法区分哪部分是网络号,哪部分是主机号。在IP地址的规则中,网络号和主机号连起来总共是32比特,但这两部分的具体结构是不固定的。在组建网络时,用户可以自行决定它们之间的分配关系,因此,我们还需要另外的附加信息来表示IP地址的内部结构。

IP地址的表示法
IP地址的结构

IP地址的主机号:

全0:表示整个子网

全1:表示向子网上所有设备发送包,即『广播』

【拓展】

问:有了 IP 地址,为什么还要用 MAC 地址?

答:知乎-有了 IP 地址,为什么还要用 MAC 地址?

2.4、浏览器如何向DNS服务器发出查询的呢?

DNS解析器

向 DNS服务器发出查询,也就是向DNS服务器发送查询消息,并接收服务器返回的响应消息。换句话说,对于 DNS服务器,我们的计算机上一定有相应的 DNS客户端,而相当于 DNS客户端的部分称为 DNS解析器,或者简称解析器。通过 DNS查询 IP地址的操作称为域名解析,因此负责执行解析( resolution)这一操作的就叫解析器( resolver)了。解析器实际上是一段程序,它包含在操作系统的 Socket库中。

首先,库到底是什么东西呢?库就是一堆通用程序组件的集合,其他的应用程序都需要使用其中的组件。库有很多好处。首先,使用现成的组件搭建应用程序可以节省编程工作量;其次,多个程序使用相同的组件可以实现程序的标准化。除此之外还有很多其他的好处,因此使用库来进行软件开发的思路已经非常普及,库的种类和数量也非常之多。

Socket库

Socket库也是一种库,是用于调用网络功能的程序组件集合。其中包含的程序组件可以让其他的应用程序调用操作系统的网络功能 ,而解析器就是这个库中的其中一种程序组件。

2.5、如何通过解析器向DNS服务器发出查询

代码调用查询DNS—C语言gethostbyname

根据域名查询 IP地址时,浏览器会使用 Socket库中的解析器。

调用解析器后,解析器会向 DNS服务器发送查询消息,然后DNS服务器会返回响应消息。响应消息中包含查询到的IP地址,解析器会取出 IP地址,并将其写入浏览器指定的内存地址中。只要运行图 1.11中的这一行程序,就可以完成前面所有这些工作,我们也就完成了IP地址的查询。接下来,浏览器在向 Web服务器发送消息时,只要从该内存地址取出 IP地址,并将它与HTTP请求消息一起交给操作系统就可以了。

DNS请求流程—调用解析器时,计算机内部工作流程

2.6、如何获取DNS服务器IP地址?(Domain Name System,域名系统)

向 DNS服务器发送消息时,我们当然也需要知道 DNS服务器的 IP地址。只不过这个 IP地址是作为 TCP/ IP的一个设置项目事先设置好的,不需要再去查询了。不同的操作系统中 TCP/ IP的设置方法也有差异, MAC中的设置如下图所示,解析器会根据这里设置的 DNS服务器 IP地址来发送消息。

Mac中DNS服务器地址的设置

2.7、DNS服务器是如何工作的

DNS服务器基本工作

DNS服务器会从域名与 IP地址的对照表中查找相应的记录,并返回 IP地址。

域名

例如:www.baidu.com

服务器、邮件服务器(邮件地址中 @后面的部分)的名称

Class

在最早设计 DNS方案时, DNS在互联网以外的其他网络中的应用也被考虑到了,而 Class就是用来识别网络的信息。不过,如今除了互联网并没有其他的网络了,因此 Class的值永远是代表互联网的 IN

记录类型

例如:A = IP地址

MX = 邮件服务器

CNAME = 域名相关的别名

表示域名对应何种类型的记录。例如,当类型为 A时,表示域名对应的是 IP地址;当类型为 MX时,表示域名对应的是邮件服务器。对于不同的记录类型,服务器向客户端返回的信息也会不同

2.8、如何在数万DNS服务器中找到目标DNS服务器

域名层次结构

DNS中的域名都是用句点来分隔的,比如 www.lab.glasscom.com,这里的句点代表了不同层次之间的界限,就相当于公司里面的组织结构不用部、科之类的名称来划分,只是用句点来分隔而已 。

在域名中,越靠右的位置表示其层级越高,比如 www. lab. glasscom. com这个域名如果按照公司里的组织结构来说,大概就是“ com事业集团 glasscom部 lab科的 www”这样。其中,相当于一个层级的部分称为域。因此, com域的下一层是 glasscom域,再下一层是 lab域,再下面才是 www这个名字。

根域

com、jp、cn这些域(称为顶级域)就是最顶层了,它们各自负责保存下级DNS服务器的信息,但实际上并非如此。在互联网中,com和 jp的上面还有一级域,称为根域。根域不像 com、 jp那样有自己的名字,因此在一般书写域名时经常被省略,如果要明确表示根域,应该像 www. lab. glasscom. com.这样在域名的最后再加上一个句点,而这个最后的句点就代表根域。不过,一般都不写最后那个句点,因此根域的存在往往被忽略,但根域毕竟是真实存在的,根域的 DNS服务器中保管着 com、 jp等的 DNS服务器的信息。由于上级 DNS服务器保管着所有下级 DNS服务器的信息,所以我们可以从根域开始一路往下顺藤摸瓜找到任意一个域的 DNS服务器。

通过根域找到目标DNS服务器

还需要完成另一项工作,那就是将根域的 DNS服务器信息保存在互联网中所有的 DNS服务器中。这样一来,任何 DNS服务器就都可以找到并访问根域 DNS服务器了。因此,客户端只要能够找到任意一台 DNS服务器,就可以通过它找到根域 DNS服务器,然后再一路顺藤摸瓜找到位于下层的某台目标 DNS服务器。分配给根域 DNS服务器的 IP地址在全世界仅有 13个,而且这些地址几乎不发生变化,因此将这些地址保存在所有的 DNS服务器中也并不是一件难事。实际上,根域 DNS服务器的相关信息已经包含在 DNS服务器程序的配置文件中了,因此只要安装了 DNS服务器程序,这些信息也就被自动配置好了。

查找目标DNS服务器-详细

三、浏览器委托协议栈发送消息

整体流程一览图

socket连接—整体流程一览图

3.1、创建套接字

<描述符> = socket(<使用IPv4>,<使用TCP>,...)

浏览器调用Socket库中socket程序组件,和调用解析器一样,调用socket之后,控制流程会转移到socket内部并执行创建套接字的操作,完成之后控制流程又会被移交回应用程序。

应用程序调用 socket申请创建套接字,协议栈根据应用程序的申请执行创建套接字的操作。在这个过程中,协议栈首先会分配用于存放一个套接字所需的内存空间,然后写入初始状态。此内存空间用于记录套接字的控制信息。

套接字 创建成功后,协议栈会返回一个描述符,应用程序会将收到的描述符存放在内存中。

描述符

应用程序是通过“描述符”这一类 似号码牌的东西来识别套接字的。

描述符是用来识别不同的套接字的,大家可以作如下理解。我们现在只关注了浏览器访问 Web服务器的过程,但实际上计算机中会同时进行多个数据的通信操作,比如可以打开两个浏览器窗口,同时访问两台 Web服务器。这时,有两个数据收发操作在同时进行,也就需要创建两个不同的套接字。这个例子说明,同一台计算机上可能同时存在多个套接字,在这样的情况下,我们就需要一种方法来识别出某个特定的套接字,这种方法就是描述符。我们可以将描述符理解成给某个套接字分配的编号。也许光说编号还不够形象,大家可以想象一下在酒店寄存行李时的场景,酒店服务人员会给你一个号码牌,向服务人员出示号码牌,就可以取回自己寄存的行李,描述符的原理和这个差不多。当创建套接字后,我们就可以使用这个套接字来执行收发数据的操作了。这时,只要我们出示描述符,协议栈就能够判断出我们希望用哪一个套接字来连接或者收发数据了。

由于套接字中记录了通信双方的信息以及通信处于怎样的状态,所以只要通过描述符确定了相应的套接字,协议栈就能够获取所有的相关信息,这样一来,应用程序就不需要每次都告诉协议栈应该和谁进行通信了。

协议栈内部—网络层次
socket所在位置

套接字

socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。

套接字是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

socket = IP address + TCP/UDP + port。

Mac查看实际的套接字

Mac查看实际的套接字

协议栈

操作系统中的网络控制软件也就是协议栈,网络硬件是网卡。

套接字与协议栈之间的关系

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

协议栈是根据套接字中记录的控制信息来工作的。

网络协议

OSI七层网络模型(Open System Interconnect) TCP/IP网络模型 对应网络协议
应用层(Application) HTTP、FTP、TFTP、SMTP、NFS
表示层(Presentation) 应用层
会话层(Session) SMTP、DNS
传输层(Transport) 传输层 TCP、UDP
网络层(Network) 网络层 IP、ARP、ICMP
数据链路层(Data Link) 数据链路层
物理层(Physical) 物理层

3.2、连接服务器

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr* servaddr, socklen_t addrlen); // 返回:成功为0,出错-1

应用程序通过调用 Socket库中的名为 connect的程序组件来完成这一操作。

第一个参数:即描述符,connect会将应用程序指定的描述符告知协议栈,然后协议栈根据这个描述符来判断到底使用哪一个套接字去和服务器端的套接字进行连接,并执行连接的操作

第二个参数,即服务器 IP地址,就是通过 DNS服务器查询得到的我们要访问的服务器的 IP地址。

第三个参数,即端口号,IP地址是为了区分网络中的各个计算机而分配的数值。因此,只要知道了 IP地址,我们就可以识别出网络上的某台计算机。但是,连接操作的对象是某个具体的套接字,因此必须要识别到具体的套接字才行,而仅凭IP地址是无法做到这一点的。

如果说描述符是用来在一台计算机内部识别套接字的机制,那么端口号就是用来让通信的另一方能够识别出套接字的机制。

服务器上所使用的端口号是根据应用的种类事先规定好的,仅此而已。

Web是 80号端口,电子邮件是 25号端口 65

问题:我们知道了服务器的端口号,但是服务器不知道客户端的端口号?

既然确定连接对象的套接字需要使用端口号,那么服务器也得知道客户端的套接字号码才行吧,这个问题是怎么解决的呢?

事情是这样的,首先,客户端在创建套接字时,协议栈会为这个套接字随便分配一个端口号。接下来,当协议栈执行连接操作时,会将这个随便分配的端口号通知给服务器。

连接服务器本质

连接实际上是通信双方交换控制信息

控制信息

通信操作中使用的控制信息分为两类。

(1)头部中记录的信息

(2)套接字(协议栈中的内存空间)中记录的信息

控制信息一:头部中记录的信息

他们是客户端和服务器相互联络时交换的控制信息。这些字段是固定的,在连接、收发、断开等各个阶段中,每次客户端和服务器之间进行通信时,都需要提供这些控制信息。具体来说,这些信息会被添加在客户端与服务器之间传递的网络包的开头。在连接阶段,由于数据收发还没有开始,网络包中没有实际的数据,只有控制信息。这些控制信息位于网络包的开头,因此被称为头部。此外,以太网和IP协议也有自己的控制信息,这些信息也叫 头部,为了避免各种不同的头部发生混淆,我们一般会记作 TCP头部、以太网头部、 IP头部

TCP头部格式
网络包中头部

控制信息二:套接字(协议栈中的内存空间)中记录的信息

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

3.2.1、连接操作的实际过程

这个过程是从应用程序调用 Socket库的connect开始的(看上图『整体流程一览图』中②)。 >

connect(<描述符>,<服务器IP地址和端口号>, …)

连接操作的第一步是在 TCP模块处创建表示连接控制信息的头部。

通过 TCP头部中的发送方和接收方端口号可以找到要连接的套接字。

TCP建立连接要进行“三次握手”

  • 客户端向服务器发送一个SYN J
  • 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
  • 客户端再向服务器发一个确认ACK K+1
TCP连接三次握手

从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

建立连接之后,协议栈的连接操作就结束了,也就是说 connect已经执行完毕,控制流程被交回到应用程序。

3.3、收发数据

3.3.1、将HTTP请求消息交给协议栈

接下来就进入数据收发阶段了。数据收发操作是从应用程序调用 write将要发送的数据交给协议栈开始的(看上图『整体流程一览图』中③),协议栈收到数据后执行发送操作。

MTU/MSS

MTU: Maximum Transmission Unit,最大传输单元。一个网络包的最大长度,以太网中一般为 1500字节。

MSS: Maximum Segment Size,最大分段大小。除去头部之后,一个网络包所能容纳的 TCP数据的最大长度。TCP和 IP的头部加起来一般是 40字节,因此 MTU减去这个长度就是 MSS。例如,在以太网中, MTU为 1500,因此 MSS就是 1460。 TCP/ IP可以使用一些可选参数( protocol option),如加密等,这时头部的长度会增加,那么 MSS就会随着头部长度增加而相应缩短。

3.3.2、对较大数据进行拆分

应用程序的数据一般都比较大,因此 TCP会按照网络包的大小对数据进行拆分。

数据拆分

3.3.3、TCP通过ACK号确保网络包是否收到

通过“序号”和“ ACK号”可以确认接收方是否收到了网络包。

ACK原理

3.3.4、接受HTTP响应消息

首先,浏览器在委托协议栈发送请求消息之后,会调用read程序(看上图『整体流程一览图』中④)来获取响应消息。然后,控制流程会通过read转移到协议栈,然后协议栈会执行接下来
的操作。和发送数据一样,接收数据也需要将数据暂存到接收缓冲区中,这里的操作过程如下。首先,协议栈尝试从接收缓冲区中取出数据并传递给应用程序,但这个时候请求消息刚刚发送出去,响应消息可能还没返回。响应消息的返回还需要等待一段时间,因此这时接收缓冲区中并没有数据,那么接收数据的操作也就无法继续。这时,协议栈会将应用程序的委托,也就是从接收缓冲区中取出数据并传递给应用程序的工作暂时挂起,等服务器返回的响应消息到达之后再继续执行接收操作。

协议栈会检查收到的数据块和 TCP头部的内容,判断是否有数据丢失,如果没有问题则返回 ACK号。然后,协议栈将数据块暂存到接收缓冲区中,并将数据块按顺序连接起来还原出原始的数据,最后将数据交给应用程序。具体来说,协议栈会将接收到的数据复制到应用程序指定的内存地址中,然后将控制流程交回应用程序。将数据交给应用程序之后,协议栈还需要找到合适的时机向发送方发送窗口更新。

3.4、断开连接,并删除套接字

3.4.1、从服务器端断开连接

这里我们以服务器一方发起断开过程为例来进行讲解。

首先,服务器一方的应用程序会调用Socket库的close程序。然后,服务器的协议栈会生成包含断开信息的 TCP头部,具体来说就是将控制位中的 FIN比特设为 1。接下来,协议栈会委托 IP模块向客户端发送数据。同时,服务器的套接字中也会记录下断开操作的相关信息。

服务器端断开socket

3.4.2、从客户端断开连接

  • 应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
  • 另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
  • 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
  • 接收到这个FIN的源发送端TCP对它进行确认。
TCP断开四次握手

3.4.3、删除套接字

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

误操作举例:如果最后客户端返回的 ACK号丢失了,结果会如何呢?这时,服务器没有接收到 ACK号,可能会重发一次 FIN。如果这时客户端的套接字已经删除了,会发生什么事呢?套接字被删除,那么套接字中保存的控制信息也就跟着消失了,套接字对应的端口号就会被释放出来。这时,如果别的应用程序要创建套接字,新套接字碰巧又被分配了同一个端口号,而服务器重发的 FIN正好到达,会怎么样呢?本来这个 FIN是要发给刚刚删除的那个套接字的,但新套接字具有相同的端口号,于是这个 FIN就会错误地跑到新套接字里面,新套接字就开始执行断开操作了。之所以不马上删除套接字,就是为了防止这样的误操作。

TCP整个流程一览

TCP整个流程

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

4.1、包的基本知识

TCP模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP模块将数据封装成包发送给通信对象。我们在 TCP的讲解中也经常提到 IP,下面就来讨论一下 IP模块是如何将包发送给对方的。

正式开始这个话题之前,我们先来介绍一下关于网络包的一些基本知识。首先,包是由头部和数据两部分构成的(下图(a))。头部包含目的地址等控制信息,大家可以把它理解为快递包裹的面单;头部后面就是委托方要发送给对方的数据,也就相当于快递包裹里的货物。一个包发往目的地的过程如图 2. 15所示。

包的基本结构

4.2、IP模块的作用

4.2.1、发送包的时候,IP模块工作

收到委托后, IP模块会将包的内容当作一整块数据,在前面加上包含控制信息的头部。

IP模块负责添加如下两个头部:

(1)IP头部: IP用的头部,包含 IP地址。IP头部中包含 IP协议规定的、根据 IP地址将包发往目的地所需的控制信息;

(2)MAC头部:以太网用的头部,包含 MAC地址。MAC头部包含通过以太网的局域网将包传输至最近的路由器所需的控制信息。

总之,加上这两个头部之后,一个包就封装好了,这些就是 IP模块负责的工作。

4.2.2、接收包的时候,IP模块工作

返回的包也会通过转发设备发送回来,然后我们需要接收这个包。接收的过程和发送的过程是相反的,信息先以电信号的形式从网线传输进来,然后由网卡将其转换为数字信息并传递给 IP模块(下图中的“ ③接收”)。接下来, IP模块会将 MAC头部和 IP头部后面的内容,也就是 TCP头部加上数据块,传递给 TCP模块。接下来的操作就是我们之前讲过的 TCP模块负责的部分了。

IP模块工作图

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

IP头部

IP头部的“接收方 IP地址”填写通信对象的 IP地址。

发送方 IP地址需要判断发送所使用的网卡,并填写该网卡的 IP地址。

问题来了:一台计算机多个网卡,如何设置发送方IP地址?

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

那么,我们应该如何判断应该把包交给哪块网卡呢?其实和路由器使用 IP表(也叫路由表)判断下一个路由器位置的操作是一样的。因为协议栈的 IP模块与路由器中负责包收发的部分都是根据 IP协议规则来进行包收发操作的,所以它们也都用相同的方法来判断把包发送给谁。

(路由器收发下一章节具体说)

查看Mac本机的路由表Route Table

Mac查看RouteTable

查看Windows本机的路由表Route Table

Windows查看RouteTable

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

MAC头部
  • 发送方 MAC地址

发送方 MAC地址,这里填写网卡本身的 MAC地址。 MAC地址是在网卡生产时写入 ROM里的,只要将这个值读取出来写入 MAC头部就可以了。对于多块网卡的情况,请大家回想一下设置发送方 IP地址的方法 。设置发送方 IP地址时,我们已经判断出了从哪块网卡发送这个包,那么现在只要将这块网卡对应的 MAC地址填进去就好了。

  • 接收方MAC地址

只要告诉以太网对方的 MAC的地址,以太网就会帮我们把包发送过去,那么很显然这里应该填写对方的 MAC地址。然而,在这个时间点上,我们还没有把包发送出去,所以先得搞清楚应该把包发给谁,这个只要查一下路由表就知道了。在路由表中找到相匹配的条目,然后把包发给 Gateway列中的 IP地址就可以了。既然已经知道了包应该发给谁,那么只要将对方的 MAC地址填上去就好了,但到这里为止根本没有出现对方的 MAC地址,也就是说我们现在根本不知道对方的 MAC地址是什么。因此,我们还需要执行根据 IP地址查询 MAC地址的操作。详情看 4.4.1、通过 ARP查询目标路由器的 MAC地址

IP模块根据路由表 Gateway栏的内容判断应该把包发送给谁。

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

这里我们需要使用 ARP协议(网络层协议),它其实非常简单。在以太网中,有一种叫作广播的方法,可以把包发给连接在同一以太网中的所有设备。 ARP就是利用广播对所有设备提问:“ × ×这个 IP地址是谁的?请把你的 MAC地址告诉我。”然后就会有人回答:“这个 IP地址是我的,我的 MAC地址是 × × × ×。” (下图)

ARP: Address Resolution Protocol,地址解析协议。

ARP工作图

通过ARP缓存提升效率,避免每次发送

Mac查看ARP缓存

Mac查看ARP缓存

Windows查看ARP缓存

Windows查看ARP缓存

MAC地址

MAC格式

MAC: Media Access Control的缩写。 MAC头部、 MAC地址中的 MAC也是这个意思。也就是说,通过 MAC模块控制包收发操作时所使用的头部和地址就叫作 MAC头部和 MAC地址。

4.5、网卡-将IP包转换成电或光信号发送出去

IP生成的网络包只是存放在内存中的一串数字信息,没有办法直接发送给对方。因此,我们需要将数字信息转换为电或光信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。负责执行这一操作的是网卡,但网卡也无法单独工作,要控制网卡还需要网卡驱动程序。驱动程序不只有网卡才有,键盘、鼠标、显卡、声卡等各种硬件设备都有。当然,不同厂商和型号的网卡在结构上有所不同,因此网卡驱动程序也是厂商开发的专用程序。

网卡

网卡

网卡的 ROM中保存着全世界唯一的 MAC地址,这是在生产网卡时写入的。

网卡是如何将包转换成电信号并发送到网线中的

网卡驱动从 IP模块获取包之后,会将其复制到网卡内的缓冲区中,然后向 MAC模块发送发送包的命令。接下来就轮到 MAC模块进行工作了。首先, MAC模块会将包从缓冲区中取出,并在开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列

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

网卡MAC模块将包从缓冲区中取出,并在开头加上 报头起始帧分界符,在末尾加上用于检测错误的 帧校验序列

网卡包格式

4.5.1、报头

报头作用

报头是一串像 10101010…这样 1和 0交替出现的比特序列,长度为 56比特,它的作用是确定包的读取时机。当这些 1010的比特序列被转换成电信号后,会形成如图这样的波形。接收方在收到信号时,遇到这样的波形就可以判断读取数据的时机。

网卡包的报头

如何通过电信号来读取数据

用电信号来表达数字信息时,我们需要让 0和 1两种比特分别对应特定的电压和电流,例如下图( a)这样的电信号就可以表达数字信息。通过电信号来读取数据的过程就是将这种对应关系颠倒过来。也就是说,通过测量信号中的电压和电流变化,还原出 0和 1两种比特的值。然而,实际的信号并不像下图所示的那样有分隔每个比特的辅助线,因此在测量电压和电流时必须先判断出每个比特的界限在哪里。但是,像下图( a)右边这种 1和 0连续出现的信号,由于电压和电流没有变化,我们就没办法判断出其中每个比特到底应该从哪里去切分。

通过时钟测量读取信号的时机

通过时钟测量读取信号的时机

如何判断出其中每个比特到底应该从哪里去切分?

要解决这个问题,最简单的方法就是在数据信号之外再发送一组用来区分比特间隔的时钟信号。如图( b)所示,当时钟信号从下往上变化时读取电压和电流的值,然后和 0或 1进行对应就可以了。但是这种方法存在问题。当距离较远,网线较长时,两条线路的长度会发生差异,数据信号和时钟信号的传输会产生时间差,时钟就会发生偏移。

数据信号和时钟信号的传输会产生时间差,时钟就会发生偏移。

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

4.5.2、起始帧分界符

起始帧分界符,它的末尾比特排列有少许变化。接收方以这一变化作为标记,从这里开始提取网络包数据。也就是说,起始帧分界符是一个用来表示包起始位置的标记。

4.5.3、帧校验序列

末尾的 FCS(帧校验序列)用来检查包传输过程中因噪声导致的波形紊乱、数据错误,它是一串 32比特的序列,是通过一个公式对包中从头到尾的所有内容进行计算而得出来的。具体的计算公式在此省略,它和磁盘等设备中使用的 CRC错误校验码是同一种东西,当原始数据中某一个比特发生变化时,计算出来的结果就会发生变化。在包传输过程中,如果受到噪声的干扰而导致其中的数据发生了变化,那么接收方计算出的 FCS和发送方计算出的 FCS就会不同,这样我们就可以判断出数据有没有错误。

4.5.4、向集线器发送网络包

网卡的 MAC模块生成通用信号,然后由 PHY( MAU)模块转换成可在网线中传输的格式,并通过网线发送出去。

4.6、接收返回包

网卡接收网络包

接收操作的第一步就是不管三七二十一把这些信号全都收进来再说。信号的开头是报头,通过报头的波形同步时钟,然后遇到起始帧分界符时开始将后面的信号转换成数字信息。这个操作和发送时是相反的,即 PHY( MAU)模块先开始工作,然后再轮到 MAC模块。首先, PHY( MAU)模块会将信号转换成通用格式并发送给 MAC模块, MAC模块再从头开始将信号转换为数字信息,并存放到缓冲区中。当到达信号的末尾时,还需要检查 FCS。具体来说,就是将从包开头到结尾的所有比特套用到公式中计算出 FCS,然后和包末尾的 FCS进行对比,正常情况下两者应该是一致的,如果中途受到噪声干扰而导致波形发生紊乱,则两者的值会产生差异,这时这个包就会被当作错误包而被丢弃。如果 FCS校验没有问题,接下来就要看一下 MAC头部中接收方 MAC地址与网卡在初始化时分配给自己的 MAC地址是否一致,以判断这个包是不是发给自己的。我们没必要去接收发给别人的包,因此如果不是自己的包就直接丢弃,如果接收方 MAC地址和自己 MAC地址一致,则将包放入缓冲区中 。到这里, MAC模块的工作就完成了,接下来网卡会通知计算机收到了一个包。

网卡中断计算机,网卡驱动从网卡缓存中读取数据

通知计算机的操作会使用一个叫作中断的机制。在网卡执行接收包的操作的过程中,计算机并不是一直监控着网卡的活动,而是去继续执行其他的任务。因此,如果网卡不通知计算机,计算机是不知道包已经收到了这件事的。网卡驱动也是在计算机中运行的一个程序,因此它也不知道包到达的状态。在这种情况下,我们需要一种机制能够打断计算机正在执行的任务,让计算机注意到网卡中发生的事情,这种机制就是中断。具体来说,中断的工作过程是这样的。首先,网卡向扩展总线中的中断信号线发送信号,该信号线通过计算机中的中断控制器连接到 CPU。当产生中断信号时, CPU会暂时挂起正在处理的任务,切换到操作系统中的中断处理程序 。然后,中断处理程序会调用网卡驱动,控制网卡执行相应的接收操作。

网卡驱动被中断处理程序调用后,会从网卡的缓冲区中取出收到的包,并通过 MAC头部中的以太类型字段判断协议的类型。现在我们在大多数情况下都是使用 TCP/ IP协议,但除了 TCP/ IP之外还有很多其他类型的协议,例如 NetWare中使用的 IPX/ SPX,以及 Mac电脑中使用的 AppleTalk等协议。这些协议都被分配了不同的以太类型,如 0080(十六进制)代表 IP协议,网卡驱动就会把这样的包交给 TCP/ IP协议栈;如果是 809B则表示 AppleTalk协议,就把包交给 AppleTalk协议栈,以此类推。

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

下面我们假设 Web服务器返回了一个网络包,那么协议栈会进行哪些处理呢 100?服务器返回的包的以太类型应该是 0800,因此网卡驱动会将其交给 TCP/ IP协议栈来进行处理。接下来就轮到 IP模块先开始工作了,第一步是检查 IP头部,确认格式是否正确。如果格式没有问题,下一步就是查看接收方 IP地址。如果接收网络包的设备是一台 Windows客户端计算机,那么服务器返回的包的接收方 IP地址应该与客户端网卡的地址一致,检查确认之后我们就可以接收这个包了。

IP模块检查IP头部,如果接收方 IP地址不是自己的地址,通过ICMP反馈回去

如果接收方 IP地址不是自己的地址,那一定是发生了什么错误。客户端计算机不负责对包进行转发,因此不应该收到不是发给自己的包 101。当发生这样的错误时, IP模块会通过 ICMP消息将错误告知发送方(图 2. 1)。 ICMP规定了各种类型的消息,如表所示。当我们遇到这个错误时, IP模块会通过表中的 Destination unreachable消息通知对方。从这张表的内容中我们可以看到在包的接收和转发过程中能够遇到的各种错误,因此希望大家看一看这张表。

ICMP格式

IP模块检查IP头部,如果接收方 IP地址是自己的地址,进行分片重组

如果接收方 IP地址正确,则这个包会被接收下来,这时还需要完成另一项工作。 IP协议有一个叫作分片的功能。简单来说,网线和局域网中只能传输小包,因此需要将大的包切分成多个小包。如果接收到的包是经过分片的,那么 IP模块会将它们还原成原始的包。分片的包会在 IP头部的标志字段中进行标记,当收到分片的包时, IP模块会将其暂存在内部的内存空间中,然后等待 IP头部中具有相同 ID的包全部到达,这是因为同一个包的所有分片都具有相同的 ID。此外, IP头部还有一个分片偏移量( fragment offset)字段,它表示当前分片在整个包中所处的位置。根据这些信息,在所有分片全部收到之后,就可以将它们还原成原始的包,这个操作叫作分片重组。

TCP模块接收包

接下来包会被交给 TCP模块。 TCP模块会根据 IP头部中的接收方和发送方 IP地址,以及 TCP头部中的接收方和发送方端口号来查找对应的套接字。找到对应的套接字之后,就可以根据套接字中记录的通信状态,执行相应的操作了。例如,如果包的内容是应用程序数据,则返回确认接收的包,并将数据放入缓冲区,等待应用程序

五、集线器、交换机、路由器

(1)路由器根据目标地址判断下一个路由器的位置

(2)集线器在子网中将网络包传输到下一个路由

实际上,集线器是按照以太网规则传输包的设备,而路由器是按照 IP规则传输包的设备,因此我们也可以作如下理解。

(1)IP协议根据目标地址判断下一个 IP转发设备的位置

(2)子网中的以太网协议将包传输到下一个转发设备

场景设定:网络包在进入互联网之前经历的传输过程。这里我们假设客户端计算机连接的局域网结构是像下图这样的。也就是说,网络包从客户端计算机发出之后,要经过集线器、交换机和路由器最终进入互联网。实际上,我们家里用的路由器已经集成了集线器和交换机的功能,像图上这样使用独立设备的情况很少见。

局域网布局

5.1、信号在网线中传播

网卡 -> 网线 -> 集线器

从信号流出网卡进入网线开始。网卡中的 PHY( MAU) 2模块负责将包转换成电信号,信号通过 RJ-45接口进入双绞线,这部分的放大图如下图的右侧部分所示。以太网信号的本质是正负变化的电压,大家可以认为网卡的 PHY( MAU)模块就是一个从正负两个信号端子输出信号的电路。

网卡与集线器用双绞线连接的形态

网卡与集线器用双绞线连接的形态

问题来了:信号衰减

信号在网线的传输过程中,能量会逐渐损失。网线越长,信号衰减就越严重。集线器收到的信号有时会出现衰减。如下图。

接收方信号变得难以识别

“双绞”是为了抑制噪声

局域网网线使用的是双绞线,其中“双绞”的意思就是以两根信号线为一组缠绕在一起,这种拧麻花一样的设计是为了抑制噪声的影响。

噪声是如何产生的

首先,我们来看看噪声是如何产生的。产生噪声的原因是网线周围的电磁波,当电磁波接触到金属等导体时,在其中就会产生电流。因此,如果网线周围存在电磁波,就会在网线中产生和原本的信号不同的电流。由于信号本身也是一种带有电压变化的电流,其本质和噪声产生的电流是一样的,所以信号和噪声的电流就会混杂在一起,导致信号的波形发生失真,这就是噪声的影响。

电磁波来源有两种:外部和内部

影响网线的电磁波分为两种:

一种是由电机、荧光灯、 CRT显示器等设备泄漏出来的电磁波,这种电磁波来自网线之外的其他设备,

另一种电磁波是从网线中相邻的信号线泄漏出来的。由于传输的信号本身就是一种电流,当电流流过时就会向周围发出电磁波,这些电磁波对于其他信号线来说就成了噪声。这种内部产生的噪声称为串扰( crosstalk)。

双绞线对噪声的抑制

a)通过两根信号线的缠绕抵消外源性噪声;

b)通过改变节距抑制内源性噪声。

双绞线对噪声的抑制

5.2、集线器将信号发往所有设备

集线器的作用?

集线器将信号发送给所有连接在它上面的线路。

信号到达集线器的 PHY( MAU)模块后,会进入中继电路。中继电路的基本功能就是将输入的信号广播到集线器的所有端口上。当然,也有一些产品具有信号整形、错误抑制等功能,但基本上就是将输入的信号原封不动地输出到网线接口。接下来,信号从所有接口流出,到达连接在集线器上的所有设备。然后,这些设备在收到信号之后会通过 MAC头部中的接收方 MAC地址判断是不是发给自己的,如果是发给自己的就接受,否则就忽略。这样,网络包就能够到达指定 MAC地址的接收方了。

由于集线器只是原封不动地将信号广播出去,所以即便信号受到噪声的干扰发生了失真,也会原样发送到目的地。这时,接收信号的设备,也就是交换机、路由器、服务器等,会在将信号转换成数字信息后通过 FCS8校验发现错误,并将出错的包丢弃。当然,丢弃包并不会影响数据的传输,因为丢弃的包不会触发确认响应。因此协议栈的 TCP模块会检测到丢包,并对该包进行重传。

5.3、交换机的包转发操作


对于交换机暂时理解不深刻


5.4、路由器的包转发操作

5.4.1、路由器内部结构

大家只要看明白路由器包括转发模块和端口模块两部分就可以了。其中转发模块负责判断包的转发目的地,端口模块负责包的收发操作。换句话说,路由器转发模块和端口模块的关系,就相当于协议栈的 IP模块和网卡之间的关系。

路由器内部结构

路由器的各个端口都具有 MAC地址和 IP地址。

5.4.2、路由器的工作原理

路由器在转发包时,首先会通过端口将发过来的包接收进来,这一步的工作过程取决于端口对应的通信技术。对于以太网端口来说,就是按照以太网规范进行工作,而无线局域网端口则按照无线局域网的规范工作,总之就是委托端口的硬件将包接收进来。接下来,转发模块会根据接收到的包的 IP头部中记录的接收方 IP地址,在路由表中进行查询,以此判断转发目标。然后,转发模块将包转移到转发目标对应的端口,端口再按照硬件的规则将包发送出去,也就是转发模块委托端口模块将包发送出去的意思。

5.4.3、路由器根据路由表对包进行转发

交换机是通过 MAC头部中的接收方 MAC地址来判断转发目标的,而路由器则是根据 IP头部中的 IP地址来判断的。

路由表

交换机在地址表中只匹配完全一致的记录,而 路由器则会忽略主机号部分,只匹配网络号部分。打个比方,路由器在转发包的时候只看接收方地址属于哪个区, × ×区发往这一边, × ×区发往那一边。

5.4.4、路由器的包接收操作

路由器的整个工作过程。首先,路由器会接收网络包。路由器的端口有各种不同的类型,这里我们只介绍以太网端口是如何接收包的。以太网端口的结构和计算机的网卡基本相同,接收包并存放到缓冲区中的过程也和网卡几乎没有区别。首先,信号到达网线接口部分,其中的 PHY( MAU)模块和 MAC模块将信号转换为数字信息,然后通过包末尾的 FCS进行错误校验,如果没问题则检查 MAC头部中的接收方 MAC地址,看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。如果包的接收方 MAC地址不是自己,说明这个包是发给其他设备的,如果接收这个包就违反了以太网的规则。

路由器的端口都具有 MAC地址,只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃。

5.4.5、查询路由表确定输出端口

MAC头部作用

完成包接收操作之后,路由器就会丢弃包开头的 MAC头部。 MAC头部的作用就是将包送达路由器,其中的接收方 MAC地址就是路由器端口的 MAC地址。因此,当包到达路由器之后, MAC头部的任务就完成了,于是 MAC头部就会被丢弃。

通过路由器转发的网络包,其接收方 MAC地址为路由器端口的 MAC地址。

路由器会根据 IP头部中的内容进行包的转发操作

  • a)查询路由表判断转发目标

关于具体的工作过程,我们还是来看一个实际的例子,如上图的情况,假设地址为 10. 10. 1. 101的计算机要向地址为 192. 168. 1. 10的服务器发送一个包,这个包先到达图中的路由器。判断转发目标的第一步,就是根据包的接收方 IP地址查询路由表中的目标地址栏,以找到相匹配的记录。就像前面讲过的一样,这个匹配并不是匹配全部 32个比特,而是根据子网掩码列中的值判断网络号的比特数,并匹配相应数量的比特 33。例如,上图的第 3行,子网掩码列为 255. 255. 255. 0,就表示需要匹配从左起 24个比特。网络包的接收方 IP地址和路由表中的目标地址左起 24个比特的内容都是 192. 168. 1,因此两者是匹配的,该行记录就是候选转发目标之一。

  • b)路由器修改包的有效期
  • c)路由器通过分片功能拆分大网络包
  • d)路由器的发送操作

这一步操作取决于输出端口的类型。如果是以太网端口,则按照以太网的规则将包转换为电信号发送出去;如果是 ADSL则按照 ADSL的规则来转换,以此类推。在家庭网络中,路由器后面一般连接 ADSL等线路接入互联网,因此路由器会根据接入网的规则来发送包。不过,要理解具体的操作过程,需要先理解相应的通信线路 ,比较复杂,因此我们留到下一章探索互联网内部时再讲解。这里,我们假设路由器位于公司等局域网的内部,即输出端口也是以太网,看看这种情况是如何操作的。

以太网的包发送操作是根据以太网规则来进行的,即便设备种类不同,规则也是相同的。也就是说,其基本过程和协议栈中的 IP模块发送包的过程是相同的,即在包前面加上 MAC头部,

设置其中的一些字段,然后将完成的包转换成电信号并发送出去。下面来简单复习一下这个过程。首先,为了判断 MAC头部中的 MAC地址应该填写什么值,我们需要根据路由表的网关列判断对方的地址。如果网关是一个 IP地址,则这个 IP地址就是我们要转发到的目标地址;如果网关为空,则 IP头部中的接收方 IP地址就是要转发到的目标地址。知道对方的 IP地址之后,接下来需要通过 ARP根据 IP地址查询 MAC地址,并将查询的结果作为接收方 MAC地址。路由器也有 ARP缓存,因此首先会在 ARP缓存中查询,如果找不到则发送 ARP查询请求。

路由器判断下一个转发目标的方法如下。

  • 如果路由表的网关列内容为 IP地址,则该地址就是下一个转发目标。
  • 如果路由表的网关列内容为空,则 IP头部中的接收方 IP地址就是下一个转发目标。

路由器也会使用 ARP来查询下一个转发目标的 MAC地址。

网络包完成后,接下来会将其转换成电信号并通过端口发送出去。这一步的工作过程和计算机也是相同的。例如,当以太网工作在半双工模式时,需要先确认线路中没有其他信号后才能发送,如果检测到碰撞,则需要等待一段时间后重发。如果以太网工作在全双工模式,则不需要确认线路中的信号,可以直接发送。如果输出端口为以太网,则发送出去的网络包会通过交换机到达下一个路由器。由于接收方 MAC地址就是下一个路由器的地址,所以交换机会根据这一地址将包传输到下一个路由器。接下来,下一个路由器会将包转发给再下一个路由器,经过层层转发之后,网络包就到达了最终的目的地。

5.5、路由器与交换机的关系

IP协议本身没有传输包的功能,因此包的实际传输要委托以太网来进行。

路由器是基于 IP设计的,而交换机是基于以太网设计的,因此 IP与以太网的关系也就是路由器与交换机的关系。换句话说,路由器将包的传输工作委托给交换机来进行

IP(路由器)负责将包送达通信对象这一整体过程,而其中将包传输到下一个路由器的过程则是由以太网(交换机)来负责的。

六、探索接入网和网络运营商

此章节没啥好说的,了解下即可!

七、服务器端的局域网中有什么玄机

7.1、防火墙

防火墙基本布局

防火墙的基本思路

防火墙的基本思路,即只允许发往特定服务器中的特定应用程序的包通过,然后屏蔽其他的包。

主流的是包过滤方式的防火墙

包过滤方式的防火墙可根据接收方 IP地址、发送方 IP地址、接收方端口号、发送方端口号、控制位等信息来判断是否允许某个包通过。

防火墙的不足

防火墙无法抵御的攻击

防火墙可以根据包的起点和终点来判断是否允许其通过,但仅凭起点和终点并不能筛选出所有有风险的包。

比如,假设 Web服务器在收到含有特定数据的包时会引起宕机。但是防火墙只关心包的起点和终点,因此即便包中含有特定数据,防火墙也无法发现,于是包就被放行了。然后,当包到达 Web服务器时,就会引发服务器宕机。通过这个例子大家可以看出,只有检查包的内容才能识别这种风险,因此防火墙对这种情况无能为力。

对防火墙无法抵御攻击的应对方法

a)方法一:这个问题的根源在于 Web服务器程序的 Bug,因此修复 Bug防止宕机就是其中一种方法。这类 Bug中,危险性较高的会作为安全漏洞公布出来,开发者会很快发布修复了 Bug的新版本,因此持续关注安全漏洞信息并更新软件的版本是非常重要的。

b)另一种方法就是在防火墙之外部署用来检查包的内容并阻止有害包的设备或软件。

7.2、负载均衡

7.2.1、多台服务器 -- 分布式架构

使用多台服务器来分担负载的方法更有效。这种架构统称为分布式架构。

  • DNS轮询分配服务器IP地址

最简单的一种是通过 DNS服务器来分配。当访问服务器时,客户端需要先向 DNS服务器查询服务器的 IP地址,如果在 DNS服务器中填写多个名称相同的记录,则每次查询时 DNS服务器都会按顺序返回不同的 IP地址。

DNS轮询不足之处

例如:假如多台 Web服务器中有一台出现了故障,这时我们希望在返回 IP地址时能够跳过故障的 Web服务器,然而普通的 DNS服务器并不能确认 Web服务器是否正常工作,因此即便 Web服务器宕机了,它依然可能会返回这台服务器的 IP地址。

  • 使用负载均衡器分配访问
负载均衡器分配访问

7.2.2、缓存服务器

临时保存内容并代替 Web服务器返回内容的缓存服务器

缓存服务器

缓存服务器三种部署

缓存服务器三种部署

7.3、内容分发服务

根据缓存服务器分布第三种方式,进行布局。

八、请求到达Web服务器,响应返回浏览器

8.1、服务器概览

服务器工作过程

8.2、服务器的接收操作

8.2.1、 网卡将接收到的信号转换成数字信息

服务器将接收到的电信号还原数字信息

网卡将接收到的信号转换成数字信息

根据信号还原的数字信息

根据信号还原的数字信息

网卡的 MAC模块将网络包从信号还原为数字信息,校验 FCS并存入缓冲区。

在这个过程中,服务器的 CPU并不是一直在监控网络包的到达,而是在执行其他的任务,因此 CPU并不知道此时网络包已经到达了。但接下来的接收操作需要 CPU来参与,因此网卡需要通过 中断 将网络包到达的事件通知给 CPU。接下来, CPU就会暂停当前的工作,并切换到网卡的任务。然后,网卡驱动会开始运行,从网卡缓冲区中将接收到的包读取出来,根据 MAC头部的以太类型字段判断协议的种类,并调用负责处理该协议的软件。这里,以太类型的值应该是表示 IP协议,因此会调用 TCP/ IP协议栈,并将包转交给它。

网卡驱动会根据 MAC头部判断协议类型,并将包交给相应的协议栈。

8.2.2、IP模块接收操作

( 1)IP模块首先会检查 IP头部的格式是否符合规范,然后检查接收方 IP地址,看包是不是发给自己的;

( 2)判断网络包是否经过分片;

( 3)需要检查 IP头部的协议号字段,并将包转交给相应的模块。例如,如果协议号为 06(十六进制),则将包转交给 TCP模块;如果是 11(十六进制),则转交给 UDP模块。

8.2.3、TCP模块如何处理连接包

TCP连接三次握手

第一步:当 TCP头部中的控制位 SYN为 1时,表示这是一个发起连接的包。这时, TCP模块会执行接受连接的操作,不过在此之前,需要先检查包的接收方端口号,并确认在该端口上有没有与接收方端口号相同且正在处于等待连接状态的套接字。如果指定端口号没有等待连接的套接字,则向客户端返回错误通知的包。向客户端返回一个表示接收方端口不存在等待连接的套接字的 ICMP消息。

第二步:如果存在等待连接的套接字,则为这个套接字复制一个新的副本,并将发送方 IP地址、端口号、序号初始值、窗口大小等必要的参数写入这个套接字中,同时分配用于发送缓冲区和接收缓冲区的内存空间。然后生成代表接收确认的 ACK号,用于从服务器向客户端发送数据的序号初始值,表示接收缓冲区剩余容量的窗口大小,并用这些信息生成 TCP头部,委托 IP模块发送给客户端。

第三步:这个包到达客户端之后,客户端会返回表示接收确认的 ACK号,当这个 ACK号返回服务器后,连接操作就完成了。这时,服务器端的程序应该进入调用 accept的暂停状态,当将新套接字的描述符转交给服务器程序之后,服务器程序就会恢复运行。

8.2.4、TCP模块如何处理数据包

( 1)收到数据包时, TCP模块会根据收到的包的发送方 IP地址、发送方端口号、接收方 IP地址、接收方端口号找到相对应的套接字;

( 2)将数据块拼合起来并保存在接收缓冲区中;

( 3)向客户端返回 ACK。

8.2.5、TCP模块的断开操作

在TCP协议的规则中,断开操作可以由客户端或服务器任何一方发起,具体的顺序是由应用层协议决定的。Web中,这一顺序随HTTP协议版本不同而不同,在HTTP1.0中,是服务器先发起断开操作。

TCP断开四次握手

(1)服务器会调用Socket库的socket,TCP模块会生成一个 FIN为 1的 TCP头部,并委托 IP模块发送给客户端

(2)当客户端收到这个包后,会返回一个ACK号,

(3)接下来客户端会调用close,生成一个 FIN为 1的 TCP头部发给服务器,

(4)服务器再返回一个 ACK号。

8.3、Web服务器程序解释请求消息并作出响应

8.4、浏览器接收到响应并显示内容

拓展阅读

完整的一次 HTTP 请求响应过程(一)

完整的一次 HTTP 请求响应过程(二)

推荐阅读更多精彩内容