浏览器生成消息—浏览器内部
生成 HTTP 请求消息
解析 URL
- URL:Uniform Resource Locator
- FTP:File Transfer Protocol
- 协议:通信操作的规则
- URL 开头部分,表示浏览器应当使用的方法;比如:http,ftp,file,mailto
- Web浏览器解析 URL 过程
URL 省略
http://www.lab.glasscom.com/dir/
- 像这个 URL 其实背后省略了具体文件
- 一般服务器上会事先设置好文件名省略时要访问的默认文件名
- 根据不同的服务器而不同,大多数情况是 index.html 之类
- 完整的访问地址为
http://www.lab.glasscom.com/dir/index.html
http:/www.lab.glasscom.com/
- 这种也是省略了的; 一般情况完整为
http:?/www.lab.glasscom.com/index.html
- 其实以 “/” 结尾,表示它访问了名为 “/” 的目录; “/” 顶级目录,根目录
- 前面的 dir/ 就是访问 dir目录下的内容
- 这种也是省略了的; 一般情况完整为
http:/www.lab.glasscom.com
- 这个连根目录 “/” 也省略了
- 连目录都不知道的情况下,也就是没有路径名的情况下;一般代表访问根目录下事先设置的默认文件
http:/www.lab.glasscom.com/whatisthis
- 这种情况,可能是末尾 “/” 省略了,可也能没省略 ;事实上是很多人没有正确理解省略文件名的规则,经常把目录结尾的 “/” 省略了
- 这种情况应该按照以下处理
- 如果 Web 服务器上存在名为 whatisthis 的文件,则将它作为文件名处理
- 如果存在名为 whatisthis 的目录,则当作目录名来处理
- Linux 下无法创建两个名字相同的文件和目录;因此不可能既有一个名为 whatisthis 的文件,同时又有一个名为 whatisthis 的目录
HTTP 基本思路
- URI:Uniform Resource Identifier
- 一般来说,URI 的内容是一个存放网页数据的文件名或者是一个 CGI 程序的文件名,例如“/dir1/file1.html”“/dir1/program1.cgi”等
- 不过,URI 不仅限于此,也可以直接使用“http:”开头的 URL来作为 URI。换句话说就是,这里可以写各种访问目标,而这些访问目标统称为 URI
CGI 程序:对 Web 服务器程序调用其他程序的规则所做的定义就是 CGI,而按照 CGI 规范来工作的程序就称为 CGI 程序
HTTP 的主要方法
请求/响应
- 一次请求响应过程
向 DNS 服务器查询 Web 服务器的 IP 地址
TCP/IP 结构思路
- 目前大部分路由器已经内置了集线器功能
数据是以包的形式传送的
IP地址:一串 32 bit 的数字,按照 8 bit 为一组分成4组,分别用十进制表示
- 但单凭这串数字还是无法区分哪部分是网络号,哪部分是主机号
- 在IP地址的规则里,网络号加主机号总共是32bit,但是这两部分具体结构是不固定的
- 因此还要附加信息(子网掩码)来表示IP地址的内部结构
DNS 由来
- 确定通信对象需要知道它的IP地址
- 如果不知道通信对象的IP地址,就无法进行通信,需要事先查询记好
- 使用IP地址可以代替使用服务器名称(域名)去访问
- 但是如果通信对象的Web服务器使用了虚拟主机,有可能用IP地址访问不了
- 网络中存在无数的路由器,相互配合,根据IP地址来转发数据
- 用域名去访问对象比用IP去访问效率要低
- 因为IP地址长度32bit,4字节;而域名要几十字节;增加了路由器的负担,自然消耗的时间要更长
- 域名并不仅是长,而且其长度是不固定的。处理长度不固定的数据比处理长度固定的数据要复杂,这也是造成效率低下的重要原因之一
- (1)由于IP难以记住(2)使用域名效率低下
- 因此,使用了折中的方案:让人使用域名,路由器使用IP
- 为了达成这个目的,把域名和IP做关联的机制就诞生了,称为DNS:Domain Name System
- Domain Name System :将服务器名称和 IP 地址进行关联是 DNS 最常见的用法,但 DNS 的功能并不仅限于此,它还可以将邮件地址和邮件服务器进行关联,以及为各种信息关联相应的名称
DNS 解析器
- 相对于DNS服务器,我们的计算机上一定有相应的DNS客户端(解析器),存在于操作系统的 Socket 库中
- 大概流程是
- (1)使用DNS解析器把域名解析写入内存,接着交给操作系统向DNS服务器发送查询消息
- (2)然后DNS服务器响应消息,解析器取出IP地址,存入浏览器指定的内存地址
- (3)浏览器在向Web服务器发送请求的时候,从该内存地址取出IP地址,将它和HTTP请求一起提交给操作系统
- HTTP消息是用文本编写的,但DNS消息是使用二进制数据编写的
浏览器本身不具备使用网络收发数据的功能,都是由操作系统内部的协议栈(操作系统内部的网络控制软件,也叫协议驱动,TCP/IP驱动,等等)来执行的
向DNS服务器发送消息的时候,也是要知道DNS服务器的IP地址的,只不过这个IP地址作为TCP/IP的一个设置项目事先设置好了
全世界 DNS 服务器的大接力
DNS 基本工作
- 来自客户端解析器的查询信息
- 域名
- Class:以前设计的时候,还需要标识网络信息,不过现在除了互联网以外没有其他网络了
- 记录类型:标识域名对应何种记录类型;比如 A(Address) 标识了域名对应的是IP地址、MX(Mail eXchange) 标识了对应邮件服务器
域名层次结构
- 互联网中存在无数的服务器,将这些服务器信息存在一台DNS服务器是不可能的
- 因此,将信息分布保存在多台DNS服务器中,层层追溯
-
- 越靠右边,层级越高
- www.lab.glasscom.com < lab.glasscom.com < glasscom.com < com
- 每个层级可以称为域,每个域作为一个整体存放在DNS服务器中(但是,DNS服务器和域也并不总是一对一的)
互联网中那么多台DNS服务器,肯定不能挨个去找
- 因此,将下级域的IP地址注册到它们的上级域的DNS服务器中,就和链表,区块链一个意思
- 也就是说,负责管理 lab.glasscom.com 域的DNS服务器的IP地址注册到 glasscom.com 域的DNS服务器中,而 glasscom.com 域的DNS服务器的IP地址又注册到了 com 域的DNS服务器中
- 这样就可以从顶级开始层层追溯了
- 目前来说,一般 com,jp 称为顶级域
- 但是事实上它们上面还有一级域,称为根域
- 根域并没有名字,因此常常被省略
- www.lab.glasscom.com. 最后一个点就代表根域(和根目录一个思路)
- 根域的DNS服务器信息保存在全互联网所有DNS服务器中,这样一来就完成了
- 分配给根域DNS服务器的IP地址在全世界只有13个,这些地址几乎不变化,所以就可以保存在所有DNS服务器中了
- 根域 DNS 服务器在运营上使用多台服务器来对应一个 IP 地址,因此尽管 IP 地址只有 13 个,但其实服务器的数量是很多的
- 客户端计算机第一次访问的DNS服务器也就是我们在TCP/IP中设置的DNS服务器(所以改DNS也是有讲究的)
第一次访问的DNS服务器作为所有访问查询的中继点,如图所示
上图追溯只是基本原理,真实互联网一台DNS服务器可以管理多个域的信息,因此并不是每个域都有一台自己的DNS服务器
- 现实中上下级域有可能共享同一台DNS服务器
- 此外,有时候并不需要从最上级的根域开始查找,因为DNS服务器有个缓存功能
- 缓存可以记住之前查询过的域名;
- 如果要查询的域名和相关信息已经在缓存中,就可以直接从缓存的位置开始向下查询;减少查询时间
- 并且,当要查询的域名信息不存在时,“不存在”这个响应结果也会被缓存;当下次查询这个不存在的域名时,也可以快速响应
- 需要注意的是,信息被缓存后,原本的注册信息可能会发生改变,这时缓存的信息就有可能是不正确的
- 因此,DNS服务器中保存的信息都会设置一个有效期;而且在查询响应时,DNS服务器也会告知客户端这个响应结果是来自缓存还是来自负责管理该域名的DNS服务器
委托协议栈发送信息
数据收发操作概览
知道IP地址后,就可以委托操作系统内部的协议栈像这个目标IP地址,发送HTTP消息(数字消息)了
收发数字消息这个操作不局限于浏览器,对于各种使用网络的应用程序都是共通的
在收发数据操作之前,双方需要先建立连接(建立如图的假设出来的管道)
- 建立管道的关键在于管道两端的数据出入口,这些出入口称为套接字
- 我们需要先创建套接字,然后再将套接字连接起来形成管道
实际上过程如下
- 服务器一方先创建套接字,等待客户端向该套接字连接管道
- 当服务器进入等待状态,客户端就可以连接管道了
- 客户端也会先创建套接字,然后从该套接字延伸出管道,
- 最后连接到服务器端的套接字上
- 之后就可以收发数据了
- 数据发送完毕后,断开时可以由任意一方发起
综上所述,可以总结为4个阶段
- 创建套接字(创建套接字阶段)
- 将管道连接到服务器段的套接字上(连接阶段)
- 收发数据(通信阶段)
- 断开管道并删除套接字(断开阶段)
以上的操作都是由操作系统内部的协议栈来执行的,浏览器和应用程序并不会自己去做这些工作,而是“委托”协议栈来做
- 这些“委托”都是通过调用 Socket库中的程序组件来执行的,这些数据通信用的组件充当了桥梁的角色
创建套接字阶段
- 套接字创建完毕后,协议栈会返回一个描述符
- 应用程序会将收到的描述符存在内存中
- 描述符用来识别不同的套接字,因为实际上会有多个数据同时通信
连接阶段
- 连接的时候还需要给出IP地址的具体端口号(表示具体哪个应用程序)
- 然后再从那个应用程序中找按照标识符找到对应的套接字
通信阶段
- 连接完毕之后,接下来就通过 Sokcet库委托协议栈来将 应用程序在内存中准备好的数据 送入套接字
- 接着协议栈再将数据发送到服务器
- 服务器接收到数据后,执行相应的操作,再返回消息给客户端
断开阶段
- Web 使用的HTTP协议规定,当Web服务器发送完响应消息后,应该主动断开操作
- HTTP协议将HTML文档和图片都作为单独对象处理,因此要执行很多次连接断开操作
- 所以后来HTTP 1.1 就可以在一次连接中收发多个请求响应
用电信号传输 TCP/IP 数据—协议栈、网卡
创建套接字
- 协议栈内部结构
- ICMP用于告知网络包传送过程中产生的错误以及各种控制消息
- ARP用于根据IP地址查询相应的以太网MAC地址
- 在协议栈内部有一块用于存放控制信息的内存空间,这里记录了用于控制通信操作的控制信息,例如通信对象的 IP 地址、端口号、通信操作的进行状态等
- 套接字只是一个概念而已,可以说这些控制信息就是套接字
- 协议栈根据套接字的信息进行活动,这就是套接字的作用
- 下图的每一行相当于一个套接字
PID:Process ID
下图是浏览器委托协议栈使用TCP协议来收发数据
- 协议栈首先会分配用于存放一个套接字所需的内存空间
- 然后向套接字的内存空间里写入表示这一初始状态的控制信息
- 这样一来套接字就创建完毕了
- 然后将这个套接字的描述符告诉应用程序;之后应用程序在向协议栈进行收发数据委托的时候就带着这个描述符,就能确定了相应的套接字,协议栈就能获取在相应套接字内相关的控制信息了
连接服务器
“连接”是什么意思
- 网线一直是连着的,随时都有信号从中流过,如果通信过程只是将数据转换为电信号,那么这个操作随时都可以进行
- 套接字创建完成的时候,里面还没有任何存放任何控制信息,这种状态下即使应用程序要求发送数据,协议栈也不知道发送给谁
- 协议栈需要等待我们在套接字中存入控制信息
- 服务器程序一般会在系统启动的时候就创建套接字并等待客户端连接
- 和客户端不同,服务器根本不知道想来通信的对象是谁
- 就和打电话一样,对方在没接到电话之前,根本不可能知道打电话的人是谁
- 连接实际上是通信双方交换控制信息,在套接字中记录这些必要信息并准备数据收发的一连串操作
- 连接操作中所交换的控制信息是根据通信规则来确定的,只要根据规则执行连接操作,双方就可以得到必要的信息从而完成数据收发的准备
通信操作中使用的控制信息可以分为两大类
- (1)客户端和服务器相互联络交换的控制信息
- 这些信息不仅连接时需要,包括数据收发和断开连接操作在内,整个通信过程都需要
- 这些内容在TCP协议中进行了定义
- 这些信息会添加在客户端和服务器之间传递的网络包头部(这里是TCP的控制信息,简称TCP头部)
- 其他协议也有自己的控制信息,以太网头部(MAC头部),IP头部
- (2)保存在套接字中,用来控制协议操作的信息
- 应用程序传递来的信息以及从通信对象接收到的信息都会保存在这里,还有收发数据操作的执行状态等信息也会保存在这里,协议栈会根据这些信息来执行每一步的操作
- 我们可以说,套接字的控制信息和协议栈的程序本身其实是一体的
- “协议栈具体需要哪些信息”会根据协议栈本身的实现方式不同而不同,但无论协议栈的实现如何不同,IP地址和端口号这些重要的信息都是共通的
- 虽然协议栈的实现不同,但是并不影响通讯;因为协议栈中的控制信息通信对方是看不见的,只要在通信时按照规则将必要的信息写入头部,客户端和服务器之间的通信就能够得以成立
- Windows 和Linux 操作系统的内部结构不同,协议栈的实现方式不同,必要的控制信息也就不同
- (1)客户端和服务器相互联络交换的控制信息
连接过程
- 连接操作的第一步是在TCP模块出创建表示连接控制信息的头部(TCP头部);通过TCP头部中的发送方和接受方端口号可以找到要连接的套接字
- 当TCP头部创建好之后,接下来TCP模块会将信息传递给IP模块并委托它进行发送
- IP 模块执行网络包发送操作后,网络包就会通过网络到达服务器
- 然后服务器上的 IP 模块会将接收到的数据传递给 TCP 模块
- 服务器的 TCP 模块根据 TCP 头部中的信息找到端口号对应的套接字
- 当找到对应的套接字之后,套接字中会写入相应的信息,并将状态改为正在连接
然后 TCP 模块会返回响应到客户端,重复和客户端一样的过程
- 服务器在返回响应的时候还需要再TCP头部设置发送方和接受方的端口号以及SYN比特,此外还要将ACK控制位设为1
- ACK 设为1表示以及接收到相应的网络包,ACK比特就是用来确认网络包是否送达的
- 服务器在返回响应的时候还需要再TCP头部设置发送方和接受方的端口号以及SYN比特,此外还要将ACK控制位设为1
客户端接收到响应信息并确认TCP头部信息的SYN为1,表示连接成功
- 接着就会向套接字中写入服务器的IP地址、端口号等信息;同时将状态改为连接完毕,
- 并将客户端的TCP头部ACK比特设置为1,发送给服务器,表明刚才的响应包已收到
- 这样连接就全部完成
收发数据
将 HTTP 请求信息交给协议栈
- 协议栈收到应用程序传来的数据会先存放到发送缓冲区,等到应用程序继续传送数据,避免发送大量小网络包,导致网络效率下降
协议栈发送数据要根据以下几个要素来判断
(1)每个网络包能容纳的数据长度,协议栈会根据一个叫作MTU(Maximum Transmission Unit)的参数来判断
- MTU表示每个网络包的最大长度,在以太网中一般是1500字节
- MTU 是包含头部的总长度,因此需要从MTU 减去头部的长度,然后得到的长度就是一个网络包中所能容纳的最大数据长度,这一长度叫作 MSS
- 当从应用程序收到的数据长度超过或者接近 MSS 时再发送出去
(2)时间
- 当应用程序发送数据的频率不高的时候,如果每次都等到长度接近 MSS 时再发送,可能会因为等待时间太长而造成发送延迟,这种情况下,即便缓冲区中的数据长度没有达到 MSS,也应该果断发送出去
- 为此,协议栈的内部有一个计时器,当经过一定时间之后,就会把网络包发送出去
- 两者其实是有矛盾的,具体如何达到平衡得看协议栈开发者
- 如果仅靠协议栈来判断发送的时机可能会带来一些问题,因此协议栈也给应用程序保留了控制发送时机的余地。应用程序在发送数据时可以指定一些选项,比如如果指定“不等待填满缓冲区直接发送”,则协议栈就会按照要求直接发送数据。像浏览器这种会话型的应用程序在向服务器发送数据时,等待填满缓冲区导致延迟会产生很大影响,因此一般会使用直接发送的选项
拆分较大的数据
- 如果数据很大,比如长篇文章,这样发送缓冲区中数据长度就会超过MSS的长度
- 这样时候就不需要继续等待后面的数据了,直接以MSS长度为单位拆分
- 拆分出来的数据放进单独的网络包,在数据前面加上TCP头部,IP头部,MAC头部,然后发送
使用 ACK 号确认网络包已收到
- TCP 具备确认对方是否成功收到网络包,以及当对方没收到时进行重发的功能
- TCP 模块在拆分数据的时候,会对数据做好标识,让服务器能够知道数据包有没有遗漏
- 返回 ACK 号时,除了要设置 ACK 号的值以外,还需要将控制位中的 ACK 比特设为 1,这代表 ACK 号字段有效,接收方也就可以知道这个网络包是用来告知 ACK 号的
实际的通信序号是随机的一个初始值,固定的序号容易被预测攻击
- 因此在收发数据之前就将初始值告诉通信对象
- 在连接过程中将 SYN(Synchronize) 控制位设为 1 的时候,就在这一步同时设置了初始值
上面的只是单项的数据传输,实际上客户端向服务器发送数据的同时,服务器也在向客户端发送数据
- 实际的初始值序号和ACK确认流程如下
- 因为有了 TCP 这个机制,我们可以确认接受方有没有收到某个包,如果没有收发则重新发送
- 这样一来,无论网络中发送任何错误,我们都可以发现并重传网络包
- 因此,网卡、集线器、路由器都不需要错误补偿机制了,一旦检测到错误,直接丢弃相应的包
- 在网络中断,服务器宕机,TCP 重传不起效,TCP会在尝试几次重传无效后强制结束通信,并向应用程序报错
根据网络包平均往返时间调整 ACK 号等待时间
前面的只是基本原理,实际上网络的错误检测和补偿机制非常复杂
设置一个合理的返回 ACK 号的等待时间
- 当网络拥塞的时候,ACK 号返回速度变慢,需要将等待时间设置长点,否则就会已经重传了包之后,前面的 ACK 号才姗姗来迟
- 重传了多余的包,数量多了之后会给本来就拥塞的网络雪上加霜
- 设置等待时间也不是越长越好,时间过长,包的重传就会出现很大的延迟,也会导致网络速度变慢
- 设置一个合理的值十分困难
- 服务器的物理距离,拥塞情况,等都要考虑
- 因此,TCP采用了动态调整等待时间的方法
- TCP 会在发送数据的过程中持续测量 ACK 号的返回时间,如果 ACK 号返回变慢,则相应延长等待时间;相对地,如果 ACK 号马上就能返回,则相应缩短等待时间
使用窗口有效管理 ACK 号
- 使用滑动窗口的方式,时间是充分利用了,但是可能会出现发送包的频率超过接受方处理能力的情况
- 接受方收到包后,会先将数据存放到接受缓冲区,然后计算ACK号,将数据块组装起来还原成数据并传递给应用程序
- 如果前面的操作没有完成,后面下一个包到了,会被暂存在接收缓冲区等待处理
- 如果数据到达的速率比处理这些数据并传递给应用程序的速率还要块,那么接受缓冲区中的数据越来越多,最终就会溢出
- 后面的数据包就收不到了,也就是超出了接受方的处理能力
- 为了避免这种情况,首先接受方需要告诉发送方字节最多能接收多少数据,然后发送方根据这个值对数据发送操作进行控制
- 这个能够接收的最大数据量称为 窗口大小 ,它是 TCP 调优参数中非常有名的一个
ACK 与 窗口的合并
- 要提高收发数据的效率,就要考虑返回ACK号和更新窗口的时机
- 更新窗口一般在接受方缓冲区剩余容量增加时,告知对方
- ACK 号在接受到后就告知对方
- 如果这两个是单独的包,就会导致接受方给发送方的包太多,从而导致网络效率下降
- 因此,接受方会等待一段时,把两种通知的包合并在一个包发送
接收 HTTP 响应消息
- 首先,协议栈会检查收到的数据块和 TCP 头部的内容,判断是否有数据丢失,如果没有问题则返回 ACK 号
- 然后,协议栈将数据块暂存到接收缓冲区中,并将数据块按顺序连接起来还原出原始的数据,最后将数据交给应用程序
- 具体来说,协议栈会将接收到的数据复制到应用程序指定的内存地址中,然后将控制流程交回应用程序
- 将数据交给应用程序之后,协议栈还需要找到合适的时机向发送方发送窗口更新
断开服务器并删除套接字
数据发送完毕后断开连接
- 服务器的协议栈会生成包含断开信息的TCP头部,将控制位中的 FIN 比特设为 1
- 接下来,协议栈会委托IP模块向客户端发送数据
- 同时,服务器的套接字中也会记录下来断开操作的相关信息
- 然后客户端接受到后,客户端的协议栈会将自己的套接字标记为进入断开操作状态
为了告诉服务器已经收到 FIN 为 1 的包,客户端会向服务器返回一个 ACK 号
应用程序有可能在收到 FIN 为 1 的包之前就来读取数据,这时读取数据的操作会被挂起,等到 FIN 包到达再继续执行
删除套接字
- 和服务器通信结束后,用来通信的套接字也不会再使用了
- 套接字并不会立刻被删除,而是等待一段时间之后再被删除
- 为了防止误操作
- 假设,客户端先发起断开,则断开的操作顺序如下
- (1)客户端发送 FIN
- (2)服务器返回 ACK 号
- (3)服务器发送 FIN
- (4)客户端返回 ACK 号
- 如果最后客户端返回的 ACK 号丢失了,结果会如何呢?这时,服务器没有接收到 ACK 号,可能会重发一次 FIN。如果这时客户端的套接字已经删除了,会发生什么事呢?套接字被删除,那么套接字中保存的控制信息也就跟着消失了,套接字对应的端口号就会被释放出来。这时,如果别的应用程序要创建套接字,新套接字碰巧又被分配了同一个端口号 B,而服务器重发的 FIN 正好到达,会怎么样呢?本来这个 FIN 是要发给刚刚删除的那个套接字的,但新套接字具有相同的端口号,于是这个 FIN 就会错误地跑到新套接字里面,新套接字就开始执行断开操作了。之所以不马上删除套接字,就是为了防止这样的误操作
TCP 整体流程
IP 与以太网的包收发操作
包的基本结构
- 网络包由头部和数据两部分构成
- 头部包含了目的地址等控制信息,就和快递的地址电话一样
- 数据就相当于快递物件
- 发送网络包的时候,发送方的网络设备会负责创建包,创建包的过程就是生成含有正确控制信息的头部,然后再附上要发送的数据
- 首先会发往最近的网络转发设备,再发往其他网络设备,层层转发,最终达到接受方的网络设备
- 路由器根据目标地址判断下一个路由器的位置
- 集线器在子网中将网络包传输到下一个路由
集线器是按照 以太网 规则传输包的设备;而路由器是按照 IP 规则传输包的设备
- IP协议根据目标地址判断下一个IP转发设备的位置
- 子网中的以太网协议将包传输到下一个转发设备
TCP/IP包含有两个头部:MAC头部(用于以太网协议);IP头部(用于IP协议)
包的收发操作
- 在整个传输流程中,是由各种网络设备合力完成的;IP模块仅仅只是整个包的传输流程的入口
- 包收发操作的起点是TCP模块委托IP模块发送网络包,这个过程中TCP模块在数据块前面加上TCP头部和指定通信对象的IP地址,然后传递给IP模块
- 收到委托后,IP模块将 前面的TCP头部和数据块 当作一个整体(二进制数据),然后在头部添加IP头部和MAC头部
- 然后封装好的包会发送给网络设备,最近的就计算机的网卡;传递给网卡的网络包是二进制的
- 接着网卡将二进制的数字信息转换为电信号/光信号,通过网线/光纤发送出去
- 然后接受方收到后,逆向操作
生成包含接受方 IP 地址的 IP 头部
IP头部最重要的内容就是接受方的IP地址,这个地址是由TCP模块告知的,而TCP模块又是在执行连接操作时从应用程序那里获得这个地址的
IP头部还需要有发送方的IP地址,这个IP地址一般大家认为是发送方的计算机IP地址
- 实际上,IP地址不是分配给计算机的,而是分配给网卡的,当计算机有多块网卡的时候就有多个IP地址(服务器就有很多块网卡)
- 如何判断把包给那块网卡呢
- 其实和路由器使用 IP 表判断下一个路由器位置的操作是一样的
- 因为协议栈的 IP模块与路由器中负责包收发的部分都是根据 IP 协议规则来进行包收发操作的,所以它们也都用相同的方法来判断把包发送给谁
- IP 表又可以叫做 路由表
- 我们对套接字中记录的目的地 IP 地址与路由表左侧的 Network Destination 栏进行比较,找到对应的一行
- 例如,TCP 模块告知的目标 IP 地址为 192.168.1.21,那么就对应图中的第 6 行,因为它和 192.168.1 的部分相匹配
- 如果目标 IP 地址为 10.10.1.166,那么就和 10.10.1 的部分相匹配,所以对应第 3 行
- Interface 列,表示网卡等网络接口,这些网络接口可以将包发送给通信对象
- Gateway 列表示下一个路由器的 IP 地址,将包发给这个 IP 地址,该地址对应的路由器就会将包转发到目标地址
路由表的第 1 行中,目标地址和子网掩码 A 都是 0.0.0.0,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行
最后还需要填写协议号,它标识包的内容来自哪个模块
- TCP模块,06(十六进制)
- UDP模块,17(十六进制)
生成以太网用的 MAC 头部
- 生成了 IP 头部之后,接下来 IP 模块还需要在 IP 头部的前面加上MAC 头部
- IP 头部中的接收方 IP 地址表示网络包的目的地,通过这个地址我们就可以判断要将包发到哪里
- 但在以太网的世界中,TCP/IP 的这个思路是行不通的
以太网在判断网络包目的地时和 TCP/IP 的方式不同,因此必须采用相匹配的方式才能在以太网中将包发往目的地,而MAC 头部就是干这个用的
MAC 头部是以太网使用的头部,它包含了接受方和发送方的 MAC 地址信息等
- IP 地址长度 32bit ; MAC 地址长度 48 bit
以太类型和协议号类似
- 在IP中,协议号标识IP头部后面的包内容的类型
- 在以太网中,可以认为以太网类类型后面就是以太网包的内容
- 而以太类型就表示后面内容的类型
- 以太网包的内容可以是IP、ARP等协议的包
以太类型,这里可以填写表示IP协议的值 0800(十六进制)
然后填写网卡本身的MAC地址,MAC地址是网卡生产时写入ROM里的,只要读取出来写入MAC头部就可以了
最后就和IP地址一样判断从那块网卡发送网络包,把这块网卡的MAC地址填进去
- 但是,接受方的MAC地址根本不知道,所以我们还需要根据IP地址查询MAC地址的操作
通过 ARP 查询目标路由器的 MAC 地址
ARP:Address Resolution Protocol 地址解析协议
ARP 利用广播对连接在同一以太网中的所有设备查询 MAC 地址
- 如果每次发送包都要这样查询一次,网络中就会增加很多 ARP包,因此我们会将查询结果放到一块叫作 ARP 缓存的内存空间中留着以后用
- 也就是说,在发送包时,先查询一下 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址,而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 查询
ARP缓存中的地址会在一定时间后被删除,避免读取到IP地址发生变化后造成的差异
将 MAC 头部加在 IP 头部前面,整个包就完成了
- 到此为止,整个打包工作是由 IP 模块完成的
- 这样的好处是,在把数据包交给网卡之前就打包好
- 这样一来,网卡就可以支持各种类型的包,网卡专注于收发
以太网的结构
- IP 模块的工作完成后,就轮到网卡开始工作了
- 以太网是一种为多台计算机能够彼此自由和廉价地相互通信而设计的通信技术
- 收发器:将不同网线之间的信号连接起来
- 尽管以太网经历了数次变迁,但其基本的 3 个性质至今仍未改变
- 即将包发送到 MAC 头部的接收方 MAC 地址代表的目的地
- 用发送方 MAC地址识别发送方
- 用以太类型识别包的内容
- 因此,可以认为具备这3 个性质的网络就是以太网
将 IP 包转换成电或光信号发送出去
- IP 生成的网络包只是存放在内存中的一串数字信息
- 因此,需要将数字信号转换成电信号或者光信号,才能在网线上传输
- 负责执行这个操作的就是网卡,但是网卡也无法单独工作,需要控制网卡就需要网卡驱动程序
- MAC:Media Access Control
- 可以从网卡 ROM 中读出 MAC 值 设置给 MAC 模块
- 也可以从命令或配置中读取 MAC 值
- 这种情况下,网卡会忽略ROM 中的 MAC 地址
- 有人认为在网卡通电之后,ROM 中的 MAC 地址就自动生效了,其实不然,真正生效的是网卡驱动进行初始化时在 MAC模块中设置的那个 MAC 地址
给网络包再加3个控制数据
- 网卡驱动从IP模块获取到包之后,将其复制到网卡内的缓冲区中,然后向 MAC 模块发送 “发送包” 的指令
MAC 模块将包从缓冲区中取出,并在开头添加 报头、起始帧分节符,在末尾添加用于检测错误的帧校验序列 (历史原因,帧和包是一个意思)
报头是一串像 10101010…这样 1 和 0 交替出现的比特序列,长度为 56比特,它的作用是确定包的读取时机
- 当这些 1010 的比特序列被转换成电信号后,会形成如下图这样的波形
- 接收方在收到信号时,遇到这样的波形就可以判断读取数据的时机
- 用电信号来表达数字信息是,需要让0和1两种比特分别对应特定的电压和电流
- 在测量电压和电流的时候,必须先判断出每个比特的界限在哪,要不连续同种比特出现的时候就很难切分(连续1111或00000)
- 为了解决这个问题,引入一组用来区分比特间隔的时钟信号
- 当时钟信号变化时,读取电压和电流值,和0或1对应
- 但是距离较远时,网线较长时,数据信号和时钟信号的传输会产生时间差,时钟就会发生偏移
- 为了解决偏差,采用将数据信号和时钟信号叠加在一起的方法
向集线器发送网络包
- 发送信号的操作分为两种
- 一种是使用集线器的半双工模式
- 另一种是使用交换机的全双工模式
- 在半双工模式中,为了避免信号碰撞,需要先判断网线中是否存在其他信号设备发送信号
- 有则等待,没有即可发送
- 首先,MAC 模块从报头开始将数字信号按每个比特转成电信号,然后由PHY或叫MAU的信号收发模块发送出去
- MAU:Medium Attachment Unit 介质连接单元
- PHY:Physical Layer Device 物理层装置
- 根据以太网的信号方式的不同,叫法不同,速度在100Mbit/s以上的以太网中都叫PHY
- 在这里,将数字信号转换成电信号的速率就是网络传输速率
- 接下来,PHY模块会将信号转换为可以在网线上传输的格式,并通过网线发送出去
- 以太网规格中对不同的网线类型和速率以及其对应的信号格式进行了规定
- 但 MAC 模块并不关心这些区别,而是将可转换为任意格式的通用信号发送给 PHY(MAU)模块
- 然后 PHY(MAU)模块再将其转换为可在网线上传输的格式
- PHY不仅仅只是转换信号,还需要监控接收线路有无信号进来,避免信号碰撞
如果发生了信号碰撞,还会发送一段阻塞信号,通知所有发送操作停止
- 并且根据 MAC 地址生成随机数,错开重新发送信号时间
- 当网络拥塞的时候,发送碰撞的可能性提高
- 多次拥塞等待后,就报告通信错误
另一种全双工模式,可以同时接收和发送,不会发生碰撞
接收网络包
- PHY (MAU)模块会将信号转换成通用格式并发送给 MAC 模块
- MAC 模块再从头开始将信号转换为数字信息,并存放到缓冲区中
- 当到达信号的末尾时,还需要检查 FCS
- 检查 MAC 头部中接收方MAC 地址与网卡在初始化时分配给自己的 MAC 地址是否一致
无误后,网卡通知计算机收到了一个网络包
计算机并不是一直监控着网卡,需要网卡去打断计算机正在执行的任务,注意到自己,这种机制就是中断
将服务器的响应包从 IP 传递给 TCP
- 首先收到包,肯定是先检查以太类型,确定是什么协议;0800就是TCP/IP
- 接着检查IP头部、查看接受方IP地址
- 如果IP格式无误,接受方IP地址和客户端网卡地址一致,就可以接收这个包了
- 如果接受方的IP不是自己的,发生了这样的错误时,IP模块会通过ICMP消息将错误告诉发送方
UDP协议的收发操作
不需要重发的数据用 UDP 发送更高效
- TCP 设计得如此完善,就是要确保它的数据可靠性
- 如果数据很短,不用分包,一个包就足够的情况下,就不需要考虑漏包等问题了
- 一旦丢失或未送达就重发,就就一个包,根本不影响效率
- 这样就不需要 TCP 这么复杂的机制了
- 发送了数据,对方给出回复就可以
控制用的短数据
- 像 DNS 查询等交换控制信息的操作基本上都可以在一个包的大小范围就可以解决,这种场景就可以用 UDP 来代替 TCP
- UDP 没有 TCP 的接收确认、窗口等机制,因此在收发数据之前也不需要交换控制信息,也就是说不需要建立建立和断开连接的步骤
- 只需要从应用程序获取的数据前面加上 UDP 头部,然后交给 IP 模块发送就可以了
- 在线播放音频和视频数据的时候,也会使用 UDP ,因为这些数据必须在规定时间内达到,否则会错过时机,导致卡顿
- 一旦错过时间,重发数据也没有用;
- 在这些无需重发,或者重发也没意义的场景,用 UDP 效率更高
从网线到网络设备—集线器、交换机、路由器
信号在网线和集线器中传输
- 每个包都是独立传输的
- 每个阶段的都不会关心上个阶段的包内部内容
- 每个后面的阶段只会根据上阶段传输过来的包头部中的控制信息,进行操作
- 电信号会在传输过程中逐渐衰弱
- 电信号频率越高,能量损失率越大,因此传输过程就会失真
- 如果再加上噪音,失真就更厉害,进一步影响0,1的判断,这就是产生通信错误的原因
- 局域网使用的网线就是双绞线,双绞就是把两根信号线为一组缠绕在一起,这样就是为了抑制噪音
- 产生噪音的原因就是网线周围的电磁波,电磁波接触到导体时会在其中产生电流,干扰原有的电流
- 产生噪音的原因就是网线周围的电磁波,电磁波接触到导体时会在其中产生电流,干扰原有的电流
影响网线的电磁波分为两种
- 一种是电机、荧光灯、CRT显示器等设备泄露出来的电磁波,这类电磁波来自网线外部
- 另一种是是从网线相邻的信号线泄露出来的,这类内部产生的噪音称为串扰
除了用“双绞”的方式外,在信号线之间加入隔板距离以及在外面包裹可阻挡电磁波的金属屏蔽网等也可以抑制信号干扰
- 网线的性能是以“类”来区分的
- 当信号到达集线器后,会被广播到整个网络中
- 以太网的基本架构就是将包发到所有的设备
- 然后由设备根据接收方 MAC 地址来判断应该接收哪些包,
- 而集线器就是这一架构的忠实体现,它就是负责按照以太网的基本架构将信号广播出去
交换机的包转发操作
交换机根据地址表进行转发
- 信号到网线接口,由PHY模块接收,转换成数字信号,校验包末尾的FCS,无误则存放到缓冲区
- 交换机一个端口可以当作一个网卡,但是交换机端口不具有MAC地址
- 存到缓冲区之后,就会查询这个包的接受方地址是否在MAC地址表中
- 该MAC地址表与端口一一对应,就能判断出包该转发到哪个端口
MAC 地址表的维护
- 交换机在转发包的过程中,还需要对MAC地址表内容进行维护
- 维护分两种
- 交换机查询地址表之后发现记录中的目标端口和这个包的源端口是同一个端口
- 当像上图这样用集线器和交换机连接在一起时就会遇到这样的情况
- 首先,计算机 A 发送的包到达集线器后会被集线器转发到所有端口上,也就是会到达交换机和计算机 B
- 这时,交换机转发这个包之后,这个包会原路返回集线器
- 然后,集线器又把包转发到所有端口,于是这个包又到达了计算机 A 和计算机 B
- 所以计算机 B 就会收到两个相同的包,这会导致无法正常通信
- 因此,当交换机发现一个包要发回到原端口时,就会直接丢弃这个包
- 地址表中找不到指定的MAC地址
- 这可能是因为具有该地址的设备还没有向交换机发送过包,或者这个设备一段时间没有工作导致地址被从地址表中删除了
- 这种情况下,交换机无法判断应该把包转发到哪个端口,只能将包转发到除了源端口之外的所有端口上
- 无论该设备连接在哪个端口上都能收到这个包
- 这样做不会产生什么问题,因为以太网的设计本来就是将包发送到整个网络的,然后只有相应的接收者才接收包,而其他设备则会忽略这个包
全双工模式可以同时进行发送和接收
- 全双工模式是交换机特有的工作模式,它可以同时进行发送和接收操作,集线器不具备这样的特性
- 使用集线器时,如果多台计算机同时发送信号,信号就会在集线器内部混杂在一起,进而无法使用
- 这种现象称为碰撞,是以太网的一个重要特征
- 不过,只要不用集线器,就不会发生碰撞
- 使用双绞线时,发送和接收的信号线是各自独立的
- 如果不存在碰撞,也就不需要半双工模式中的碰撞处理机制了
- 也就是说,发送和接收可以同时进行
- 然而,以太网规范中规定了在网络中有信号时要等该信号结束后再发送信号,因此发送和接收还是无法同时进行
- 于是,人们对以太网规范进行了修订,增加了一个无论网络中有没有信号都可以发送信号的工作模式,同时规定在这一工作模式下停用碰撞检测,这种工作模式就是全双工模式
- 在全双工模式下,无需等待其他信号结束就可以发送信号,因此它比半双工模式速度要快
- 由于双方可以同时发送数据,所以可同时传输的数据量也更大,性能也就更高
自动协商:确定最优传输速率
- 随着全双工模式的出现,如何在全双工和半双工模式之间进行切换的问题也产生了
- 在全双工模式出现的时候,需要手动切换模式
- 后来出现了自动切换模式的模式,可以由相互连接双方检测对方是否支持全双工模式,并自动切换成对应的工作模式
- 此外,还能探测对方的传输速率进行自动切换,这种自动切换称为自动协商
在以太网中,当没有数据在传输时,就会填充一种叫做连接脉冲的脉冲信号
- 使得网络中一致保持一定的信号流过,从而能够检测对方是否在正常工作,或者说网线有没有正常连接
- 以太网设备的网线接口周围有个绿色指示灯,表示是否检测到正常的脉冲信号
- 灯亮,说明PHY模块以及网线正常
- 灯亮,说明PHY模块以及网线正常
后来,人们又设计出了上图具有特定排列的脉冲信号,通过这种信号可以将自身的状态告知对方
- 自动协商就利用了这样的脉冲信号,将自己能够支持的工作模式和传输速率相互告知对方,并从中选择一个最优的组合
- 假设现在连接双方的情况如上表所示
- 网卡一方支持所有的速率和工作模式,而交换机只支持到 100 Mbit/s 全双工模式
- 当两台设备通电并完成硬件初始化之后,就会开始用脉冲信号发送自己支持的速率和工作模式
- 当对方收到信号之后,会通过读取脉冲信号的排列来判断对方支持的模式,然后看看双方都支持的模式有哪些
- 表是按照优先级排序的,因此双方都支持的模式就是第 3 行及以下的部分
- 越往上优先级越高,因此在本例中 100 Mbit/s 全双工模式就是最优组合
- 于是双方就会以这个模式开始工作
交换机可以同时执行多个转发操作
- 交换机只将包转发到具有特定 MAC 地址的设备连接的端口,其他端口都是空闲的
- 如图,当包从最上面的端口发送到最下面的端口时,其他端口都处于空闲状态,这些端口可以传输其他的包,
- 因此交换机可以同时转发多个包
- 相对地,集线器会将输入的信号广播到所有的端口
- 如果同时输入多个信号就会发生碰撞,无法同时传输多路信号
- 因此从设备整体的转发能力来看,交换机要高于集线器
路由器的包转发操作
路由器的基本结构
- 路由器也是通过查表判断包转发的目标的,工作原理和交换机类似
- 不过路由器是基于IP设计的,交换机是基于以太网设计的
转发模块负责判断包的转发目的地,端口模块负责包的收发操作
- 换句话说,路由器转发模块和端口模块的关系,就相当于协议栈的 IP 模块和网卡之间的关系
- 因此,可以将路由器的转发模块想象成 IP 模块,将端口模块想象成网卡
- 通过更换网卡,计算机不仅可以支持以太网,也可以支持无线局域网,路由器也是一样
- 如果路由器的端口模块安装了支持无线局域网的硬件,就可以支持无线局域网了
- 此外,计算机的网卡除了以太网和无线局域网之外很少见到支持其他通信技术的品种
- 而路由器的端口模块则支持除局域网之外的多种通信技术,如ADSL、FTTH,以及各种宽带专线等,只要端口模块安装了支持这些技术的硬件即可
端口模块是以实际的发送方或接受方的身份来执行包收发操作的
- 意味着路由器的各个端口都具有MAC地址和IP地址
- 而交换机只是将进来的包转发出去而已,它自己并不会称为发送方或者接受方
路由表中的信息
- 交换机是通过MAC头部中的接受方MAC地址来判断转发目标的,而路由器是根据IP头部中的IP地址来判断的
- 路由器会忽略主机号,只匹配网络号
路由器的包收发操作
- 首先,信号到达网线接口部分,其中的 PHY(MAU)模块和 MAC 模块将信号转换为数字信息
- 然后通过包末尾的 FCS 进行错误校验,
- 如果没问题则检查 MAC 头部中的接收方 MAC 地址,
- 看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包
- 如果包的接收方 MAC地址不是自己,说明这个包是发给其他设备的,如果接收这个包就违反了以太网的规则
查询路由表确定输出端口
- 路由器接收到包之后,就会丢弃包开头的MAC头部
- 因为这个包的MAC头部就是为了将包送达路由器,之后就没作用了,自然被丢弃
- 因为这个包的MAC头部就是为了将包送达路由器,之后就没作用了,自然被丢弃
- 然后就根据MAC头部后方的IP头部中的内容判断转发操作
- 查询路由表判断转发目标,根据包的接受方IP查询路由表中的目标栏,找到相匹配的记录
- 如果路由表中无法找到匹配的记录,则会丢弃这个包,通过ICMP消息告知发送方
- 这里和交换机不一样,交换机连接的的网络最多也就几千台设备的规模,交换机找不到匹配的记录的时候,会发送到所有端口
- 但是路由器的工作设备都在互联网上,规模还在持续扩大,因此为了不造成网络拥塞,只能将匹配不到对应记录的包丢弃
找不到匹配路由时选择默认路由
互联网中那么多转发目标,不可能都配置在路由器中
- 关键就在于把子网掩码设置为 0.0.0.0
- 这样无论任何地址都能匹配到这条记录,就不会发生匹配不到的记录了
- 这个配置的网关地址被称为默认网关,和在计算机里面设置TCP.IP填的默认网关一样
- 关键就在于把子网掩码设置为 0.0.0.0
这样一来就可以层层追溯查找出转发目标
包的有效期
- 找到转发目标后,还会更新IP头部中的TTL(Time to live)
- TTL表示了包的有效期,包每经过一个路由器的时候,这个值就会减1,当这个值为0的时候,就会被丢弃
- 这个机制是为了包在一个地方进入死循环
- 发生死循环的情况在于路由表配置信息有误,或者网络故障
- 发送方在发送包时会将 TTL 设为 64 或 128,也就是说包经过这么多路由器后就会“寿终正寝”
- 现在的互联网即便访问一台位于地球另一侧的服务器,最多也只需要经过几十个路由器,因此只要包被正确转发,就可以在过期之前到达目的地
通过分片功能拆分大网络包
- 路由器的端口并不只有以太网一种,也可以支持其他局域网或专线通信技术
由于不同线路的输出输入端口的最大包长度不同,所以为了避免数据包受影响,而采用IP协议中的分片功能对包进行拆分
- 这里和TCP拆分的机制不同
- TCP是将拆好的数据块正好装进一个包里
- 从IP角度来看,这个包还是一个未拆分的整体
- IP分辨是对一个完整的包再拆分的过程
- 这里和TCP拆分的机制不同
MTU:包能传输的最大数据长度
- 首先查看IP头部的标志字段,确认是否可以分片(一般都是可以的,除非发送方设置了不允许,或者这个包已经被分片过了)
如果不能分片,只能丢弃,并通过ICMP消息通知发送方
在分片中,TCP以及后面的数据都是可以分片的,从IP角度来看,TCP的请求数据也是网络包数据的一部分
- 然后就会在每个分片加上IP头部,MAC头部
路由器的发送操作和计算机相同
- 准备工作完成就会进入包的发送操作
- 将包转换成什么信号,取决于输出端口的类型,以太网则是电信号
- 将包转换成什么信号,取决于输出端口的类型,以太网则是电信号
路由器和交换机的关系
- 路由器在转发网络包的时候,都要在包的头部加上MAC头部
- 准确说,应该是将IP网络包装进以太网包的数据部分
- 委托以太网去传输这些数据
- IP协议本身没有传输包的功能,包的实际传输要委托以太网来执行
- 路由器是基于IP设计的,交换机是基于以太网设计的
- IP与以太网的关系,也就是路由器与交换机的关系
- 当然上面的基本理论只适用于“纯粹”的路由器和交换机
目前的路由器都内置有交换机功能了,但是底层本质还是不变
从包的转发目标也可以看出路由器和交换机之间的委托关系。IP 并不是委托以太网将包传输到最终目的地,而是传输到下一个路由器。在创建MAC 头部时,也是从 IP 的路由表中查找出下一个路由器的 IP 地址,并通过 ARP 查询出 MAC 地址,然后将 MAC 地址写入 MAC 头部中的,这表示 IP 对以太网的委托只是将包传输到下一个路由器就行了。当包到达下一个路由器后,下一个路由器又会重新委托以太网将包传输到再下一个路由器。随着这一过程反复执行,包就会最终到达 IP 的目的地,也就是通信的对象
路由器的附加功能
- 地址转换、包过滤
- 最初的互联网没有内外网区分,所有客户端都是直接连接到互联网的,并每台设备都能够分配到属于自己的IP地址
- 后来设备越来越多,为了解决这个问题才有了内外网的概念
- 内网地址-私有地址;外网地址-公有地址
- 内网可用的私有地址
- 10.0.0.0 ~ 10.255.255.255
- 172.16.0.0 ~ 172.31.255.255
- 192.168.0.0 ~ 192.168.255.255
- 为什么上面这些地址用作私有地址,其实在制定私有地址的时候,上面那些地址属于公有地址中还没被分配的范围而已
- 这样就解决了内外网分配不足的问题,但是两个内网想要通过互联网通信并且私有地址又相同怎么办
- 这样的情况就需要用到了路由器的地址转换功能
地址转换的原理
- 地址转换的基本原理是在转发网络包时对IP头部中的IP地址和端口号进行改写
- 具备地址转换功能的设备不仅有路由器,有些防火墙也有地址转换功能
改写端口号的原因
- 现在的地址转换机制是同时改写地址和端口号,但是早期的地址转换只是改写地址
- 但是这种方法的前提是私有地址和公有地址一一对应
- 需求数量多起来共有地址就不够用了,因此也要改写端口号
- 端口号是一个16bit的数值,可用分配出几万个端口,就完全够用了
从互联网访问公司内网
- 没有在访问互联网的内网设备,是无法从互联网想起发起网络包的
- 因为从内网去访问外网的时候,用来改写的公有地址就是地址转换设备自身的地址,端口号随便选个空闲的就行
- 从外网去访问内网的话,因为在地址表中没有记录,意味着地址转换设备无法判断公网地址和内网地址的对应关系
- 但是如果想要从外网去访问内网的话,需要手动添加对应的关系记录
路由器的包过滤功能
- 包过滤就是在对包进行转发时,根据MAC头部、IP头部、TCP头部的内容,按照事先设置好的规则决定是转发这个包,还是丢弃这个包
- 防火墙等软件,大多数就利用这个机制来防止非法入侵的
通过接入网进入互联网内部—接入网、网络运营商
ADSL 接入网的结构和工作方式
接入网,是之连接互联网与家庭、公司网络的通信线路
- 接入网这个词表示的是通信线路的用法,而不是表示通信线路的结构
- 公司里使用的专线,当它用来连接互联网的时候叫做接入网;而用来连接总公司和分公司的时候就不叫接入网
- 这个词不局限于互联网,运营商提高通信服务时,将用户和运营商之间的线路叫做接入网
- 接入网这个词表示的是通信线路的用法,而不是表示通信线路的结构
互联网接入路由器是按照入网规则来发送网络包的
一般加用接入网方式:ADSL、FFTH、电话线、ISDN等
- ADSL:Asymmetric Digital Subscriber Line 不对称数字用户线
- 它是一种利用架设在电线杆上的金属电话线来通信的技术、上行与下行的通信速率不对称
- 上行:用户到互联网(上传);下行:互联网到用户(下载)
- FTTH:Fiber TO The Home 光纤入户
- ADSL:Asymmetric Digital Subscriber Line 不对称数字用户线
ADSL Modem 工作方式
- 上图中的网络包是从右向左传输的
网络包从用户传输到运营商的过程中,会变换几种不同的形态:
客户端生成的网络包 1、2,先经过集线器和交换机到达互联网接入路由器 3
- 互联网接入路由器从以太网包中取出IP包并判断转发目标 4
- 然后根据互联网接入路由器和ADSL Modem之间的以太网规则添加头部信息 5
- ADSL Modem 接收到网络包 6 ,然后拆分成信元 7
- 信元是一个非常小的数据库,开头是有5个字节的头部,后面是48字节的数据,用于一种叫做ATM的通信技术(Asynchronous Transfer Mode 异步传输)
将信元转换成电信号 8 (将数字信号转换成模拟信号)
- 以太网采用的是方波信号表示0和1的方式,而ADSL采用圆滑波形(正弦波)来表示0和1的技术,称为 调制
调制有很多方式,ADSL 采用的调制方式是振幅调制(ASK)和相位调制(PSK)相结合的正交振幅调制(QAM)方式
- 振幅调制是用信号的强弱,也就是信号振幅的大小来对应 0 和 1 的方式
- 如图,振幅小的信号为 0,振幅大的信号为 1,这是一种最简单的对应关系。在这个例子中,振幅大小只有两个级别,如果增加振幅变化的级别,就可以对应更多的比特
- 例如,如果将振幅增加到 4 个级别,则振幅从小到大可分别对应 00、01、10 和 11,这样就可以表示两个比特了
- 这样做可以将单位时间内传输的数据量加倍,也就能够提高速率。以此类推,如果振幅有 8 个级别,就可以表示 3 个比特,16 个级别就可以表示 4 个比特,速率也就越来越高
- 不过,信号会在传输过程中发生衰减,也会受到噪声影响而失真,如果振幅级别太多,接收方对信号的识别就容易出错,因此振幅级别也不能太多
- 组成要素是相位调制,这是一种根据信号的相位来对应 0 和 1的方式
- Modem 产生的信号是以一定周期振动的波,如图所示,振动的起始位置不同,波的形状也就不同
- 如果将波的一个振动周期理解为一个圆,则起始位置就可以用 0 度到 360 度的角度来表示,这个角度就是相位,用角度来对应 0 和 1 的方式就叫作相位调制
- 例如,从 0 度开始的波为 0,从 180 度开始的波为 1,这是一种最简单的对应关系,如图所示
- 和振幅调制一样,相位调制也可以通过将角度划分为更细的级别来增加对应的比特数量,从而提高速率
- 但是,角度太接近的时候也容易产生误判,因此这样提升速率还是有限度的
- ADSL 使用的正交振幅调制就是将前面这两种方式组合起来实现的
- 正交振幅调制中,通过增加振幅和相位的级别,就可以增加能表示的比特数
- 例如,如果振幅和相位各自都有 4 个级别,那么组合起来就有 16个级别,也就可以表示 4 个比特的值
- 当然,和单独使用振幅调制或相位调制的情况一样,级别过多就容易发生误判,因此这种方法提升的速率是有限度的
- 实际上信号不一定要限制在一个频率
- 不同频率的波可以合成,也可以用滤波器从合成的波中分离出某个特定频率的波
- 因此,我们可以使用多个频率合成的波来传输信号,这样一来,能够表示的比特数就可以成倍提高了
- ADSL 就是利用了这一性质,通过多个波增加能表示的比特数来提高速率的
ADSL 技术中,上行方向(用户到互联网)和下行方向(互联网到用户)的传输速率是不同的,原因也在这里
- 如果上行使用 26 个频段,下行则可以使用 95 个或者 223 个频段,波的数量不同,导致了上下行速率不同
ADSL Modem 将信元转换为电信号之后,信号会进入一个叫作分离器的设备,然后 ADSL 信号会和电话的语音信号混合起来一起从电话线传输出去
- 在信号从用户端发送出去时,电话和 ADSL 信号只是同时流到一条线路上而已,分离器实际上并没有做什么事
- 分离器的作用其实在相反的方向,也就是信号从电话线传入的时候;这时,分离器需要负责将电话和 ADSL 的信号进行分离
- 电话线传入的信号是电话的语音信号和 ADSL 信号混合在一起的,如果这个混合信号直接进入电话机,ADSL 信号就会变成噪音,导致电话难以听清
- 为了避免这样的问题,就需要通过分离器将传入的信号分离,以确保 ADSL信号不会传入电话机
- 具体来说,分离器的功能是将一定频率以上的信号过滤掉,也就是过滤掉了 ADSL 使用的高频信号,这样一来,只有电话信号才会传入电话机,但对于另一头的 ADSL Modem,则是传输原本的混合信号给它
- ADSL Modem 内部已经具备将 ADSL 频率外的信号过滤掉的功能,因此不需要在分离器进行过滤
- 从分离器出来后,就是电话线的接口,通过室内电话线到达大楼的中间配线盘和主配线盘
- 这里将外面的电话线和室内的电话线连接起来
- 通过配线盘后,信号达到保安器;保安器是为了防止雷电
- 然后信号进入电线杆上架设的电话电缆
- 然后沿着电线杆侧面的金属管进入地下,电话局附近的地下都是电缆
- 信号要通过电话线到达电话局之后,会经过配线盘、分离器到达 DSLAM 9 (DSL Access Multiplexer 数字用户接入复用设备)
- DSLAM 它是一种电话局用的多路ADSL Modem ,可用理解为多个ADSL Modem整合在一个外壳里的设备
- 然后电信号会被还原成数字信息——信元 10
- 接着到达 BAS 的包转发设备 11 ,在这里将 ATM 信元还原成原始的包 12
- 然后 BAS 取出数据 13 ;MAC 头部和 PPPoE 头部的作用是将包送达到 BAS ,完成任务后被丢弃
- 紧接着,在包前面加上隧道专用头部 14,并发送到隧道的出口
- 网络包达到隧道出口的隧道专用路由器 15,在这里丢弃掉隧道专用头部并取出 IP 包 16
- 最后转发到互联网内部 17
光纤接入网(FTTH)
光纤的基本结构
- 它由一种双层结构的纤维状透明材质(玻璃和塑料)构成,通过在里面的纤芯中传导光信号来传输数字信息,亮表示1,暗表示0
- 先将数字信息转换成电信号,再将电信号转换成光信号
- 电信号,1用高电压表示,0用低电压表示
- 这些光源就会根据信号电压的变化发光,高电压发光亮,低电压发光暗
- 这样的光信号在光纤中传导之后,就可以通过光纤到达接收端
- 接收端有可以感应光线的光敏元件,光敏元件可以根据光的亮度产生不同的电压
- 当光信号照射到上面时,光亮的时候就产生高电压,光暗的时候就产生低电压,这样就将光信号转换成了电信号
- 最后再将电信号转换成数字信息,我们就接收到数据了
单模和多模
光纤通信的关键技术就是能够传导光信号的光纤
光源在所有方向上都会发光,因此会有各种角度的光线进入纤芯,但入射角度太大的光线会在纤芯和包层(纤芯外沿部分)的边界上折射出去,只有入射角较小的光线会被包层全反射,从而在纤芯中前进
也不是所有入射角小的光线都会在纤芯中传导
- 光也是一种波,当光线在纤芯和包层的边界上反射时,会由于反射角产生相位变化
- 当朝反射面前进的光线和被反射回来的光线交会时,如果两条光线的相位不一致,就会彼此发生干涉抵消,只有那些相位一致的光线才会继续在光纤中传导
大多数角度都会因为相位不同而被干涉抵消,只有少数按照特定角度入射以保持一致的光纤才会继续传导
根据纤芯直径,光纤可以划分成几种类型,大体上包括较细的单模光纤(8~10 μm)和较粗的多模光纤(50 μm 或 62.5 μm)
单模光纤的纤芯很细,只有入射角很小的光线才能进,因此在能够保持相位一致的角度中,只有角度最小的光线能进入光纤
- 单模光纤的纤芯直径就是按照只允许相位一致的最小角度的光进入而设计的
多模光纤的纤芯比较粗,入射角比较大的光也可以进入,这样一来,在相位一致的角度中,不仅角度最小的可以在光纤中传导,其他角度更大一些的也可以,也就是说,可以有多条光线在纤芯中同时传导
单模和多模实际上表示相位一致的角度有一个还是多个
单模光纤和多模光纤在光的传导方式上有所不同,这决定了它们的特性也有所不同
- 多模光纤中可以传导多条光线,这意味着能通过的光线较多,对光源和光敏元件的性能要求也就较低,从而可以降低光源和光敏元件的价格
- 相对地,单模光纤的纤芯中只能传导一条光线,能通过的光线较少,相应地对于光源和光敏元件的性能要求就较高,但信号的失真会比较小
信号失真与光在纤芯传导时反射的次数相关
- 多模光纤中,多条反射角不同的光线同时传导,其中反射角越大的光线反射次数越多,走过的距离也就越长;
- 相对地,反射角越小的光线走过的距离越短。光通过的距离会影响其到达接收端的时间,也就是说,通过的距离越长,到达接收端的时间越长
- 结果,多条光线到达的时间不同,信号的宽度就会被拉伸,这就造成了失真
- 因此,光纤越长,失真越大,当超过允许范围时,通信就会出错
- 相对地,单模光纤则不会出现这样的问题。因为在纤芯传导的光线只有一条,不会因为行进距离的差异产生时间差,所以即便光纤很长,也不会产生严重的失真
- 光纤的最大长度也是由上述性质决定的。单模光纤的失真小,可以比多模光纤更长
- 因此多模光纤主要用于一座建筑物里面的连接,单模光纤则用于距离较远的建筑物之间的连接
- FTTH 属于后者,因此主要使用单模光纤
通过光纤分路来降低成本
- 用光纤来代替 ADSL 将用户端接入路由器和运营商的 BAS 连接起来的接入方式就是 FTTH ,从形态上可大致分为两种
- 一种是用一根光纤直接从用户端连接到最近的电话局
- 首先,用户端的光纤收发器将以太网的电信号转换成光信号
- 这一步只进行电信号到光信号的转换,而不会像 ADSL 一样还需要将包拆分成信元,将以太网包原原本本地转换成了光信号
- 接下来,光信号通过连接到光纤收发器的光纤直接到达 BAS 前面的多路光纤收发器
- FTTH 一般使用单模光纤,因此其纤芯中只有特定角度的光信号能够反射并前进
- 然后,多路光纤收发器将光信号转换成电信号,BAS的端口接收之后,将包转发到互联网内部
- 把网络包发送到互联网之后,服务器会收到响应,响应包的光信号也是沿着同一条光纤传输到用户端
- 这里,前往互联网的上行光信号和前往用户的下行光信号在光纤中混合在一起,信号会变得无法识别
- 因此需要对它们进行区分,办法是上行和下行信号采用不同波长的光
- 波长不同的光混合后可通过棱镜原理进行分离,因此光纤中的上行和下行信号即便混合起来也可以识别
- 像这样在一条光纤中使用不同的波长传输多个光信号的方式叫作波分复用
- 另一种光纤的接入方式是在用户附近的电线杆上安装一个名为分光器的设备,通过这个设备让光纤分路,同时连接多个用户
- 在这种方式下,用户端不使用光纤收发器,而是使用一个叫作 ONU(Optical Network Unit 光网络单元) 的设备,它将以太网的电信号转换成光信号之后,会到达 BAS 前面的一个叫作 OLTC(Optical Line Terminal 光线路终端)的设备
- 信号的传导方式和刚才介绍的直连方式是一样的,但有一点不同,因为多个用户同时收发网络包时信号会在分光器产生碰撞
- OLT 和 ONU 中具备通过调整信号收发时机来避免碰撞的功能
- OLT 会调整信号发送时机并向 ONU 下发指令,ONU 则根据 OLT 的指令来发送数据。反过来,当 BAS 端向用户发送数据时,分光器只需要将信号发给所有用户就可以了,这里并不会发生碰撞,但这样做会导致一个用户收到其他所有用户的信号,造成信息泄露的问题
- 因此需要在每个包前面加上用于识别 ONU 的信息,当 ONU 收到信号后,会接收发给自己的信号并将其转换成以太网信号
- 一种是用一根光纤直接从用户端连接到最近的电话局
接入网中使用 PPP 和隧道
用户认证和配置下发
- 用户发送的网络包会通过 ADSL 和 FTTH 等接入网达到运营商的 BAS
- BAS 由接入网连接的路由器演变而来
- ADSL 和 FFTH 接入网中,都需要用户认证和配置下发
- BAS 使用 PPPoE(Point-to-Point Protocol over Ethernet 以太网的点对点协议) 方式来实现用户验证
- PPPoE 由传统的电话拨号上网使用的 PPP 协议发展而来的
- 关键在 2-3 的下发的 TCP/IP 配置信息
- 在接入互联网的时候,必须为计算机分配一个公有地址,但这个地址并不是事先确定的
- 因为在拨号连接时,可以根据电话号码来改变接入点,而不同的接入点具有不同的 IP 地址,因此无法事先在计算机上设置这个地址
- 所以,在连接时运营商会向计算机下发 TCP/IP 配置信息,其中就包括为计算机分配的公有地址
在以太网上传输 PPP 消息
- ADSL 和 FTTH 接入方式也需要为计算机分配公有地址才能上网,这一点和拨号上网是相同的
- 不过,ADSL 和 FTTH 中,用户和 BAS 之间是通过电缆或光纤固定连接在一起的,因此没有必要验证用户身份,所以实际上并不需要 PPP 的所有这些功能
- 然而,通过用户名和密码登录的步骤可以根据用户名来切换不同的运营商,这很方便
- 因此,接入运营商在ADSL 和 FTTH 中一般也会使用 PPP
- 传输 PPP 消息的思路和将 IP 包装入以太网包中传输的思路是一样的
- PPPoE 是将 PPP 消息装入以太网包进行传输的方式
通过隧道将网络包发送给运营商
- BAS 除了作为用户认证的窗口之外,还可以使用隧道方式来传输网络包
- 隧道就类似于套接字之间建立TCP连接一样
- 无论任何机制,只要能够将包原封不动搬运到另一端,从原理上看就可以用来建立隧道
接入网的整体工作过程
- 接入网的工作从用户端的互联网接入路由器进行连接操作开始
- 首先接入路由器中需要设置运营商分配的用户名和密码
- 然后根据 PPPoE 的发现机制来寻找 BAS
- 这个机制是 ARP 一样是基于广播来实现的
- 这个机制是 ARP 一样是基于广播来实现的
然后进入用户认证和配置下发阶段
- 重点在于如何将用户名和密码发送给 BAS
- 有两种方式,一种是将密码加密的 CHAP(Challenge Handshake Authentication Protocol 挑战握手认证协议)
- 另一种是不加密的 PAP(Password Authentication Protocol 密码验证协议)
- 第二个重点在,校验密码之后 BAS 如何向用户发 TCP/IP配置信息
- 配置信息包括分配给上网设备的IP地址、DNS服务器的IP地址以及默认网关的IP地址
- 配置信息包括分配给上网设备的IP地址、DNS服务器的IP地址以及默认网关的IP地址
- 重点在于如何将用户名和密码发送给 BAS
接下来,接入网路由器判断转发目标后,按照PPPoE规则转发
- 会在包前面加入MAC头部、PPPoE头部和PPP头部信息
- 第一个 MAC 头部中,接收方 MAC 地址填写通过 PPPoE 发现机制查询到的BAS 的 MAC 地址,发送方 MAC 地址填写互联网接入路由器的 BAS 端的端口的 MAC 地址,然后以太类型填写代表 PPPoE 的 8864(十六进制)
- 接下来加入PPPoE头部和PPP头部,除了载荷长度以外都是可以事先确定的
- 载荷长度就是需要传输的包的长度
- 会在包前面加入MAC头部、PPPoE头部和PPP头部信息
- 接下来,网络包会到达 BAS,而 BAS 会将 MAC 头部和 PPPoE 头部去掉,取出 PPP 头部以及后面的部分,然后通过隧道机制将包发送出去
- 最后,PPP 包会沿隧道到达另一端的出口,也就是网络运营商的路由器
不分配 IP 地址的无编号端口
- 一对一连接的端口可以不分配IP地址,这种方式称为无编号
互联网接入路由器将私有地址转换成公有地址
- 互联网接入路由器在转发包时需要进行地址转换
- BAS 向用户下发 TCP/IP 配置信息,如果这些配置在计算机上,相当于计算机有了公有地址,就不需要地址转换
- 然后如果使用路由器上网,BAS下发的参数就会配置在路由器上,公有地址就分配给了路由器,计算机就没有公有地址了
- 这是,计算机会被分配到一个私有地址,计算机发送的包需要通过路由器进行地址转换,然后再转发到互联网中
- 有些应用程序需要将自己的IP告知通信对象,但因为地址转换这些操作无法完成
- 但是目前由很多解决方案解决了这些问题
- 遇到这种情况,我们可以不适用路由器,而是让计算机直接接收来自 BAS 的 PPPoE 消息
除 PPPoE 之外的其他方式
- PPPoA (Point-to-Point Protocol over ATM)
- ADSL 使用PPPoE 方式时,是先将 PPP 消息装入以太网包中,然后再将以太网包拆分并装入信元,而 PPPoA 方式是直接将 PPP 消息装入信元
- 由于只是开头加不加 MAC 头部和 PPPoE 头部的区别,PPP 消息本身是没有区别的,因此密码校验、下发 TCP/IP 配置参数、收发数据包等过程都是和PPPoE 基本相同的
- 由于 PPPoA 没有 MAC 头部,所以 PPP 消息是无法通过以太网来传输的,这就意味着需要和 BAS 收发 PPP 消息的设备,也就是计算机和路由器,必须和 ADSL Modem 是一体的,否则 PPP 机制就无法工作了
- 这个一体化的方式主要有以下两种
- 第一种是将计算机和 ADSL Modem 用 USB 接口连接起来,这样ADSL Modem 就和计算机成为一体了;不过,这种方式最终并没有普及
- 另一种方式是如图所示的,将 ADSL Modem 和路由器整合成一台设备。这种方式和 PPPoE 中使用路由器上网的方式基本没什么区别,因此得到了广泛的普及
- 不过,当由于地址转换产生问题时,这种方式就不容易处理了,因为我们无法抛开路由器用计算机直接上网
- 不过,当由于地址转换产生问题时,这种方式就不容易处理了,因为我们无法抛开路由器用计算机直接上网
- 当然,PPPoA 和 PPPoE 相比也有一些优势
- PPPoE 方式中,需要添加 PPPoE 头部和 PPP 头部,这意味着 MTU 就相应变小了,这可能会降低网络的效率
- 而 PPPoA 不使用以太网包来传输 PPP 消息,因此不会发生 MTU 变小的问题
- PPPoE 会降低网络效率,PPPoA 也有 ADSL Modem 和路由器无法分离的限制,这两个问题其实都是由 PPP 引起的。因此,有一些运营商不使用 PPP,他们使用 DHCP 协议从 BAS 向用户端下发 TCP/IP 配置信息
- DHCP:Dynamic Host Configuration Protocol,动态主机配置协议
- 使用信元的 PPPoE 和 PPPoA 方式中,BAS 需要配备比较昂贵的 ATM 接口,因此不使用信元还可以控制成本
- DHCP 经常用于通过公司网络向客户端计算机下发 TCP/IP 配置信息
- 首先客户端请求配置信息,然后 DHCP服务器下发配置信息,非常简单,不需要像 PPP 那样需要多个步骤,也不需要验证用户名和密码
- 没有用户名和密码就意味着无法通过用户名来切换运营商网络,但这种方式也有优势,它可以单纯地直接传输以太网包,不需要添加额外的 PPP 头部,因此不会占用 MTU
此外,采用 DHCP 的运营商使用的 ADSL Modem 也和 PPPoE、PPPoA方式不同,这种 ADSL Modem 不使用信元,而是直接将以太网包调制成ADSL 信号,因此没有 ADSL Modem 和路由器无法分离的问题
还有一种 DHCP 方式,它不使用 PPP,而是将以太网包直接转,而是将以太网包直接转换成换成 ADSL
- ADSL 信号发送给信号发送给 DSLAM
网络运营商的内部
POP 和 NOC
- POP:Point of Presense 接入点
- 现在网络包已经到达网络运营商的路由器,网络包会从这里进入互联网内部
- 互联网的实体是由多个运营商网络相互连接组成的
- ADSL、FTTH等接入网是与用户签约的运营商设备相连接的,这些设备称为POP,互联网的入口就位于这里
- 根据不同的接入网类型使用不同类型的路由器
- 专线使用具备通信线路端口的一般路由器,因为专线不需要用户认证,配置下发等功能
- 专线是固定连接线路,不需要进行身份认证,参数是根据传真、书面等方式下发后进行手动配置的,因此也不需要 PPP、DHCP 等机制;其实,这就是最古老的互联网接入方式
- 电话/ISDN等拨号方式使用称作RAS的路由器,拨号接入需要对用户拨电话的动作进行应答
- RAS也具备身份认证、配置下发功能
- PPPoE 方式中,ADSL、FTTH 接入服务商会使用 BAS,运营商的路由器则与 BAS 相连
- PPoE 中的身份认证和配置下发操作由接入服务商的 BAS 来负责,运营商的路由器只负责对包进行转发,因此这里也是使用一般的路由器就可以了
- 如果 ADSL 采用 PPPoA 方式接入,那么工作过程会有所不同,DSLAM 通过 ATM 交换机与 ADSL 的运营商的 BAS 相连,然后再连接到运营商的路由器
- 用户端传输的信号先经过 ADSL Modem 拆分成 ATM 信元并进行调制,然后 DSLAM 将信号还原成信元,通过 ATM 交换机转发到 BAS,最后 BAS 将信元还原成网络包,再通过运营商的路由器转发到互联网内部
- 专线使用具备通信线路端口的一般路由器,因为专线不需要用户认证,配置下发等功能
- 接入网部分,传输的网络包相对少,而线路数量很多,需要配置大量端口,因此端口多价格低的路由器适合用在此场景
- 运营商POP部分要处理大量的接入网传过来的网络包,这里需要分配转发性能和数据吞吐量高的路由器
- NOC(Network Operation Center)是运营商的核心设备,从POP传过来的网络包会集中到这里,然后再转发到其他POP或者运营商
室外通信线路的连接
- POP和NOC遍布全国各地,规模有大有小,和平常的机房一样都是位于一幢建筑物中,但运营商的网络中需要传输大量的包,一般更多的是使用光纤
- 拥有光纤的运营商可以选择自己用光纤将NOC和POP直接连接起来
- 维护和修建光纤设施,需要不少费用,没有光纤的运营商就需要租借别人的光纤
- 拥有光纤的公司一般会对外提供光纤租用服务
- 这样没有光纤的运营商页可以通过租用别人的光纤将相距较远的NOC和POP连接起来
跨越网络运营商的网络包
运营商之间的路由器信息交换
- 如果路由表中没有相应的路由信息,路由器就无法对包进行转发,因此仅仅用线路将路由器连起来,还是无法完成包转发的
- 运营商之间可以交换路由信息,让相连接的路由器告知路由信息
获得对方的路由信息之后,我们也需要将自身的路由信息告知对方。这样一来,对方也可以将发往我们所在子网的包转发过来。这个路由信息交换的过程是由路由器自动完成的,这里使用的机制称为 BGP(Border Gateway Protocol 边界网关协议)
根据所告知的路由信息的内容,这种路由交换可分为两类
- 一类是将互联网中的路由全部告知对方。
- 如图,如果运营商 D 将互联网上所有路由都告知运营商 E
- 则运营商 E 不但可以访问运营商 D,还可以访问运营商 D 后面的运营商 B、A 和 C
- 然后,通过运营商 D 就可以向所有的运营商发送包
- 像这样,通过运营商 D 来发送网络包的方式称为 转接
- 另一种类型是两个运营商之间仅将与各自网络相关的路由信息告知对方
- 这样,只有双方之间的网络可以互相收发网络包,这种方式称为非转接,也叫对等
- 一类是将互联网中的路由全部告知对方。
与公司网络中自动更新路由表机制的区别
- 路由器之间相互交换信息自动更新路由表的方式在公司网络中也会用到,不过公司内部和运营商之间在路由交换方式上是有区别的
- 公司中使用的方式是寻找与目的地之间的最短路由,并按照最短路由来转发包,因此,周围的所有路由器都是平等对待的
- 公司内部采用这样的方式没问题,但运营商之间就不行了
- 假设某个运营商拥有一条连接日本和美国的高速线路,那么要访问美国的地址时,可能这条线路是最短路由
- 如果单纯采用最短路由的方式,那么其他运营商的包就都会走这条线路,这时,该运营商需要向其他运营商收取相应的费用,否则就成义务劳动了
- 同时页无法区分哪些运营商有无付费,也无法阻止没付费的运营商使用这条线路
- 因此,互联网中不能单纯的采用最短路由
- 运营商之间的路由交换是在特定的路由器间一对一进行的,这样就可以只将路由信息提供给那些付了费的运营商
- 该机制也可以设置其他判断因素,设置路线优先级
IX 的必要性
IX:Internet eXchange 互联网交换中心
设置中心设备,通过连接中心设备的方式来减少路线数量
运营商如何通过 IX 互相连接
- IX 的部署场所需要能够防御一定的自然灾害、保持继续工作
- IX 所在的场所一般都装有自主发电设备
- 这个要求不局限于IX,运营商NOC等也一样
- 这个要求不局限于IX,运营商NOC等也一样
- IX 的核心是具有大量高速以太网端口的二层交换机
- 原理和一般交换机相同
- IX 的交换机和一般的交换机在工作方式上没有区别,路由器发送网络包时,先通过 ARP 查询下一个路由器的 MAC 地址,然后将其写入 MAC 头部发送出去即可
- 只要填写了正确的 MAC 地址,就可以向任何运营商的路由器发送包
服务器端的局域网运作方式
Web 服务器的部署地点
- a 的方式需要为客户端和服务器各自分配公有地址,然而目前公有地址不够用,这种方式已经不是主流了
- 另外还有安全问题
- b 可以解决一定的安全问题
- c 将服务器放在运营商那边,或者是租用运营商的服务器,利用可运营商的安全等等各自服务,相对更为可靠
防火墙的结构和原理
- 防火墙基本思路是只允许往特定服务器中的特定应用程序的包通过,屏蔽其他的包
- 防火墙可分为包过滤、应用层网关、电路层网关等
设置包过滤的规则
- 网络包的头部包含了用于控制通信操作的控制信息,只要检查这些信息就可以获得很多有用的信息
- 下图允许 Web 服务器从外网访问,禁止 Web 服务器访问互联网
- 设置接受方和发送方的地址信息过滤网络包
- 还可以限定某个应用程序的包通过,设置TCP或UDP头部的端口号
设置TCP的控制位信息,阻断TCP建立连接
而UDP,没有连接操作,无法根据控制位来判断访问方向
- 代表例子就是对DNS服务器的访问
- 这种情况只能二者择其一,要么冒风险允许该应用程序的所有包通过,要么牺牲便利性阻止该应用程序的所有包通过
- 实际上,在防火墙允许包通过之后,就没有什么特别的机制了
- 因此包过滤并不是防火墙专用的一种特殊机制,而是应该看作在路由器的包转发功能基础上附加的一种功能
- 只不过当判断规则比较复杂时,通过路由器的命令难以维护这些规则,而且对阻止的包进行记录对于路由器来说负担也比较大,因此才出现了专用的硬件和软件
- 如果规则不复杂,也不需要记录日志,那么用内置包过滤功能的普通路由器来充当防火墙也是可以的
负载均衡
- 当访问量上升时,可以增加带宽来解决一定的问题
- 带宽高可用使网络速度变快,但是并不能解决所有问题
使用多台服务器来分担负载的方法更加有效,这种架构统称为分布式架构
分担负载的方法有几种,最简单的是用多台服务器,减少每台服务器的访问量
- 要实现这种方法,必须需要一种机制将客户端发送的请求分配到每台服务器上
- 做法也有很多种,最简单是通过DNS服务器来分配
- 访问服务器时,客户端需要先向 DNS 服务器查询服务器的 IP地址,如果在 DNS 服务器中填写多个名称相同的记录,则每次查询时DNS 服务器都会按顺序返回不同的 IP 地址
- 对于域名 www.lab. glasscom.com,如果我们给它分配如下 3 个 IP 地址
- 192.0.2.60
- 192.0.2.70
- 192.0.2.80
- 当第 1 次查询这个域名时,服务器会返回如下内容
- 192.0.2.60 192.0.2.70 192.0.2.80
- 当第 2 次查询这个域名时,服务器会返回如下内容
- 192.0.2.70 192.0.2.80 192.0.2.60
- 当第 3 次查询这个域名时,服务器会返回如下内容
- 192.0.2.80 192.0.2.60 192.0.2.70
- 第4次就回到第一次的结果,这种方式称为 轮询(round-robin)
- 对于域名 www.lab. glasscom.com,如果我们给它分配如下 3 个 IP 地址
- 访问服务器时,客户端需要先向 DNS 服务器查询服务器的 IP地址,如果在 DNS 服务器中填写多个名称相同的记录,则每次查询时DNS 服务器都会按顺序返回不同的 IP 地址
- 这种方式是有缺点的,假如其中一台服务器故障,就无法跳过该服务器,依然指向该服务器;因为DNS服务器并不能确认Web服务器是否正常工作
为了可以跳过故障服务器,可用使用一种叫做负载均衡器的设备
- 使用负载均衡器时,首先要用负载均衡器的 IP 地址代替 Web 服务器的实际地址注册到 DNS 服务器上
- 假设有一个域名 www.lab.glasscom.com,我们将这个域名对应的 IP 地址设置为负载均衡器的 IP 地址并注册到 DNS 服务器上
- 于是,客户端会认为负载均衡器就是一台 Web 服务器,并向其发送请求
- 然后由负载均衡器来判断将请求转发给哪台 Web 服务器
- 使用负载均衡器时,首先要用负载均衡器的 IP 地址代替 Web 服务器的实际地址注册到 DNS 服务器上
负载均衡器可以定期采集 Web 服务器的 CPU、内存使用率,并根据这些数据判断服务器的负载状况,也可以向 Web 服务器发送测试包,根据响应所需的时间来判断负载状况
当操作跨多个页面时,则不考虑 Web 服务器的负载,而是必须将请求发送到同一台 Web 服务器上
- 要实现这一点,关键在于我们必须要判断一个操作是否跨了多个页面
- HTTP 的基本工作方式是在发送请求消息之前先建立 TCP 连接,当服务器发送响应消息后断开连接,下次访问 Web 服务器的时候,再重新建立 TCP 连接
- 因此,在 Web 服务器看来,每一次HTTP 访问都是相互独立的,无法判断是否和之前的请求相关
- 之所以会这样,是因为 Web 中使用的 HTTP 协议原本就是这样设计的
- Web 服务器最早并不是用来运行CGI 程序的,而是主要用来提供静态文件的,而静态文件不需要判断请求之间的相关性,因此最早设计 HTTP 规格的时候,就有意省略了请求之间相关性的判断
- 如果要判断请求之间的相关性,就必须在 Web 服务器一端保存相应的信息,这会增加服务器的负担
- 于是,人们想出了一些方案来判断请求之间的相关性
- 可以在发送表单数据时在里面加上用来表示关联的信息,或者是对 HTTP 规格进行扩展,在 HTTP 头部字段中加上用来判断相关性的信息
- 这种信息就是 Cookie
- 这样,负载均衡器就可以通过这些信息来作出判断,将一系列相关的请求发送到同一台Web 服务器,对于不相关的请求则发送到负载较低的服务器了
使用缓存服务器分担负载
缓存服务器基本思路
除了使用多台功能相同的 Web 服务器分担负载之外,还有另外一种方法,就是将整个系统按功能分成不同的服务器
- 如 Web 服务器、数据库服务器
- 缓存服务器就是一种按功能来分担负载的方法
缓存服务器是一台通过代理机制对数据进行缓存的服务器
- 代理介于Web 服务器和客户端之间,具有对 Web 服务器访问进行中转的功能
- 当进行中转时,它可以将 Web 服务器返回的数据保存在磁盘中,并可以代替Web 服务器将磁盘中的数据返回给客户端
- 这种保存的数据称为缓存,缓存服务器指的也就是这样的功能
- Web 服务器需要执行检查网址和访问权限,以及在页面上填充数据等内部操作过程,因此将页面数据返回客户端所需的时间较长
- 相对地,缓存服务器只要将保存在磁盘上的数据读取出来发送给客户端就可以了,因此可以比 Web 服务器更快地返回数据
- 其实就是缓存服务器把 Web 服务器的部分工作给分离出来了,减轻了 Web 服务器的工作量
- 不过,如果在缓存了数据之后,Web 服务器更新了数据,那么缓存的数据就不能用了,因此缓存并不是永久可用的
- 此外,CGI 程序等产生的页面数据每次都不同,这些数据也无法缓存
- 无论如何,在来自客户端的访问中,总有一部分访问可以无需经过 Web 服务器,而由缓存服务器直接处理。
- 即便只有这一部分操作通过缓存服务器提高了速度,整体性能也可以得到改善
- 此外,通过让缓存服务器处理一部分请求,也可以减轻 Web服务器的负担,从而缩短 Web 服务器的处理时间
缓存服务器通过更新实际管理内容
- 如果一台缓存服务器对应多台 Web 服务器,就需要根据请求信息的URI中的目录进行判断
- 然后缓存服务器充当客户端的身份去向目标Web服务器发送请求信息
- 它会先创建套接字,然后连接到Web服务器的套接字,并发送请求
- 接着缓存服务器会在响应消息中加上 Via 头部字段,表示这个消息是经过缓存服务器中转的
- 最后缓存服务器充当了Web服务器的身份向客户端发送响应消息
- 同时,缓存服务器会将响应消息保存在缓存中,并记录保存时间
- 在这个过程中充当了中间人的方式就是代理的基本原则
- 在中转消息的过程中,缓存服务器还会顺便将页面数据保存下来,随着缓存数据的积累,用户访问的数据命中缓存的几率也会提高
- 如果缓存服务器中已有缓存信息的话
- 缓存服务器会添加一个 If-Modified-Since 头部字段并将请求转发给 Web 服务器
- 询问 Web 服务器用户请求的数据是否已经发生变化
- 这时, Web 服务器只要查询一下数据的最后更新时间就好了,比返回页面数据的负担要小一些;而且返回的响应消息也比较短,能相应地减少负担
- 然后,Web 服务器会根据 If-Modified-Since 的值与服务器上的页面数据的最后更新时间进行比较
- 如果在指定时间内数据没有变化,就会返回一个像上图一样的表示没有变化的响应消息
- 接下来,返回消息到达缓存服务器,然后缓存服务器就会知道 Web 服务器上的数据和本地缓存中的数据是一样的,于是就会将缓存的数据返回给客户端
正向代理
- 上面将的是在Web服务器一端部署一个代理,然后利用其缓存功能来改善服务器性能
- 还有另一种方法是在客户端一侧部署缓存服务器
- 实际上,缓存服务器使用的代理机制最早就是放在客户端一侧的,这才是代理的原型,称为正向代理(forward proxy)
- 正向代理刚刚出现的时候,其目的之一就是缓存,这个目的和服务器端的缓存服务器相同
- 不过,当时的正向代理还有另外一个目的,那就是用来实现防火墙
- 代理在转发过程可以查看请求的内容,可以根据内容判断是否允许访问
- 包过滤方式的防火墙只能根据IP地址和端口号判断,因此无法实现这一目标
在使用正向代理时,一般需要在浏览器的设置窗口中的“代理服务器”一栏中填写正向代理的 IP 地址,浏览器发送请求消息的过程也会发生相应的变化
- 在没有设置正向代理的情况下,浏览器会根据网址栏中输入的http://… 字符串判断 Web 服务器的域名,并向其发送请求消息
- 当设置了正向代理时,浏览器会忽略网址栏的内容,直接将所有请求发送给正向代理
- 请求消息的内容也会有一些不同
- 没有正向代理时,浏览器会从网址中提取出 Web 服务器域名后面的文件名或目录名,然后将其作为请求的URI 进行发送
- 而有正向代理时,浏览器会像图 5.9 这样,在请求的 URI字段中填写完整的 http://… 网址
使用正向代理时,URI 部分为http://… 这样的完整网址,因此可以根据这个网址来转发,不需要像服务器端的缓存服务器一样实现设置好转发目标 Web 服务器,而且可以发给任意Web 服务器;而服务器端的缓存服务器只能向事先设置好的目标进行转发。
反向代理
- 通过将请求消息中的 URI 中的目录名与 Web 服务器进行关联,使得代理能够转发一般的不包含完整网址的请求消息
- 前面所说的服务器端的缓存服务器采用的就是这种方式,这种方式称为反向代理
透明代理
- 前面说过缓存服务器对应多台Web服务器时,一种方式是根据请求信息的URI中的目录进行判断转发目标
- 另一种方式是查看请求信息的包头部,根据接受方的IP地址去访问目标,这种方式称为透明代理
- 这种方法也可以转发一般的请求消息,因此不需要像正向代理一样设置浏览器参数,也不需要在缓存服务器上设置转发目标,可以将请求转发给任意 Web 服务器
透明代理集合了正向代理和反向代理的优点,是一个非常方便的方式,但也需要注意一点,那就是如何才能让请求消息到达透明代理
- 由于透明代理不需要设置在浏览器中,那么浏览器还是照常向 Web 服务器发送请求消息
反向代理采用的是通过 DNS 服务器解析引导的方法,但透明代理是不能采用这种方法的,否则透明代理本身就变成了访问目标,也就无法通过接收方 IP 地址判断转发目标了,这就失去了透明代理的意义
于是,我们必须将透明代理放在请求消息从浏览器传输到 Web 服务器的路径中,当消息经过时进行拦截
- 如果请求消息有多条路径可以到达 Web 服务器,那么就必须在这些路径上都放置透明代理,因此一般是将网络设计成只有一条路可以走的结构,然后在这一条路径上放置透明代理
- 连接互联网的接入网就是这样一个关口,因此可以在接入网的入口处放置反向代理
- 使用透明代理时,用户不会察觉到代理的存在,也不会注意到 HTTP 消息是如何被转发的,因此大家更倾向于将透明代理说成是缓存
- 最近有很多场合中已经将透明代理直接叫作“缓存”而不是“代理”了,不过无论叫什么名字,其内部结构是相同的
内容分发服务
利用内容分发服务分担负载
- 将缓存服务器部署在距离用户很近的地方,同时Web 服务器运营者还可以控制这些服务器,但这种方式也有问题
- 对于在互联网上公开的服务器来说,任何地方的人都可以来访问它,因此如果真的要实现这个方式,必须在所有的运营商 POP 中都部署缓存服务器才行,这个数量太大了,非常不现实
- 要解决这个问题也有一些办法
- 首先,我们可以筛选出一些主要的运营商,这样可以减少缓存服务器的数量
- 尽管这样做可能会导致有些用户访问到缓存服务器还是要经过很长的距离,但总比直接访问 Web 服务器的路径要短多了,因此还是可以产生一定的效果
- 要解决这个问题也有一些办法
- 即便减少了数量,作为一个 Web 服务器运营者,如果自己和这些运营商签约并部署缓存服务器,无论是费用还是精力都是吃不消的
为了解决这个问题,一些专门从事相关服务的厂商出现了,他们来部署缓存服务器,并租借给 Web 服务器运营者
- 这种服务称为内容分发服务 CDS (Content Delivery Service)
- 现在更常用的名字叫 CDN (Content Delivery Network / Content Distribution Network)
提供 CDS 服务的厂商称为 CDSP(Content Delivery Service Provider)
- 他们会与主要的供应商签约,并部署很多台缓存服务器
- 另一方面,CDSP 会与 Web 服务器运营者签约,使得 CDSP 的缓存服务器配合 Web 服务器工作
- 只要 Web 服务器与缓存服务器建立关联,那么当客户端访问 Web 服务器时,实际上就是在访问 CDSP 的缓存服务器了
- 缓存服务器可以缓存多个网站的数据,因此 CDSP 的缓存服务器就可以提供给多个 Web 服务器的运营者共享。
- 这样一来,每个网站运营者的平均成本就降低了,从而减少了网站运营者的负担
- 而且,和运营商之间的签约工作也由 CDSP 统一负责,网站运营者也节省了精力
- 总之,依然是采用了解耦的思想,把部分工作给分担出去了
寻找最近的缓存服务器
- 如何从众多的缓存服务器中找到离客户端最近的一个,并让客户端去访问那台服务器
第一个方法是像负载均衡一样用 DNS 服务器来分配访问
- 首先需要事先从缓存服务器部署地点的路由器收集路由信息
- 接下来,DNS 服务器根据路由表查询从本机到 DNS 查询消息的发送方,也就是客户端 DNS 服务器的路由信息
- 例如,根据上图路由器 A的路由表,可以查出路由器 A 到客户端 DNS 服务器的路由。
- 通过互联网内部的路由表中的路由信息可以知道先通过运营商 X,然后通过运营商 Y,最后到达运营商 Z 这样的信息,通过这样的信息可以大致估算出距离
- 依次查询所有路由器的路由表之后,我们就可以通过比较找出哪一台路由器距离客户端 DNS 服务器最近
- 提供路由表的路由器位于缓存服务器的位置,而客户端 DNS 服务器也应该和客户端在同一位置,这样就等于估算出了缓存服务器与客户端之间的距离,从而能够判断出哪台缓存服务器距离客户端最近了
- 实际上,客户端 DNS 服务器不一定和客户端在同一位置,因此可能无法得出准确的距离,但依然可以达到相当的精度
重定向服务器分配访问目标
- 另一个让客户端访问最近的缓存服务器的方法
- 使用重定向告知客户端最近的缓存服务器时,首先需要将重定向服务器注册到 Web 服务器端的 DNS 服务器上
- 这样一来,客户端会将 HTTP请求消息发送到重定向服务器上
- 重定向服务器和刚才一种方法中的 DNS服务器一样,收集了来自各个路由器的路由信息,并根据这些信息找到最近的缓存服务器,然后将缓存服务器的地址放到 Location 字段中返回响应
- 这种方法的缺点在于增加了 HTTP 消息的交互次数,相应的开销也比较大,但它也有优点
- 对 DNS 服务器进行扩展的方法是估算客户端 DNS 服务器到缓存服务器之间的距离,因此精度较差;相对而言,重定向的方法是根据客户端发送来的 HTTP 消息的发送方 IP 地址来估算距离的,因此精度较高
缓存的更新方法会影响性能
- 还有一个因素会影响缓存服务器的效率,那就是缓存内容的更新方法
- 缓存本来的思路是将曾经访问过的数据保存下来,然后当再次访问时拿出来用,以提高访问操作的效率
- 不过,这种方法对于第一次访问是无效的,而且后面的每次访问都需要向原始服务器查询数据有没有发生变化,如果遇到网络拥塞,就会使响应时间恶化
- 要改善这一点,有一种方法是让 Web 服务器在原始数据发生更新时,立即通知缓存服务器,使得缓存服务器上的数据一直保持最新状态,这样就不需要每次确认原始数据是否有变化了,而且从第一次访问就可以发挥缓存的效果
- 内容分发服务采用的缓存服务器就具备这样的功能
- 此外,除了事先编写好内容的静态页面之外,还有一些在收到请求后由 CGI 程序生成的动态页面,这种动态页面是不能保存在缓存服务器上的
- 这种情况下,我们可以不保存整个页面,而是将应用程序生成的部分,也就是每次内容都会发生变化的动态部分,与内容不会发生变化的静态部分分开,只将静态部分保存在缓存中
请求到达 Web 服务器,响应返回浏览器—短短几秒的“漫长旅途”迎来终点
服务器概览
- 服务器的程序可以同时和多台客户端计算机进行通信
- 服务器需要同时和多个客户端通信,但一个程序来处理多个客户端的请求是很难的,因为服务器必须把握每一个客户端的操作状态
- 一般的做法是,每有一个客户端连接进来,就启动一个新的服务器程序,确保服务器程序和客户端是一对一的状态
- 这样,在工作时就不必考虑其他客户端的连接情况,只要关心自己对应的客户端就可以了
- 通过这样的方式,可以降低程序编写的难度。服务器操作系统具有多任务、多线程功能,可以同时运行多个程序,服务器程序的设计正是利用了这一功能
- 多任务和多线程的区别在于任务和线程的区别;在操作系统内部,任务是作为单独的程序来对待的,而线程则是一个程序中的一部分
- 客户端的数据收发需要经过下面 4 个阶段
- (1)创建套接字(创建套接字阶段)
- (2)用管道连接服务器端的套接字(连接阶段)
- (3)收发数据(收发阶段)
- (4)断开管道并删除套接字(断开阶段)
相对地,服务器是将阶段(2)改成了等待连接,具体如下
- (1)创建套接字(创建套接字阶段)
- (2-1)将套接字设置为等待连接状态(等待连接阶段)
- (2-2)接受连接(接受连接阶段)
- (3)收发数据(收发阶段)
- (4)断开管道并删除套接字(断开阶段)
由于等待连接的模块在服务器程序启动时就已经在运行了,所以在刚启动时,应该还没有客户端的连接包到达
在复制出一个新的套接字之后,原来那个处于等待连接状态的套接字会怎么样呢
- 它还会以等待连接的状态继续存在,当再次调用 accept,客户端连接包到达时,它又可以再次执行接受连接操作
- 接受新的连接之后,和刚才一样,协议栈会为这个等待连接的套接字复制一个新的副本,然后让客户端连接到这个新的副本套接字上
- 像这样每次为新的连接创建新的套接字就是这一步操作的一个关键点
- 如果不创建新副本,而是直接让客户端连接到等待连接的套接字上,那么就没有套接字在等待连接了,这时如果有其他客户端发起连接就会遇到问题
- 为了避免出现这样的情况,协议栈采用了这种创建套接字的新副本,并让客户端连接到这个新副本上的方法
创建新套接字时端口号也是一个关键点
- 端口号是用来识别套接字的,因此我们以前说不同的套接字应该对应不同的端口号,但如果这样做,这里就会出现问题
- 因为在接受连接的时候,新创建的套接字副本就必须和原来的等待连接的套接字具有不同的端口号才行
- 这样一来,比如客户端本来想要连接 80 端口上的套接字,结果从另一个端口号返回了包,这样一来客户端就无法判断这个包到底是要连接的那个对象返回的,还是其他程序返回的
- 因此,新创建的套接字副本必须和原来的等待连接的套接字具有相同的端口号
- 但是这样一来又会引发另一个问题。
- 端口号是用来识别套接字的,如果一个端口号对应多个套接字,就无法通过端口号来定位到某一个套接字了
- 当客户端的包到达时,如果协议栈只看 TCP 头部中的接收方端口号,是无法判断这个包到底应该交给哪个套接字的
这个问题可以用下面的方法来解决,即要确定某个套接字时,不仅使用服务器端套接字对应的端口号,还同时使用客户端的端口号再加上 IP 地址,总共使用下面 4 种信息来进行判断
既然通过客户端 IP 地址、客户端端口号、服务器 IP 地址、服务器端口号这 4 种信息可以确定某个套接字,那么要指代某个套接字时用这 4 种信息就好了,为什么还要使用描述符呢
服务器的接收操作
网卡信号转换
- 客户端发送的网络包已经到达服务器
网卡接收到光信号或电信号,将其还原成数字信号
- 局域网中传输的网络包信号是由 1 和 0 组成的数字信息与用来同步的时钟信号叠加而成的,因此只要从中分离出时钟信号,然后根据时钟信号进行同步,就可以读取并还原出 1 和 0 的数字信息了
接下来根据包末尾的帧校验序列 FCS 来校验错误
- 根据 CRC-32 来计算接收到的数字信号和包末尾的 FCS 值比较
- FCS 值是在发送时根据转换成电信号之前的数字信息进行计算得到的,因此如果根据信号还原出的数字信息与发送前的信息一致,则计算出的FCS 也应该与包末尾的 FCS 一致
- 如果两者不一致,则可能是因为噪声等影响导致信号失真,数据产生了错误,这时接收的包是无效的,因此需要丢弃
当 FCS 一致,接下来就检查 MAC 头部中的 MAC 地址的接收方是不是自己
- 被还原后的数字信号被保存在网卡内部的缓冲区
在这个过程中,服务器的 CPU 并不是一直在监控网络包的到达,而是在执行其他的任务,因此 CPU 并不知道此时网络包已经到达了
- 但接下来的接收操作需要 CPU 来参与,因此网卡需要通过中断将网络包到达的事件通知给 CPU
- 接下来,CPU 就会暂停当前的工作,并切换到网卡的任务
- 然后,网卡驱动会开始运行,从网卡缓冲区中将接收到的包读取出来,根据 MAC 头部的以太类型字段判断协议的种类,并调用负责处理该协议的软件
IP 模块的接收操作
- 当网络包转交到协议栈时,IP 模块会首先开始工作,检查 IP 头部
IP 模块首先会检查 IP 头部的格式是否符合规范,然后检查接收方 IP 地址,看包是不是发给自己的
- 当服务器启用类似路由器的包转发功能时,对于不是发给自己的包,会像路由器一样根据路由表对包进行转发
确认包是发给自己的之后,接下来需要检查包有没有被分片,检查 IP头部的内容就可以知道是否分片
- 如果是分片的包,则将包暂时存放在内存中,等所有分片全部到达之后将分片组装起来还原成原始包
- 如果没有分片,则直接保留接收时的样子,不需要进行重组
接下来需要检查 IP 头部的协议号字段,并将包转交给相应的模块
- 如果协议号为 06(十六进制),则将包转交给 TCP 模块
- 如果是 11(十六进制),则转交给 UDP 模块
TCP 模块处理连接网络包
- 当 TCP 头部中的控制位 SYN 为 1 时,表示这是一个发起连接的包
- 这时,TCP 模块会执行接受连接的操作
- 不过在此之前,需要先检查包的接收方端口号
- 并确认在该端口上有没有与接收方端口号相同且正在处于等待连接状态的套接字
- 如果指定端口号没有等待连接的套接字,则向客户端返回错误通知的包
- 如果存在等待连接的套接字,则为这个套接字复制一个新的副本并将发送方 IP 地址、端口号、序号初始值、窗口大小等必要的参数写入这个套接字中,同时分配用于发送缓冲区和接收缓冲区的内存空间
然后生成代表接收确认的 ACK 号,用于从服务器向客户端发送数据的序号初始值,表示接收缓冲区剩余容量的窗口大小,并用这些信息生成 TCP 头部,委托IP 模块发送给客户端
这个包到达客户端之后,客户端会返回表示接收确认的 ACK 号,当这个 ACK 号返回服务器后,连接操作就完成了
- 这时,服务器端的程序应该进入调用 accept 的暂停状态,当将新套接字的描述符转交给服务器程序之后,服务器程序就会恢复运行
TCP 模块处理数据网络包
- 在 TCP 处理连接包之后,进入到数据收发阶段
- 首先,TCP 模块会检查收到的包对应哪一个套接字
- 在服务器端,可能有多个已连接的套接字对应同一个端口号,因此仅根据接收方端口号无法找到特定的套接字
- 这时我们需要根据 IP 头部中的发送方 IP 地址和接收方 IP 地址,以及 TCP 头部中的接收方端口号和发送方端口号共 4 种信息,找到上述 4 种信息全部匹配的套接字
- 找到对应的套接字后,TCP 模块会对比该套接字中保存的数据收发状态和收到的包的 TCP 头部中的信息是否匹配,以确定数据收发操作是否正常
- 最后,TCP模块会从包中提出数据,并存放到接收缓冲区中,与上次收到的数据块连接起来。这样一来,数据就被还原成分包之前的状态了
- 当收到的数据进入接收缓冲区后,TCP 模块就会生成确认应答的 TCP 头部,并根据接收包的序号和数据长度计算出 ACK 号,然后委托 IP 模块发送给客户端
TCP 模块的断开操作
- 在 TCP 协议的规则中,断开操作可以由客户端或服务器任何一方发起,具体的顺序是由应用层协议决定的
- Web 中,这一顺序随 HTTP 协议版本不同而不同,在 HTTP1.0 中,是服务器先发起断开操作
- 这时,服务器程序会调用 Socket 库的 close,TCP 模块会生成一个控制位 FIN 为 1 的 TCP 头部,并委托 IP 模块发送给客户端
- 当客户端收到这个包之后,会返回一个 ACK 号
- 接下来客户端调用 close,生成一个FIN 为 1 的 TCP 头部发给服务器
- 服务器再返回 ACK 号,这时断开操作就完成
Web 服务器程序解释请求消息并作出响应
URI 转换和运行程序
- 服务器接收到数据后,根据 URI 去读取内容并向客户端返回数据
- 不过 Web 服务器的目录并不是磁盘的实际目录,而是用的虚拟目录
- 如果是实际目录的话,就会有安全隐患
- 而 URI 中写的就是在这个虚拟目录结构下的路径名
- 因此,当读取文件时,需要先查询虚拟目录与实际目录的对应关系,并将 URI 转换成实际的文件名后,才能读取文件并返回数据
- 运行 CGI 程序
Web 服务器的访问控制
Web 服务器的基本工作方式就是根据请求消息的内容判断数据源,并从中获取数据返回给客户端,不过在执行这些操作之前,Web 服务器还可以检查事先设置的一些规则,并根据规则允许或禁止访问
Web 服务器的访问控制规则主要有以下 3 种
- (1) 客户端 IP 地址
- (2) 客户端域名
- (3) 用户名和密码
- 根据客户端 IP 地址设置的规则,这个情况很简单,在调用accept 接受连接时,就已经知道客户端的 IP 地址了,只要检查其是否允许访问就可以了
- 当根据客户端域名设置规则时,需要先根据客户端 IP 地址查询客户端域名,这需要使用 DNS 服务器
- 一般我们使用 DNS 服务器都是根据域名查询 IP 地址,其实根据 IP 地址反查域名也可以使用 DNS 服务器
- 具体来说,这个过程是这样的
- 收到客户端的请求消息后,Web 服务器会委托协议栈告知包的发送方 IP 地址,然后用这个 IP 地址生成查询消息并发送给最近的 DNS 服务器
- 接下来,DNS 服务器找出负责管辖该 IP 地址的 DNS 服务器,并将查询转发给它查询到相应的域名之后返回结果
- 然后 Web 服务器端的 DNS 服务器再将结果转发给 Web 服务器
- 这样一来,我们就可以根据发送方IP 地址查询到域名
- 接下来,为了保险起见,还需要用这个域名查询一下IP 地址,看看结果与发送方 IP 地址是否一致
- 这是因为有一种在 DNS 服务器上注册假域名的攻击方式,因此我们需要进行双重检查
- 根据用户名和密码设置规则
浏览器接收响应消息并显示内容
- Web 服务器发送的响应消息会被分成多个包发送给客户端,然后客户端需要接收数据
首先,网卡将信号还原成数字信息,协议栈将拆分的网络包组装起来并取出响应消息,然后将消息转交给浏览器
- 这个过程和服务器的接收操作相同
要显示内容,首先需要判断响应消息中的数据属于哪种类型
- 当数据类型为文本时,还需要判断编码方式,这时需要用 charset 附加表示文本编码方式的信息
- 除了通过 Content-Type 判断数据类型,还需要检查 Content-Encoding 头部字段
- 如果消息中存放的内容是通过压缩或编码技术对原始数据进行转换得到的,那么 Content-Encoding 的值就表示具体的转换方式,通过这个字段的值,我们可以知道如何将消息中经过转换的数据还原成原始数据