目录
1、前言
本篇是对读完《网络是怎样连接的》这本书的一篇整的总结,具体的每一章节的学习记录之前已经有发布过!然后呢,还会结合本书的内容也大概总结一下外网进内网的一般步骤,其中会经过哪些环节,在下一章发布。有错误还请大佬们指正。
2、探索之旅
具体的过程还是大概是书中这个图的流程。
图1
2.1 输入网址DNS服务器返回IP
第一步就是浏览器输入网址(第一步),然后浏览器区调用操作系统的Socket库,Socket库的解析器开始工作,发送一条请求消息给DNS服务器(UDP),网卡把消息发出去,然后DNS响应,然后解析器收到响应存到内存,浏览器从内存获取IP地址。
2.2 包创建阶段
2.2.1 TCP
首先了解一个概念,在协议栈里面有一个叫做套接字的东西,它其实是一块用于存储控制信息的内存空间(IP、端口等)。在终端可以用netstat命令查看。
在发送请求时需要先执行连接操作(第二步)(TCP协议情况下,UDP不需要连接),也就是TCP三次握手,但是在三次握手前,其实还需要应用程序去调用Socket库的connect函数,需要提供描述符和服务器的IP地址、端口号,然后进入到三次握手环节。
网上有很多三次握手的过程,下面简单介绍一下:
- 第一次:
SYN只有在发起连接和连接接受时为1,所以只有第一次和第二次时为1。
seq为发送数据的序号,此时为随机值。
- 第二次:
SYN为1。ACK为确认标志,除了第一次其他都要为1。
ack为期望下一次对方发送的数据的序号,也表示已经接收到的数据的序号。此时应该为x+1。
seq为服务端发送的数据的偏移量,此时也是随机值。
- 第三次:
ACK为1;seq发送的序号为上一次对方发过来的ack,也就是x+1;ack为y+1表示告诉对方已经收到了截止到y+1偏移量的数据。
图2
三次连接完成后,就可以收发数据了,但是在此之前,协议栈并不是直接马上发送数据出去,而是将数据包存放在操作系统为TCP分配的发送缓冲内存中,具体是要等待缓冲区满再发送数据包还是缓冲区有数据就发送取决与应用程序自己判断,像浏览器这种一般是马上发送。
其中还有一些TCP增加收发效率的操作,比如根据网络包平均往返时间调整ACK包等待时间,使用滑动窗口有效管理ACK号等。
TCP模块建立连接后,在收发操作时将数据包加上TCP头部后,将包交给IP模块去处理(第三步)。
2.2.2 IP
此时网络包流程来到了IP模块,IP模块会给网络包前面加上IP头部和MAC头部。
IP头部中需要填写接收方Ip地址和发送方Ip地址,发送方Ip地址是通过计算机中的路由表找到的对应的网卡的Ip。
MAC头部中需要填写发送方以及接收方的MAC地址。发送方MAC地址就是填写网卡本身的MAC地址,这是在网卡出厂时就写好的。接收方MAC地址需要根据IP地址使用ARP协议(广播)查询MAC地址。
写好这两个头部后,下一步就是将包交给网卡(第四步)。
2.2.3 网卡
网卡在设备通电时会有网卡驱动程序初始化网卡。
然后网络包从IP模块到达网卡,网卡会将包复制到网卡的缓冲区中,然后向MAC模块发送命令让它去发送包,然后就该到MAC模块工作了。
MAC模块会将包从缓冲区中取出,然后在开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。加上这三个控制信息后就将包交给网卡的PHY(MAU)模块,它将包转换成光电信号,然后通过RJ-45接口流出电脑,流向网线。
接下来就是进入硬件了,以本书的集线器-路由器模式为例(第五步)。
图3
图4
2.3 包传输阶段
如上图网络包流出网线后就离开我们的电脑,开始进入其他设备了,本书中以集线器 -> 交换机 -> 路由器的结构讲的,但现在的路由器大多数都集成了集线器和交换机的功能,比如现在家庭中使用的电脑直接连个路由器就可以了。现代的网络结构中也很少用集线器了,因为集线器是所有连接在集线器的设备共享带宽,效率很低,现在大多数都是使用交换机,在像公司内部的局域网中大多就使用交换机实现快速网络交换,比如连接计算机、打印机等等。
2.3.1 集线器
集线器如图,从网卡的RJ-45流出后进入集线器的双绞线,双绞线是为了抑制噪声的影响。然后包到达接口会经过PHY(MAU)模块,这个模块就是和上面网卡中的一样,发送和接收光电信号。然后网络包会到达中继电路,中继电路就是将信号广播到集线器的所有端口上,从后从所有接口流出,到达连接在集线器上的所有设备。假如集线器之后连接着交换机,那么现在包就从集线器流进交换机了(第六步)。
图5
2.3.2 交换机
如下图为交换机的图,在交换机的每个接口后面其实就相当于一块网卡,可以看见和之前的图的网卡的结构差不多,所以它的工作方式也和网卡差不多,交换机和网卡有唯一一个区别就是,交换机不判断对方的MAC地址,而是单纯地进行转发,不管是发给谁的包。。首先信号到达网线接口,然后传入PHY(MAU)模块,然后它会将信号转换为通用模式,传递给MAC模块,MAC模块将信号转换为数字信息,然后通过包末尾的FCS校验错误,如果没问题就放在缓冲区中。
接下来就是查询这个包的接收方MAC地址是否在MAC地址表中有记录,如下图,找到对应MAC地址,就知道把包转发到哪个端口上了。还有一个需要记住的是交换机是支持全双工模式的,也就是可以同时发送和接收,而极线器只能半双工,不能同时发送和接收。
信号流出交换机后就到达路由器(第七步)。
图6
2.3.3 路由器
下图是路由器的结构图,分为两个模块,左边的包转发模块和右边的端口模块。转发模块负责判断包的转发目的地,端口模块负责包的收发操作。
如图8是路由器中的路由表。首先看判断操作,会根据第一列的目标地址和第二列的子网掩码来判断记录,找到符合的记录然后就转发到第四列的指定端口,通过这个端口转发到第三列的网关。但如果存在路由表中找不到匹配的情况下,路由器就丢弃这个包,并通过ICMP消息告诉发送方。交换机的MAC地址表如果找不到就会直接广播到所有连接到交换机的设备,这是因为交换机连接的设备不大,最多就几千台,而路由器是互联网的入口,所以肯定不能发到整个网络上。
然后关于路由器的包收发操作,其实和上面讲的交换机的收发操作差不多,都是先PHY(MAU)模块和MAC模块将信号转换为数字信息,然后通过FCS校验,然后比交换机多一步是要检查MAC头部中的MAC地址,如果是发给自己的就放在接收缓冲区中,否则就丢弃这个包。
最后是发送包操作,其基本过程和协议栈的IP模块发送包差不多。首先,为了判断 MAC 头部中的 MAC 地址应该填写什么值,我们需要根据路由表的网关列判断对方的地址。如果网关是一个 IP 地址,则这个 IP 地址就是我们要转发到的目标地址;如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址。知道对方的IP 地址之后,接下来需要通过 ARP 根据 IP 地址查询 MAC 地址,并将查询的结果作为接收方 MAC 地址。路由器也有ARP缓存,因此首先会在 ARP 缓存中查询,如果找不到则发送 ARP 查询请求。
然后是发送方MAC地址字段, 填写输出端口的MAC地址(一般在生产过程中写入ROM中)还有一个以太类型字段,填写0080。
然后将包转换成电信号并通过端口转发出去。如果输出端口为以太网,则发送出去的网络包会通过交换机到达下一个路由器。由于接收放MAC地址就是下一个路由器的地址,所以交换机会传输到下一个路由器,然后经过层层转发到达目的地。
同时路由器还有两个重要的功能,地址转换和包过滤。地址转换就是为了解决IPv4不足的问题,通过地址转换将内网的ip和公网的ip区别开。包过滤就是在包进行转发时根据MAC头部、IP头部和TCP头部中的内容按照事先设置好的规则决定是转发这个包还是丢弃这个包。通常防火墙就是利用这一机制。
图7
图8
2.3.4 运营商
包会从互联网接入路由器开始进入互联网,也就是进入运营商的传输线路上了,然后再通过互联网接入路由器,在公司也常称为接入网关,然后又从互联网进入公司的内网。这中间的过程大家可以去看下书中的第四章,这里不多介绍,这里进入运营商是第八步。
2.4 各类网关
在包到达服务器之前,在公司的架构中,一般会在服务器之前添加各种设备网关,比如防火墙、负载均衡器、CDN内容分发等等,下面简单介绍一下。这里进入各类公司部署的网关可以看作是第九步。
2.4.1 防火墙
目前实现防火墙功能最为普及的方式是包过滤方式。
包过滤方式其实就是根据网络包中的各种头部TCP头部、IP头部、MAC头部等等里的字段来设置一套规则来达到我们的目的。如下图中的表就是维护的规则。
举个例子,比如我们要阻止某个ip访问我们的服务器,那么我们就判断发送方Ip地址以及判断TCP头部中的SYN字段是否为1,因为SYN只有在三次握手的前两次为1,其他时候都为0,如果符合以上两个条件,那么我们就阻止这个包,不让其通过防火墙。
实际上防火墙并不是什么很特别的东西,还记得之前说过路由器也有包过滤功能吗。其实路由器就可以实现防火墙的功能,只不过当规则越来越复杂,通过路由器的命令难以维护这些规则,而且对于阻止的包进行记录对于路由器来说负担过大,所以才出现了专用的硬件和软件。如果规则不复杂,也不需要记录日志,那么用内置包过滤功能的普通路由器来充当防火墙也可以。
图9
2.4.2 负载均衡
现在企业中都在用负载均衡,有ALB(应用负载均衡)、NLB(网络负载均衡)。
它的做法是把负载均衡器的IP地址代替Web服务器的Ip地址注册到DNS服务器上,然后当访问Web时,DNS服务器就返回负载均衡器的地址,负载均衡器再用自己的算法判断将请求转发给哪台服务器。
负载均衡器可以定期采集Web服务器的CPU、内存使用率,根据这些数据判读服务器的负载状况。也可以不去查询这些数据,而是直接根据事先设置的服务器性能指数,按比例去分配请求。当然,有很多种算法,大家可以自行网上看一下。
2.4.3 CDN内容分发网络
CDN就是在离用户近的地方部署缓存服务器,可以对网站中的静态内容进行缓存加速,加快用户的访问速度。内容分发网络会当服务器的原始数据更新时,立即通知缓存服务器,使得缓存服务器上的数据一直保持最新状态。
找到离用户最近的缓存服务器有两种方法,一种是通过DNS服务器来分配访问,当客户端使用DNS服务器去查询IP时,DNS服务器会用其路由表里所有缓存服务器的路由表去查,可以通过估算距离比较出哪个离用户最近。第二种方法是通过重定向服务器返回离用户最近的缓存服务器的IP。
2.5 包接收阶段
服务器其实大部分和客户端的过程一样,但是有一点不一样的是一般服务器是等待连接,客户端发起连接。然后进入到服务器就可以看作第十步。
服务器等待连接四个阶段:
(1)创建套接字 bind
(2-1)将套接字设置为等待连接状态(等待连接阶段) listen
(2-2)接受连接(接受连接阶段)
(3)收发数据(收发阶段)write
(4)断开管道并删除套接字(断开阶段)close
具体的服务器的接收包操作如下:
2.5.1 网卡
基本过程和客户端的网卡操作过程一样。
第一步是网卡接收到信号,然后将其还原成数字信息(通过时钟信号来还愿成数字信息)。然后根据包末尾的FCS来检测错误,如果与接收到的包FCS不一致,就可能是因为噪声导致信号失真,这时就丢弃这个包。如果一致,即数据没有错误,那么就需要检查MAC头部中的接收方MAC地址,如果不是自己的,就丢弃这个包。到这里,网卡的工作就完成了,还原后的数字信息被保存在网卡内部的缓冲区中。然后网卡需要通过中断机制(第二章提到过)通过CPU包已经到达。
2.5.2 IP模块
当网络包转交到协议栈后,IP模块开始工作,首先检查IP头部是否符合规范,然后检查接收方IP地址,看包是不是发给自己的。是发给自己的话,就需要检查有没有被分片,检查IP头部的内容就可以知道是否分片,如果是分片的包,则将包暂时存放在内存中,等所有分片全部到达后将分片组装起来还原成原始包。然后检查IP头部的协议号字段,看是转发给TCP(协议号06)还是UDP模块(协议号11)。
2.5.3 TCP模块
处理连接包:
当TCP头部的控制位SYN为1时表示是连接包(三次握手)。在执行接受连接操作之前,需要先检查包的接收方端口号,并确认在该端口上有没有与接收方端口号相同并处于等待连接状态的套接字。如果指定端口号没有等待连接的套接字,则向客户端返回错误通知的包。
如果存在等待连接的套接字,则为这个套接字复制一个新的副本,并将发送方IP地址、端口号、序号初始值、窗口大小等必要的参数写入这个套接字中,同时分配用于发送缓冲区和接收缓冲区的内存空间。然后生成代表接收确认的ACK号,用于从服务器向客户端发送数据的序号初始值,表示接收缓冲区剩余容量的窗口大小,并用这些信息生成TCP头部,委托IP模块发送给客户端。
然后客户端返回接收确认的ACK号,当这个包到达服务器后,连接操作就完成了。这时,服务器端的程序应该进入调用accept的暂停状态,当将新套接字的描述符转交给服务器程序后,服务器程序就会恢复运行。
处理数据包:
首先,TCP模块会检查收到的包对应哪一个套接字,找到套接字后,TCP模块会根据套接字中保存的上一个序号和数据长度计算下一个序号,并检查与收到的包的TCP头部中的序号是否一致,如果一致,就说明包正常到达了服务器,没有丢失。这时,TCP模块会从包中提出数据,并存放到接收缓冲区中,与上次收到的数据块连接起来。这样数据就被还原成分包之前的状态了。
当收到的数据进入接收缓冲区后,TCP模块就会生成确认应答的TCP头部,并根据接收包的序号和数据长度计算出ACK号,然后委托IP模块发送给客户端。
然后应用程序会调用Socket库的read来获取收到的数据,然后数据就被转交给应用程序。然后应用程序对收到的数据进行处理,也就是检查HTTP请求消息的内容,并根据请求的内容向浏览器返回相应的数据。
2.5.4 断开连接
调用Socket库的close函数,然后执行四次挥手断开连接,第11步。
3 流程总结
所以从整本书来看一个网络包的生命周期过程如下:
三次挥手建立连接 --> 进入客户端的TCP模块加TCP头部 --> IP模块加IP头部和MAC头部 --> 网卡将数据转换为光电信号 --> 进入传输阶段 --> 集线器 --> 交换机 --> 路由器 --> 不断循环传输阶段 --> 到达互联网接入路由器 --> 进入互联网(运营商传输) --> 通信对象端的互联网接入路由器 --> 防火墙(此阶段之前可能还有负载均衡或CDN) --> 通信对象的网卡 --> IP模块 --> TCP模块 --> 应用程序 --> 四次挥手断开连接 --> Done