计算机网络基础知识分享
发送一个http请求,从客户端到服务器端,都经历了什么?
我们的探索之旅从在浏览器中输入网址开始,网址,准确来说应该叫 URL。常见的就是以 http:// 开头的那一串东西,但实际 上除了“http:”,网址还可以以其他一些文字开头,例如“ftp:”“file:” “mailto:”等。
几种常见的URL
尽管 URL 有各种不同的写法,但它们有一个共同点,那就是 URL 开 头的文字,即“http:”“ftp:”“file:”“mailto:”这部分文字都表示浏览器应 当使用的访问方法。比如当访问 Web 服务器时应该使用 HTTP 协议,而 访问 FTP 服务器时则应该使用 FTP 协议。因此,我们可以把这部分理解为 访问时使用的协议类型 。尽管后面部分的写法各不相同,但开头部分的内容决定了后面部分的写法,因此并不会造成混乱。
特殊情况: index.html 或者 default.htm
(a)http://www.lab.glasscom.com/dir/
(b)http://www.lab.glasscom.com/
(c)http://www.lab.glasscom.com
(d)http://www.lab.glasscom.com/whatisthis
解析完 URL 之后,我们就知道应该要访问的目标在哪里了。接下来看看浏览器使用 HTTP 协议来访问 Web 服务器
HTTP协议的基本思路:
首先,客户端会向服务器发送请求消息。请求 消息中包含的内容是“对什么”和“进行怎样的操作”两个部分。
对什么:URI
进行怎样的操作:方法
对 URL 进行解析之后,浏览器确定了 Web 服务器和文件名,接下 来就是根据这些信息来生成 HTTP 请求消息了。
生成HTTP请求消息
HTTP 消息在 格式上是有严格规定的,因此浏览器会按照规定的格式来生成请求消息
生成 HTTP 消息之后,尽管浏览器能够解析网址并生成 HTTP 消息,但它本身并不具备将消息发送到网络中的功能,因此这一功能需要委托操作系统来实现。在进行这一操作时,我们还需要查询网址中服务器域名对应的 IP 地址。因为在网络中需要通过IP地址才能判断出访问对象服务器的位置。IP地址基本思路图
栋:网络号
号:主机号
实际的 IP 地址是一串32 比特的数字,按照 8 比特(1 字节)为一组分成 4 组,分别用十进制表示然后再用圆点隔开。这就是我们平常经常见到的 IP 地址格式。如:10.11.12.13
但仅凭这一串数字我们无法区分哪部分是网络号,哪部分是主机号。在 IP 地址的规则中,网络号和主机号连起来总共是 32 比特,但这两部分的具体结构是不固定的。在组建网络时,用户可以自行决定它们之间的分配关系,因此,我们还需要另外的附加信息来表示 IP 地址的内部结构。
这一附加信息称为子网掩码。是一串与 IP 地址长度相同的 32 比特数字,其左边一半都是 1,右边一半都是0。其中,子网掩码为 1 的部分表示网络号,子网掩码为 0 的部分表示主机号。
这种子网掩码写法太长,我们也可以把 1 的部分的比特数用十进制表示
IP 地址的主机号
全 0:表示整个子网
全 1:表示向子网上所有设备发送包,即“广播”
现在我们知道了通过ip地址可以查找到目标服务器,但是ip地址是一串数字,记忆起来都是比较困难。所以我们可以通过服务器名称来表示IP地址。
需要有一个机制能够通过名称来查询 IP 地址,或者通过 IP 地址来查询名称,这个机制就是 DNS
DNS基本查询原理
DNS 服务器的基本工作就是接收来自客户端的查询消息,然后根据消息的内容返回响应。
其中,来自客户端的查询消息包含以下 3 种信息。
(a)域名
服务器、邮件服务器(邮件地址中 @ 后面的部分)的名称
(b) Class
在最早设计 DNS 方案时,DNS 在互联网以外的其他网络中的应用也被考虑到了,而 Class 就是用来识别网络的信息。不过,如今除了互联网并没有其他的网络了,因此 Class 的值永远是代表互联网的 IN
(c)记录类型
表示域名对应何种类型的记录。例如,当类型为 A 时,表示域名对应的是 IP 地址;当类型为 MX 时,表示域名对应的是邮件服务 器。对于不同的记录类型,服务器向客户端返回的信息也会不同。
DNS 服务器上事先保存有前面这 3 种信息对应的记录数据,如下图(例如,对于一个邮件地址 [email protected],当需要知道这个地址对应的邮件服务器时,我们需要提供 @ 后面的那一串名称。查询消息的内容如下。)
寻找相应的DNS服务器并获取IP地址
我们已经知道了从DNS服务器可以查询到域名的相关ip信息,所以我们现在来探究如何找到某个域名对应的DNS服务器。
互联网中有数万台 DNS 服务器,肯定不能一台一台挨个去找。我们可以采用下面的办法。首先,将负责管理下级域的 DNS 服务器的 IP 地址注册到它们的上级 DNS 服务器中,然后上级 DNS 服务器的 IP 地址再注册到 更上一级的 DNS 服务器中,以此类推。
例如:lab.glasscom.com
lab.glasscom.com 这个域的 DNS 服务器的 IP 地址需要注册到 glasscom.com 域的 DNS 服务器中,
而 glasscom.com 域的 DNS 服务器的 IP 地址又需要注册到 com 域的 DNS 服务器中。
我们就可以通过上级 DNS 服务器查询出下级 DNS 服务器的 IP 地址.
似乎 com、jp 这些域(称为顶级域)就是最顶层了,但实际上并非如此。在互联网中,com 和 jp 的上面还有一级域,称为根域。根域不像 com、jp 那样有自己的名字,因此在一般书写域名时经常被省略,如果要明确表示根域,应该像 www.lab.glasscom.com**.** 这样在域名的最后再加上一个句点,而这个最后的句点就代表根域。不过,一般都不写最后那个句点,因此根域的存在往往被忽略,但根域毕竟是真实存在的,根域的 DNS 服务器中保管着 com、jp 等的 DNS 服务器的信息。
根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器中。这样一来,任何 DNS 服务器就都可以找到并访问根域 DNS 服务器了.分配给根域 DNS 服务器 的 IP 地址在全世界仅有13 个 ,而且这些地址几乎不发生变化,因此将这些地址保存在所有的 DNS 服务器中也并不是一件难事。实际上,根域 DNS 服务器的相关信息已经包含在 DNS 服务器程序的配置文件中了,因此只要安装了 DNS 服务器程序,这些信息也就被自动配置好了。
www.lab.glasscom.com
在真实的互联网中,一台 DNS 服务器可以管理多个域的信息。上级域和下级域有可能共享同一 台 DNS 服务器。在这种情况下,访问上级 DNS 服务器时就可以向下跳过一级 DNS 服务器,直接返回再下一级 DNS 服务器的相关信息。而且 DNS 服务器有一个缓存功能,可以记住之前查询过的域名。如果要查询的域名和相关信息已经在缓存中,那么就可以直接返回响应。
知道了 IP 地址之后,就可以委托操作系统内部的协议栈向这个目标 IP 地址发送消息了。要发送给 Web服务器的 HTTP消息是一种数字信息(digital data),因此也可以说是委托协议栈来发送数字信息。
收发数据是使用Socket 库中的程序组件来实现的,简单来说,收发数据的两台计算机之间连接了一条数据通道,数据沿着这条通道流动,最终到达目的地。我们可以把数据通道想象成一条管道,将数据从一端送入管道,数据就会到达管道的另一端然后被取出。数据可以从任何一端被送入管道,数据的流动是双向的。
建立管道的关键在于管道两端的数据出入口,这些出入口称为套接字。我们需要先创建套接字,然后再将套接字连接起来形成管道。
收发数据的操作可分为4个阶段。
(1)创建套接字(创建套接字阶段)
(2)将管道连接到服务器端的套接字上(连接阶段)
(3)收发数据(通信阶段)
(4)断开管道并删除套接字(断开阶段)
我们从解析浏览器中输入的网址开始,探索了生成 HTTP 请求消息、委托操作系统发送消息等步骤。 现在,我们将讲解操作系统中的协议栈是如何处理数据发送请求的。
(应用程序那一层,Socket库用来收发数据,而解析器用于向DNS服务器发出查询)。
接下来我们看看操作系统部分,其中包括协议栈。协议栈的上半部分有两块,分别是负责用 TCP 协议收发数据的部分和负责用 UDP 协议收发数据的部分,它们会接受应用程序的委托执行收发数据的操作。
下面一半是用 IP 协议控制网络包收发操作的部分。在互联网上传送数据时,数据会被切分成一个一个的网络包,而将网络包发送给通信对象的操作就是由 IP 来负责的。此外,IP 中还包括 ICMP 协议和 ARP 协议。 ICMP 用于告知网络包传送过程中产生的错误以及各种控制消息,ARP 用于根据 IP 地址查询相应的以太网 MAC 地址 。 IP下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收的操作。
在协议栈内部有一块用于存放控制信息的内存空间,这里记录了用于控制通信操作的控制信息,例如通信对象的 IP 地址、端口号、通信操作的进行状态等。本来套接字就只是一个概念而已,我们可以说这些控制信息就是套接字的实体,或者说存放控制信息的内存空间就是套接字的实体。
协议栈是根据套接字中记录的控制信息来工作的.
在连接阶段,由于数据收发还没有开始,网络包中没有实际的数据,只有控制信息。这些控制信息位于网络包的开头,因此被称为头部。客户端和服务器在通信中会将必要的信息记录在头部并相互确认。此外,以太网和 IP 协议也有自己的控制信息,这些信息也叫头部,为了避免各种不同的头部发生混淆, 我们一般会记作 TCP 头部、以太网头部(MAC头部)、IP 头部。
TCP头部格式
连接操作过程,从从应用程序调用 Socket 库的 connect 开始的
connect(< 描述符 >, < 服务器 IP 地址和端口号 >, …)
connect中的套接字会传递给协议栈中的 TCP 模块。然后TCP 模块会与服务器的 TCP模块交换控制信息,这一交互过程包括下面几个步骤
三次握手
当控制流程从 connect 回到应用程序之后,接下来调用 write 将要发送的数据交给协议栈。协议栈并不关心应用程序传来的数据是什么内容。在协议栈看来,要发送的数据就是 一定长度的二进制字节序列而已。
协议栈并不是一收到数据就马上发送出去,而是会将数据存放在内部的发送缓冲区中,并等待应用程序的下一段数据。在数据积累到一定量时再发送出去。判断要素是每个网络包能容纳的数据长度,协议栈会根据一个叫作 MTU 的参数来进行判断。
MTU 表示一个网络包的最大长度,在以太 网中一般是 1500 字节。MTU 是包含头部的总长度,因此需要从 MTU 减去头部的长度,然后得到的长度就是一个网络包中所能容纳的最大数据长度,这一长度叫作 MSS。
发送缓冲区中的数据会被以 MSS 长度为单位进行拆分,拆分出来的每块数据会被放进单独的网络包中。在每一块数据前面加上 TCP 头部,并根据套接字中记录的控制信息标记发送方和接收方的端口号,然后交给 IP 模块来执行发送数据的操作。
1,ACK号
到这里,网络包已经装好数据并发往服务器了,但数据发送操作还没有结束。TCP 具备确认对方是否成功收到网络包,以及当对方没收到时进行重发的功能。(首先,TCP 模块在拆分数据时, 会先算好每一块数据相当于从头开始的第几个字节,接下来在发送这一块 数据时,将算好的字节数写在 TCP 头部中,“序号”字段就是派在这个用场上的。然后,发送数据的长度也需要告知接收方,不过这个并不是放在 TCP 头部里面的,因为用整个网络包的长度减去头部的长度就可以得到数据的长度,所以接收方可以用这种方法来进行计算。有了上面两个数值, 我们就可以知道发送的数据是从第几个字节开始,长度是多少了。)
具体流程看下图:
在实际的通信中, 序号并不是从 1 开始的,而是需要用随机数计算出一个初始值,这是因为如果序号都从 1 开始,通信过程就会非常容易预测,有人会利用这一点来发动攻击。
如果对方没有返回某些包对应 的 ACK 号,那么就重新发送这些包。通过这一机制,我们可以确认接收方有没有收到某个包,如果没有收到则重新发送,这样一来,无论网络中发生任何错误, 我们都可以发现并采取补救措施(重传网络包)。
并且TCP会在发送数据的过程中持续测量 ACK 号的返回时间,如果 ACK 号返回变慢,则相应延长等待时间;相对地,如果 ACK号马上就能返回,则相应缩短等待时间(动态跳转等待时间)。
使用窗口有效管理ACK号
(每发送一个包就等到ACK,这样子的传输效率太慢了),TCP采用滑动窗口的方式来解决这个问题,如下图
并且为了防止数据到达的速率比处理这些数据并传递给应用程序的速率还要快的情况,接收方会通过 TCP 头部中的窗口字段将自己能接收的数据量告知发送方,发送发会根据窗口字段动态跳转发送数据量。
ACK 与窗口的合并
接收方在发送 ACK 号和窗口更新时,并不会马上把包发送出去,而是会等待一段时间,在这个过程中很有可能会出现其他的通知操作, 这样就可以把两种通知合并在一个包里面发送了。
举个例子,在等待发送 ACK 号的时候正好需要更新窗口,这时就可以把 ACK 号和窗口更新放在 一个包里发送,从而减少包的数量。
当需要连续发送多个 ACK 号时,也可以减少包的数量,这是因为 ACK 号表示的是已收到的数据量,也就是说,它是告诉发送方目前已接收的数据的最后位置在哪里,因此当需要连续发送 ACK 号时,只要发送最后一个ACK 号就可以了,中间的可以全部省略。
当需要连续发生窗口更新说明应用程序连续请求了数据,接收缓冲区的剩余空间连续增加。这种情况和 ACK号一样,可以省略中间过程,只要发送最终的结果就可以了。
总结:
首先,协议栈会检查收到的数据块和 TCP 头 部的内容,判断是否有数据丢失,如果没有问题则返回 ACK号。然后, 协议栈将数据块暂存到接收缓冲区中,并将数据块按顺序连接起来还原出 原始的数据,最后将数据交给应用程序。
首先,断开连接的一方的应用程序(假设是服务器)会调用 Socket 库的 close 程序。然后,服务器的协议栈会生成包含断开信息的 TCP 头部,具体来说就是将控制位中的 FIN 比特设为 1。接下来,协议栈会委托 IP 模块向客户端发送数据。同时,服务器的套接字中也会记录下断开操作的相关信息。
接下来轮到客户端了。当收到服务器发来的 FIN 为 1 的 TCP 头部时, 客户端的协议栈会将自己的套接字标记为进入断开操作状态。客户端会向服务器返回一个 ACK 号 ,用于告知服务器已收到 FIN 为 1 的包。
过了一会儿,应用程序就会调用 read 来读取数据 。这时,协议栈不会向应用程序传递数据 ,而是会告知应用程序(浏览器)来自服务器的数据已经全部收到了。因此,客户端应用程序会调用 close 来结束数据收发操作,这时客户端的协议栈也会和服务器一样,生成一个 FIN 比特为 1 的 TCP 包,然后委托 IP 模块发送给服务器。
一段时间之后,服务器就会返回 ACK 号。到这里,客户端和服务器的通信就全部结束了。
总结:TCP整体流程
TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模 块将数据封装成包发送给通信对象。我们在 TCP 的讲解中也经常提到 IP, 下面就来讨论一下 IP 模块是如何将包发送给对方的。
过网络中有路由器和集线器两种不同的转发设备,它们在传输网络包时有着各自的分工。
1,路由器根据目标地址判断下一个路由器的位置,IP 协议根据目标地址判断下一个 IP 转发设备的位置(IP头)。
2,集线器在子网中将网络包传输到下一个路由。子网中的以太网协议将包传输到下一个转发设备(MAC头)。
首先,发送方将包的目的地,也就是要访问的服务器的 IP 地址写入 IP 头部中。接下来,IP 协议会委托以太网协议将包传输过去。
这时,IP 协议会查找下一个路由器的以太网地址(MAC 地址),并将这个地址写入 MAC 头部中。这样一来,以太 网协议就知道要将这个包发到哪一个路由器上了。
接下来,包会到达下一个路由器。路由器中有一张 IP 协议的路由表,可根据这张表以及 IP 头部中记录的目的地信息查出接下来应该发往哪个路由器。为了将包发到下一个路由器,我们还需要查出下一个路由器的MAC 地址,并记录到 MAC 头部中,大家可以理解为改写了 MAC 头部 。这样,网络包就又被发往下一个节点了。
ARP协议
IP 协议会查找下一个路由器的以太网地址(MAC 地址),并将这个地址写入 MAC 头部中。 IP协议通过 ARP 查询目标路由器的 MAC地址。
如果对方和自己处于同一个子网中(如果路由表的设置正确,那么对方应该在同一子网,否则对方无法作出 ARP 响应,这时只能认为对方不存在,包的发送操作就会失败。),ARP协议广播发送ARP请求对所有设备提问:“×× 这个 IP 地址是谁的?请把你的 MAC 地址告诉我。”然后就会有人回答:“这个 IP 地址是我的,我的 MAC 地址是××”。然后,我们将这个 MAC地址写入 MAC 头部,MAC头部就完成了。
相应地,如果每次都要发广播,那就会增加很多包,所以ARP也有缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询。
IP协议 生成了网络包,但只是存放在内存中 的一串数字信息,没有办法直接发送给对方。接下来,网卡将将数字信息转换为电或光信号,才能在网线上传输。现在整个一条线路差不多都通了。
集线器
集线器是一个简单的物理层设备,广播数据包到所有设备。集线器不理解数据包的内容,它只是物理层的转发设备。
交换机管理器(Managed Switch):
交换机
交换机是一个数据链路层设备,根据MAC地址精确地将数据包转发到目标设备;
光猫
光猫是用户家庭或企业网络与光纤接入网络之间的关键接口。它将光纤提供的高速光信号转换为电信号,以便连接到用户的路由器、计算机或其他网络设备。
路由器
路由器是一个网络层设备,用于跨越不同网络并根据IP地址路由数据包。在路由器中也要维护一张表格,叫做路由条目表,用来反映如何到达某个 IP 地址网段(查找路由的下一跳),同时在路由器中也有 ARP 高速缓存,反映了该路由器各直接网段的 IP 地址和 MAC 地址的对应关系。
接入点(Access Point):
接入点用于扩展无线网络覆盖范围,允许设备通过Wi-Fi接入网络。
无线路由器(Wireless Router)
无线路由器连接有线网络和无线网络,允许设备通过Wi-Fi连接到网络。
防火墙(Firewall)
防火墙用于保护网络免受未经授权的访问和网络攻击,它可以过滤入站和出站流量,控制访问权限。
网关(Gateway)
网关是连接不同网络协议和技术的设备,充当不同网络之间的桥梁。
负载均衡器(Load Balancer)
负载均衡器用于平衡多台服务器之间的流量,以提高性能和可用性。
VPN集线器(VPN Hub)
VPN集线器用于建立虚拟私人网络(VPN)连接,以提供远程访问和安全通信。
网络存储设备(Network Attached Storage,NAS)
NAS设备用于存储和共享文件,允许多个设备访问共享文件。