从浏览器地址栏输入后敲下回车,直到浏览器呈现信息,这个过程到底发生了什么?
这一过程涉及宽带接入、浏览器、前端技术,DNS,TCP/IP,操作系统,网卡,驱动程序,传输设备,交换机、路由器,服务器等等网络、通信、web相关的几乎所有技术
本文只是宏观上简单的了解,加深自己对网络连接的理解,不是大而全的技术论文
只是一个过程了解,阅读需要对网络、web有所了解
在前文中,我有上面两幅图,网络的通信类似快递的运输
计算机网络传输的核心是TCP/IP协议,分为四层结构
应用层产生需要传输的真正数据。
传输层、网际层、网络接口层由操作系统以及网卡驱动程序和物理网卡实现,负责将数据从计算机中发送出去,经过路由器(网际层)等网络设置到达最终的目的地。
本文以发送HTTP请求以及返回响应的过程简单介绍网络的通信
浏览器发起请求
解析URL
打开浏览器之后,可以在地址栏输入网址(或者点击某个超链接)本质是一样的,网址就是URL
URL是Uniform Resource Locator的缩写,译为“统一资源定位符”,通常由三部分组成,协议,ip地址(或者域名)以及资源的具体目录(第三个也可以省略)
比如: https://www.crazybytex.com/thread-33-1-1.html
https是协议,www.crazybytex.com
是域名,thread-33-1-1.html
是资源具体路径
输入网址,按下回车后,浏览器的目的就是请求这一“URL”的资源,并将解析呈现出来。
浏览器首先要解析URL,进一步确定通信协议
应用层不止一种协议,有http、ftp、file、https 等,每种协议自然有不同的约定方式
所以需要先确定协议,协议是什么?协议就是相互之间的一个约定,比如我给你一块钱,你给我一瓶矿泉水,这就是约定,约定好了大家就按照协议进行执行。
解析URL之后,浏览器解析出URL给出的信息
HTTP请求
我们输入的网址是https协议,简单起见以HTTP来了解
HTTP 协议定义了客户端和服务器之间交互的消息内容和步骤,大家按照固定的步骤和格式进行通信
根据URL可以定位请求资源的位置,但是对于这个资源可能还有多种处理方法,比如是请求资源还是要删除指定资源?
所以HTTP请求也规定了方法,用于指定请求的类型
HTTP主要请求方法为 GET 和 POST
如上图所示,HTTP主要有两类报文,一个是从客户端向服务器发送请求,一个是服务器到客户端的应答
下面就是请求和响应的格式
请求和响应都由三部分组成,首行、头部以及实体
- 请求报文中分别叫做,请求行,请求头,请求体
- 响应报文分别叫做:状态行(响应行),响应头,响应体
每一部分又都由多个字段约定了更加详细具体的通信规则
在浏览器中可以查看到这些信息,比如chrome中查看请求和响应信息
在查看工具中看的信息,为了更加直观,工具对原始数据进行了一定处理,所以不是上面的HTTP格式,不要奇怪
总之
浏览器将需要请求的信息,按照HTTP协议约定的格式,封装成为HTTP请求报文
DNS解析
HTTP请求是应用层协议,本身不具备将消息发送到网络的功能
想要进一步进行通信,还需要调用操作系统提供的socket接口,与服务器建立连接进行通信
socket是操作系统对TCP/IP的封装,提供应用编程接口,想要依赖socket进行通讯,需要ip地址和端口号
此时,我们仅仅知道服务器的名称,也就是域名www.crazybytex.com
需要获取到服务器的真实IP地址,才能够真正的进行通信
解析的过程:
- 操作系统会先检查自己本地的hosts文件,如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存
- 如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/IP参数中设置的首选DNS服务器,叫做本地DNS服务器
- 本地DNS解析器是操作系统中的DNS客户端程序,负责DNS的解析管理
所以,以上的步骤就是
- hosts文件有没有?
- 操作系统的客户端--本地DNS解析器缓存 有没有?
- 还没有?去配置的DNS服务器中进行查找!
如果要查询的域名,不由本地DNS服务器区域解析
但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性
如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发)进行查询
如果未用转发模式,那么迭代查询
本地DNS就把请求发至 “根DNS服务器”,“根DNS服务器”收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。
本地DNS服务器收到IP信息后,将会联系负责.com域的这台服务器。
这台负责.com域的服务器收到请求后,如果自己无法解析,它就会找一个管理.com域的下一级DNS服务器地址(crazybytex.com)给本地DNS服务器。
当本地DNS服务器收到这个地址后,就会找crazybytex.com域服务器,重复上面的动作,进行查询,直至找到www.crazybytex.com主机。
如果用的是转发模式,那么递归查询
此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析
上一级服务器如果不能解析,或找根DNS或把转请求转至上上级,以此循环。
不管本地DNS服务器是否使用转发,最后都是把结果返回给本地DNS服务器,然后由此DNS服务器再返回给客户机。
本文作者:程序员潇然 疯狂的字节X https://crazybytex.com/
本地DNS服务器解析的过程,就是前面提到的DNS的查询
主机到本地域名服务器的查询是递归查询
本地域名服务器的查询过程,可以是递归查询,也可以是迭代查询
DNS解析之后就获得了请求域名的IP地址,对于HTTP请求,如果没有设置那么默认是80端口
如果设置的话,那么就是使用指定的端口
端口号会设置在URL中,浏览器客户端解析URL就可以获得,不需要专门的解析
所以此时,拥有了Ip地址和端口号
socket连接
socket是操作系统提供的TCP/IP的薄层封装,大大简化了TCP/IP的使用
借助于socket通信的过程大致分为下面四个过程
- 创建套接字
- 连接阶段
- 通信阶段
- 断开阶段
web服务器会先创建套接字,然后进行监听,等待客户端进行连接
下图是一个简化版,简化版,简化版,socket连接请求的过程
socket是对TCP/IP协议的封装,所以本质还是TCP/IP,接下来的TCP/IP中会继续介绍
TCP处理
tcp报文格式、字段
如果应用层的数据过大,会将数据进行分块,每个块都会添加TCP头部信息
如下图所示
TCP协议与HTTP协议一样,协议就是一种约定好的格式
TCP的头部的所有字段信息,体现了TCP协议所有的功能,TCP协议模块的实现就是对这些字段的解析使用处理
源端口和目的端口
各占2个字节,分别写入源端口号和目的端口号。
序号
序号占4字节
序号范围是[0, 232 - 1 ],共232(即4 294 967 296)个序号。序号增加到232-1后,下一个序号就又回到0。
也就是说,序号使用mod 232运算。
TCP是面向字节流的。在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。
确认号
确认号 占4字节
是期望收到对方下一个报文段的第一个数据字节的序号
若确认号=N,则表明:到序号N-1为止的所有数据都已正确收到
确认ACK
仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效
TCP规定,在连接建立后所有传送的报文段都必须把ACK置1
同步SYN
在连接建立时用来同步序号
当SYN=1而ACK=0时,表明这是一个连接请求报文段。
对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK = 1
因此,SYN置为1就表示这是一个连接请求或连接接受报文。
终止FIN
用来释放一个连接
当FIN = I时,表明此报文段的发送方的数据己发送完毕,并要求释放运输连接
TCP三次握手
TCP运输连接有三个阶段:
连接建立、数据传送、连接释放
TCP连接过程通常叫做握手,握手需要客户端和服务器端交换三个报文,如下图所示
之所以需要三次握手是因为TCP是可靠传输,三次能够刚好可靠又不多余
TCP三次握手与Socket的连接过程是相关联对应的,Socket就是对于TCP/IP的封装么
客户端有CLOSED、SYN-SEND、ESTABLISHED三种状态
客户端有CLOSED、LISTEN、SYN-RCVD、ESTABLISHED四种状态
服务器会首先创建连接,并且进入监听等待阶段,等待客户端的请求
当需要发送请求时,浏览器客户端主动打开连接,然后服务器被动打开连接
连接过程
客户端在需要时,向服务器发起请求连接报文,发出后状态从CLOSED转换为SYN-SEND 同步-已发送状态
服务器一直处于LISTEN状态,接收到请求后,对客户端的请求进行回应,转换为SYN-RCVD,同步-已收到状态
客户端收到服务器的回应后,状态转换为ESTABLISHED,并且再次向服务器发送确认
服务器收到客户端的确认之后,服务器也转换为ESTABLISHED状态,完成了连接
发出消息或者收到消息后状态才会进行切换
客户端与服务器的握手是一个往复确认的过程
- 客户端:发出确认请求,SYN=1,seq=x,你听得到么,我想建立连接(SYN=1),我的序号是x(seq=x)
- 服务器:对请求进行确认,也就是回应,我听到了(ACK=1,ack=x+1),你听得到么(SYN=1),我的序号是y(seq=y)
- 客户端:对服务器的回应进行确认,我听到了(ACK=1,ack=y+1),我的序号是x+1
IP数据报经过运输层需要分段发送,所以在TCP的处理过程中,有序号的概念
比如客户端说我要从666号开始,发送100个数据,服务器说,我是从888号开始回应的
上面的seq=x 和 seq=y seq=x+1(上一个seq=x,下一个自然就是seq=x+1了)都是各自的序号
握手的过程就是SYN seq ACK ack的来回确认
SYN ACK是头部的字段,可以理解为标志位,协议中有对他们的值有具体的规定
ack就是确认号,确认号是期望收到的对方的下一个报文段的第一个数据字节的序号,也就是收到的序号+1
否则随便一个,怎么对得上号
为什么要三次握手?
如果不是三次握手,只有两次
如果客户端发出请求连接时,报文延时了,于是客户端重新发送了一次连接请求消息
后来收到了确认,建立了连接,然后完成了数据传输,关闭了连接
此时,服务器收到了那个迟到的请求消息,此时他应该是个废物了
但是如果只有两次握手,服务器收到请求就响应建立了连接了
但是如果是三次,客户端不会再次确认,服务器也就随后知道了这消息有问题,不会建立连接
TCP四次挥手
连接建立以后就可以进行数据通信传输了
通信结束后,需要断开连接,断开连接需要四次交互,常被称为四次挥手
最初状态均为ESTABLISHED,客户端与服务器相互进行数据传送
下图假设客户端无数据发送,请求断开连接
断开过程
客户端无数据发送时,请求关闭连接,我好了,我想断开连接了(FIN=1)我的序号是u(u就是之前传送过的所有数据的最后一个字节的序号+1)
此时客户端转变为FIN-WAIT-1状态
服务器收到客户端的消息后,告诉客户端“好的,我知道了”(ACK=1,ack=u+1),这条消息的序号是v(seq=v ,这是服务器发送消息的序号)
此时服务器的状态就转换为了CLOSE-WAIT状态
此时,客户端通往服务器的路就断开了,客户端不能向服务器发送数据
但是服务器仍旧可以向客户端发送数据,现在是“半关闭”的状态
当客户端收到来自服务器的确认之后,进入FIN-WAIT-2状态,等待服务器那边说断开连接,等待中。。。。。
当服务器所有的数据也都完全发送完成了之后,服务器才开始主动告知客户端断开连接(FIN=1,seq=w)
这中间服务器可能又继续发送了一些数据,可能是v+1 也可能发送了更多,所以设置为w
并且再次发送确认信息(ACK=1,ack=u+1,因为客户端已经不能发送数据了,服务器期望收到的序号永远都是最后一个序号+1,也就是u+1)
这时,服务器就进入了LAST-ACK状态,最后确认状态
客户端收到了服务器的断开连接请求后,也需要给出确认响应(ACK=1,ack=w+1,seq=u+1),然后进入TIME-WAIT状态
等待两个MSL后,进入关闭状态
MSL 是Maximum Segment Lifetime英文的缩写“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
服务器最终收到来自客户端的确认信息后,关闭,进入CLOSED状态
四次挥手也是一个互相确认的过程,你说不玩了,别人答应了,还要等别人都搞好了再告诉你可以走了,你才能走
客户端:我不想玩了
服务器:好的我知道了
服务器:你可以走了
客户端:好的我走了
就如同在网吧上网,你点击下机之后,再去网管那边结账
结账清楚了之后才彻底结束,而不是你说走就走了,难道你办会员卡了么
这个过程很好理解,客户端发出请求后,并不意味着服务器都已经完成响应
所以当客户端请求断开时,并不能立即断开,还需要等待服务器那边处理妥当,再来通知你的确是可以断开了
消息发出来谁知道别人收没收到,所以还需要一个确认
为什么还需要等待2MSL?
为了保证客户端发送的最后一个确认,能够达到服务器
这个ACK可能丢失,就会导致服务器在LAST-ACK状态,没办法正常结束,那么服务器收不到就会超时重传可以断开的消息
那么A就能够在这个2MSL中收到这个重传的消息,并且重新计时2MSL
而且,客户端持续2MSL时间后断开,就可以保证这个连接的所有报文都会死亡,可以看下MSL的含义,也就是2MSL之后,断开这个连接之后,肯定不会还存在这个连接的旧的报文了
上面说的过程虽然说得是TCP的交流,但是尽管同层之间看似同层交换,但是能不用底层的协议嘛
TCP的数据也还都是要经过IP层,链路层、网卡、路由,一样不缺
IP层处理
TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成包发送给通信对象。
关于TCP和IP的分段
我们之前讲过TCP会进行分段,IP也会进行分段,看起来有点奇怪
有两个概念:
MTU(Maximum Transmission Unit,MTU),最大传输单元,以太网和802.3对数据帧的长度都有一个限制,其最大值分别是1500和1492个字节
MSS(Maxitum Segment Size)最大分段大小,TCP数据包每次能够传输的最大数据分段,往往MSS为1460
所以说TCP分段的原因是MSS,IP分片的原因是MTU
对于TCP来说,MSS<MTU,所以说,对于TCP报文,IP不需要再继续对他进行分段了
但是IP是为运输层服务的,不仅仅是为了TCP,还有UDP,UDP则很可能会导致IP分段
首部由两部分组成,分为固定部分和可变部分(就是固定参数和可选参数的概念)
这些数据都是由操作系统组装生成的,比如目的地址就是来源于应用程序的层层传递
ip地址与硬件地址
ip地址是网际层以及以上使用的,是一种逻辑地址,不存在物理实体,因为IP地址是用软件实现的,没有一台机器被写死某个IP
数据经过IP处理封装后,数据报会交给数据链路层,会被封装成MAC帧
MAC帧在传送时,使用的是物理地址,源地址和目的地址都是硬件地址,这两个地址被写入MAC帧的头部
连接在通信线路上的网络设备(主机或者路由器)在收到MAC帧之后,根据MAC帧首部的硬件地址决定收下或者丢弃。
总之,IP地址保存在IP数据报的首部,MAC地址保存在MAC数据报的首部
网际层以及网际层以上使用IP地址,数据链路层以及以下使用硬件地址
硬件地址就是网卡的地址,任何一块网卡,都有一个唯一的物理地址,这就是MAC地址,所以叫做硬件地址,因为是被写死固化在网卡中的
一个网卡就有一个MAC地址,如果计算机中安装多个网卡,那么就会有多个MAC地址
既然有了IP为什么还要使用MAC?或者说有了MAC为什么还需要IP呢?
IP层关注的是网际层的传输,但是具体的数据传输必然无法脱离具体的物理线路以及组网环境
IP地址是根据网络的拓扑结构分配的,所以根据IP可以实现高效的路由选择
物理地址跟网络拓扑结构没有任何关系,显然不适合用来路由选择,而且,如果物理地址跟网络拓扑建立了关联
那么,对于设备的更换,或者网卡的更换,将会增加难度
而且
实际的物理组网环境多变复杂,所以才会分为网际层和网络接口层,所以才会同时使用IP地址和MAC地址
IP地址可以实现高效的路由选择,MAC地址实现相邻链路间的数据传送
本文作者:程序员潇然 疯狂的字节X https://crazybytex.com/
应用层通过借助于操作系统的DNS解析可以获得接收方的IP地址,操作系统也知道本机的IP地址
如何确定目的MAC?
答案是通过路由表,这是操作系统实现的功能,其实也还是借助于各种协议来生成的,说白了就是计算规则,先找到下一个中转设备的IP地址
然后再借助于ARP协议,查询MAC地址
在windows下可以通过命令 route print查看
路由表是怎么来的?
有几种常见的路由选择协议,路由选择协议的核心就是路由选择算法
互联网采用分层次的路由选择协议
因为互联网的规模本身非常大,不可能让所有的路由都知道所有的网络怎么到达,这是不现实的
另外本身很多单位,并不希望外界了解自己内部的路由选择协议以及网络布局细节
所以互联网被划分为了多个自治系统(autonomous system AS)
其实还是分片管理,区域自治的思想
在目前的互联网中,一个ISP就是一个自治系统,这样互联网的路由选择协议就分为两大类:
一个是AS内部,一个是AS与AS之间
内部网关协议IGP Interior Gateway Protocol,AS内部使用的路由选择协议,目前主要是RIP和OSPF
外部网关协议EGP External Gateway Protocol,两个AS之间相互联系时使用的协议,目前是BGP-4(版本4)
自治系统之间的又叫做域间路由选择,自治系统内部的路由选择叫做域内路由选择
路由选择协议是一个自学习的过程,通过相互之间的信息互换最终形成了路由表
想要了解具体的学习过程可以查看路由选择协议的相关资料
路由表怎么使用?
- 网络目标(destination): 目的地网段
- 网络掩码(mask):子网掩码
- 网关(gateway):下一跳路由器入口的ip
如果是本地计算机直接连接到的网络,网关通常是本地计算机对应的网络接口,但是此时接口必须和网关一致;
如果是远程网络或默认路由,网关通常是本地计算机所连接到的网络上的某个服务器或路由器
- 接口(interface): 接口定义了针对特定的网络目的地址,本地计算机用于发送数据包的网络接口
- 跃点数(metric) :可理解为跳数,表示该条路由记录的质量,一般情况下,如果有多条到达相同目的地的路由记录,路由器会采用metric值小的那条路由
简单说就是在“网络目标“里面查找匹配,匹配到了就使用”接口“ 发往”网关“。
网络目标0.0.0.0 表示默认路由,也叫做缺省路由,如果路由表匹配不到则通过这一条记录执行
127.0.0.0 表示环回地址,系统将接收发送给该网段的所有数据包
总之,通过这个路由表
我们就能够得到想要发送到目的地IP,下一跳的IP地址是什么,以及网络接口的IP地址,也就是发送方的IP地址
下一跳的IP地址有什么用?
有了下一跳IP地址,我们就可以查找他的物理地址,这样就可以获得下一个设备的MAC地址了
如何根据IP地址查询MAC地址呢?
答案是通过ARP协议
ARP 地址解析协议Address Resolution Protocol
网络层使用的是IP地址,但在实际网络的链路上传送数据帧时,最终还是必须使用该网络的硬件地址。
但IP地址和下面的网络的硬件地址之间由于格式不同而不存在简单的映射关系(例如,IP地址有32位,而局域网的硬件地址是48位),也就是说没办法单纯的像个文件一样一一对应,或者写死的那种
此外,在一个网络上可能经常会有新的主机加入进来,或撤走一些主机
更换网络适配器也会使主机的硬件地址改变
地址解析协议ARP解决这个问题的方法是:
在主机ARP高速缓存中存放一个从IP地址到硬件地址的映射表,并且这个映射表还经常动态更新(新增或超时删除)。
每一台主机都设有一个ARP高速缓存(ARP cache),里面有本局域网上的各主机和路由器的IP地址到硬件地址的映射表
假设需要查找主机B,IP地址为X,B可能新来的,也可能主机才开始运行,缓存为空,总之,没有差找不到IP地址为X的主机B的信息
首先,ARP在局域网上广播发送ARP请求分组
大致内容是:
“我的IP地址是XXX,我的MAC地址是YYY,我想找IP地址为ZZZ的主机收到请回答
然后 局域网内所有运行ARP的主机都会收到这个请求
最后 主机B的IP地址就是请求中要查询的主机,所以会应答,其他人则不理睬这个请求,因为不是找他们的
ARP请求是广播,这个响应就是普通的单播了
此时一旦收到了响应就得到主机B IP地址为X的物理地址了
由于是IP协议使用了ARP协议,因此通常就把ARP协议划归网际层
但ARP协议的用途是为了从网络层使用的IP地址,解析出在数据链路层使用的硬件地址。所以有的书又说是链路层
此时经过路由表和ARP协议我们获得了目的MAC地址
IP 生成的网络包只是存放在内存中的一串数字信息,没有办法直接发送给对方
接下来就是数据链路层的相关职责,数据链路层属于计算机网络的低层
数据链路层使用的信道主要有以下两种类型:
- 点对点信道 这种信道使用一对一的点对点通信方式。使用的协议有ppp点对点协议
- 广播信道 这种信道使用一对多的广播通信方式,因此过程比较复杂
广播信道上连接的主机很多,因此必须使用专用的共享信道协议来协调这些主机的数据发送。
广播信道可以进行一对多的通信,局域网使用的就是广播信道
以太网(Ethernet)是一种计算机局域网技术规范,以太网是目前应用最普遍的局域网技术,他可不是某种具体的网络
所以接下来就是借助链路层处理封装成帧进行传输了
链路层处理
计算机与外界局域网的连接是通过通信适配器(adapter)进行的,也就是网卡
网卡上装有RAM和ROM
适配器和局域网之间的通信是通过电缆或者双绞线串行传输方式通信
适配器和操作系统之间的通信是通过主板上的IO总线并行传输的
网卡的正确运行还需要操作系统安装网卡驱动程序
他们掌管着应当从存储器的什么位置上把多长的数据块发送到局域网
或者应当在存储器的什么位置上把局域网传送过来的数据块存储下来
还要能够实现以太网协议
适配器在接收和发送各种帧时,不使用计算机的CPU,这时计算机中的CPU可以处理其他任务。
当适配器收到有差错的帧时,就把这个帧直接丢弃而不必通知计算机。
当适配器收到正确的帧时,它就使用中断来通知该计算机,并交付协议栈中的网络层。
当计算机要发送IP数据报时,就由协议栈把IP数据报向下交给适配器,组装成帧后发送到局域网。
网卡驱动程序部分就是实现链路层的相关协议的实体
数据链路层协议有许多种,但有三个基本问题则是共同的。
这三个基本问题是:封装成帧、透明传输和差错检测
封装成帧
封装成帧((framing)就是在一段数据的前后分别添加首部和尾部,这样就构成了一个帧。
接收端在收到物理层上交的比特流后,就能根据首部和尾部的标记,从收到的比特流中识别帧的开始和结束。
透明传输
“透明” 它表示:某一个实际存在的事物看起来却好像不存在一样
“在数据链路层透明传送数据”表示无论什么样的比特组合的数据,都能够按照原样没有差错地通过这个数据链路层
因此,对所传送的数据来说,这些数据就“看不见”数据链路层有什么妨碍数据传输的东西。或者说,数据链路层对这些数据来说是透明的。
使用专门的控制字符,如果出现控制字符则插入转义字符,这种方法被称为字节填充
差错检测
现实的通信链路都不会是理想的,这就是说,比特在传输过程中可能会产生差错
1可能会变成0,而0也可能变成1。这就叫做比特差错。比特差错是传输差错中的一种
目前在数据链路层广泛使用了循环冗余检验CRC (Cyclic Redundancy Check)的检错技术。
常用的以太网MAC帧格式有两种标准
- 一种是DIX Ethernet V2标准(即以太网V2标准)
- 一种是IEEE的802.3标准
就这样,经过驱动程序和网卡,数据就被从TCP层再到IP层再到链路层再到物理层完成了数据从计算机端口的输出
注意,以上TCP、IP、链路层的处理,都是由操作系统以及网卡驱动程序按照TCP/IP的协议以及以太网等相关技术实现的
上面各个模块功能的划分描述只是大致的一般性的
操作系统到底实现到协议的哪一部分以及网卡驱动到底实现到哪一部分的细节,完全由实现决定
数据转发接收
数据从应用层,经过操作系统和网卡驱动程序对协议栈的实现,在经过网卡转换为广电信号从网口发送出去
接下来就是借助于网络设备进行数据传送
设备主要是交换机、路由器。不过现在路由器都集成了这几个的功能
但是本质是一样的,可以理解为网络设备传输需要某些功能,而这几种功能通过交换机、路由器实现了
随着技术发展,设备的功能集成度很高,家用路由器都集成了这些功能
也有三层交换机,实现了转发和路由的功能
所以,根本是我们要理解交换机、路由器这几个设备的功能点,从而了解数据处理过程,到底是个什么设备我们可以不用关心
先了解下发展,最初的设备是集线器
集线器的英文称为“Hub”。“Hub”是“中心”的意思,集线器的主要功能是对接收到的信号进行再生整形放大,以扩大网络的传输距离
集线器属于物理层设备,与网卡、网线等传输介质一样,属于局域网中的基础设备,集线器(hub)属于纯硬件网络底层设备
由于集线器会把收到的任何数字信号,经过再生或放大,收到1就转发1,收到0就转发0
从集线器的所有端口提交,这会造成信号之间碰撞的机会很大
而且集线器不进行碰撞检测,若两个接口同时有信号输入(即发生碰撞),那么所有的接口都将收不到正确的帧
而且信号也可能被窃听,并且这代表所有连到集线器的设备,都是属于同一个碰撞域名以及广播域名,因此大部份集线器已被交换机取代。
组建局域网可以用集线器,也可以用交换机。通过集线器连接的一组工作站同属一个冲突域,也同属一个广播域
为了能够具有更高性能的扩展以太网,出现了网桥,网桥对收到的帧根据其MAC帧的目的地址进行转发和过滤。
网桥收到一个帧时,并不是向所有的接口转发此帧
而是根据此帧的目的MAC地址,查找网桥中的地址表,然后确定将该帧转发到哪一个接口,或者是把它丢弃(即过滤)
再后来又出现了交换机(二层交换机),核心与网桥差不多,但是改进了很多,反正交换机是网桥的进化版本就是了
交换机也被称为多接口的网桥
以太网的交换机的每个接口都直接与一台主机或者另一个以太网交换机连接,并且是全双工模式
全双工就好比双向两车道(两个方向可以同时进行),如果其中一条道堵上了,被堵方向的车辆就需要借道,双方交替,某一瞬间只能有一辆车通过,这就是半双工。
而且
以太网还具有并行性,即同时联通多接口,使多对主机同时进行通信(网桥只能一次分析和转发一个帧)相互通信的主机都是独占传输媒体,无碰撞的进行数据传输。
交换机根据MAC地址转发是借助于内部的交换表,是通过自学习算法自动的建立起来的
交换机使用专用的交换芯片,用硬件转发,其转发效率比用软件转发的网桥快很多。
交换表是如何学习的?
假设最开始是空的,当A发送数据给B时,从1进入,交换机将A,1 记录在表中
此时交换机不知道要送到哪里,所以广播(转发到除了入口之外的所有端口),C,D会丢弃,B会接收到
B要发送给A时,从3进入,交换机将B,3记录在表中,查表得到A的端口1,所以从1转发出去
只要经过这样的步骤,只要C,D也发送数据,那么就最终会将所有主机的地址与端口进行匹配,就得到了完整的交换表
表中的数据都是有时效性的,过期的条目会被自动删除,所以可以很方便的更换主机,哪怕主机更换网卡
不过这个自学习的过程,某种条件下会产生问题,可能会成环
假如,最开始A通过1 向B发送一帧,交换机#1广播,假设3收到后发送到交换机#2的1号口,然后再到2号口,形成了环装,浪费了资源
为了解决这种问题,有一个协议生成树协议STP Spanning Tree Protocol
,主要就是为了不改变网络拓扑结构的情况下,切段环形
如果交换机收到的目的地址就是源端口地址,会简单的丢弃包
因为A发出后,经过集线器会发送给B,如果交换机再将这个包返回,达到集线器之后,会再次的发送给A和B,这是有问题的
此外,如果接收方MAC地址是一个广播地址 A,那么交换机会将包发送到除源端口之外的所有端口。
网桥和交换机用于分割冲突域
冲突域简单理解就是连接在一块的节点的集合,集线器连接的所有节点,就是一个冲突域,因为他不做任何区分,所有的数据都广播发到所有节点
网桥和交换机工作在链路层,会根据MAC地址进行转发,所以可以分割冲突域,减少了不必要的广播数据,让网络通道舒服了一些
路由器是一种具有多个输入端口和多个输出端口的专用计算机,其任务是转发分组
从路由器某个输入端口收到的分组,按照分组要去的目的地(即目的网络),把该分组从路由器的某个合适的输出端口转发给下一跳路由器
下一跳路由器也按照这种方法处理分组,直到该分组到达终点为止。路由器的转发分组正是网络层的主要工作
路由器的结构
路由器包括转发模块和端口模块两部分
路由器的行为和交换机是类似的,只不过路由器工作在网络层,处理的是IP
路由器的端口的硬件将包接收进来
接下来,转发模块会根据接收到的包的 IP 头部中记录的接收方 IP 地址,在路由表中进行查询,以此判断转发目标。
然后,转发模块将包转移到转发目标对应的端口,端口再按照硬件的规则将包发送出去
路由器的端口具有MAC 地址,所以他可以作为以太网的接收方和发送方,端口还具有 IP 地址
首先路由器端口会接收发给自己的以太网包,然后查询转发目标,再由相应的端口作为发送方将以太网包发送出去
这一点和交换机是不同的,交换机只是将进来的包转发出去而已,它自己并不会成为发送方或者接收方
需要记住:
路由器的各个端口都具有 MAC地址和 IP 地址
交换机在地址表中只匹配完全一致的记录,而路由器则会忽略主机号部分,只匹配网络号部分
这涉及到子网划分
总之
路由器只看网络号,不关注具体的门牌号,负责到小区,这也是路由表中为何有子网掩码的信息,因为路由器需要知道网络号的位数
路由器中的路由表和上面计算机操作系统中的路由表是一个概念,逻辑思路也是一样的
只不过具体实现肯定不同(操作系统的代码跟路由器的代码怎么可能一样)
对路由表进行维护的方法有几种,大体上可分为以下两类。
- 由人手动维护路由记录
- 根据路由协议机制,通过路由器之间的信息交换由路由器自行维护路由表的记录
其中 2 中提到的路由协议有很多种,例如RIP、OSPC、BGP 等都属于路由协议
路由表记录维护的方式和交换机也有所不同
交换机中对 MAC 地址表的维护是包转发操作中的一个步骤
而路由器中对路由表的维护是与包转发操作相互独立的,也就是说,在转发包的过程中不需要对路由表的内容进行维护。
前面讲到的交换机会在转发时记录端口与mac,路由器进行分租转发过程中不会做类似的事情
路由器处理过程
再次梳理下路由器的处理过程:
首先,信号到达网线接口部分,然后通过网卡将信号转换为数字信息
然后通过包末尾的 FCS 进行错误校验,如果没问题则检查 MAC 头部中的接收方 MAC 地址
确认是否发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。
路由器的端口都具有MAC地址,只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃。
计算机网卡对数据的接收也是这样子的,网卡和驱动程序负责了这部分内容
然后,完成包接收操作之后,路由器就会丢弃包开头的 MAC 头部。
因为MAC 头部的作用就是将包送达路由器,完成这一段链路上数据的传送,如果这个下一站是路由器,那么其中的接收方 MAC 地址就是路由器端口的 MAC 地址。
因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC头部就会被丢弃。
然后,路由器会根据 IP 头部中的内容进行包的转发操作。
转发操作分为几个阶段,首先是查询路由表判断转发目标。
路由器根据路由表进行匹配的时候,路由器首先寻找网络号比特数最长的一条记录
因为网络号比特数越长,说明主机号比特数越短,也就意味着该子网内可分配的主机数量越少,即子网中可能存在的主机数量越少
这一规则的目的是尽量缩小范围,所以根据这条记录判断的转发目标就会更加准确
如果路由器发送出现问题,路由器会丢弃这个包,并通过ICMP 消息告知发送方主机。
这里的处理方式和交换机不同, 原因在于网络规模的大小。
交换机连接的网络最多也就是几千台设备的规模,遇到不知道应该转发到哪里的包
交换机可以将包发送到所有的端口上,虽然这个方法很简单粗暴,但不会引发什么大问题。
可是,路由器工作的网络环境就是互联网,它的规模是远远大于以太网的,所以不能将包发送到整个网络上,那就会产生大量的网络包,造成网络拥塞。
因此,路由器遇到不知道该转发到哪里的包,就会直接丢弃。
ICMP
为了更有效地转发IP数据报和提高交付成功的机会,在网际层使用了网际控制报文协议ICMP (Internet Control Message Protocol)
ICMP允许主机或路由器报告差错情况和提供有关异常情况的报告。
ICMP是互联网的标准协议。
但ICMP不是高层协议(看起来好像是高层协议,因为ICMP报文是装在IP数据报中,作为其中的数据部分),而是IP层的协议。
ICMP报文作为IP层数据报的数据,加上数据报的首部,组成IP数据报发送出去。
下一步的前置处理
路由器的转发与计算机操作系统中IP实现模块的工作核心点是一样的,他们都是负责交付IP数据报
路由器从路由表中查询到转发目标之后,数据包会转发给指定的端口
在此之前还需要做一系列的处理
TTL
第一个工作是更新 IP 头部中的 TTL(Time to Live,生存时间)字段。
TTL 字段表示包的有效期,包每经过一个路由器的转发,这个值就会减 1,当这个值变成 0 时,就表示超过了有效期,这个包就会被丢弃,防止死循环永远发送。
分片
路由器的端口并不只有以太网一种,也可以支持其他局域网或专线通信技术。
不同的线路和局域网类型各自能传输的最大包长度也不同,因此输出端口的最大包长度可能会小于输入端口。
即便两个端口的最大包长度相同,也可能会因为添加了一些头部数据而导致包的实际长度发生变化,ADSL、FTTH 等宽带接入技术中使用的 PPPoEB 协议就属于这种情况。
无论哪种情况,一旦转发的包长度超过了输出端口能传输的最大长度,就无法直接发送这个包了。
遇到这种情况,可以使用 IP 协议中定义的分片功能对包进行拆分,缩短每个包的长度。
查询物理地址
与计算机操作系统的处理类似,知道了目标地址,可以根据ARP协议查询目的MAC
路由器的端口也具有网卡,具有MAC以及IP,所以就是网卡负责链路层帧的封装以及光电信号的转换,然后发送
就是这样一段一段,就像快递一样,就到达了最终的目的地
IP(路由器)负责将包发送给通信对象这一整体过程,而其中将包传输到下一个路由器的过程则是由以太网(交换机)来负责的。
整个传输过程中,目的IP并不会发生变化
虽然我们通过路由表查找下一跳的IP地址,但是是为了查询下一个设备的物理地址,进而借助于以太网进行发送
本文作者:程序员潇然 疯狂的字节X https://crazybytex.com/
下一跳的IP地址并不存在与传输线路上,传输线路上一直都是目的IP,而以太网的源MAC和目的MAC却是一直在变化的
所以说
IP负责包发送给通信对象这一整体过程,以太网交换机负责将包传输到下一个路由器。
为什么要分IP层和链路层?
网络并非只有以太网一种
还有无线局域网,以及接入互联网的通信线路,它们和 IP 之间的关系又是什么样的呢?
其实只要将以太网替换成无线局域网、互联网线路等通信规格就可以了。
也就是说
如果和下一个路由器之间是通过无线局域网连接的,那么就委托无线局域网将包传输过去;
如果是通过互联网线路连接的,那么就委托它将包传输过去。
除了这里列举的例子之外,世界上还有很多其他类型的通信技术
它们之间的关系也是一样的,都是委托所使用的通信技术将包传输过去。
IP 本身不负责包的传输,而是委托各种通信技术将包传输到下一个路由器
这样的设计是有重要意义的
即可以根据需要灵活运用各种通信技术,这也是 IP 的最大特点。
正是有了这一特点,我们才能够构建出互联网这一规模巨大的网络
所以这一点再次的看出来,为什么网络的通信模型采用分层次的结构设计
通过层次的设计,IP层就专职负责整体交付就好了
每一层都能够独立发展,运用最合适的技术
数据的接收
从网卡到操作系统
计算机数据的一部分接收过程与路由器逻辑是一模一样的
通过网线的光/电信号,达到计算机的网口,转换为数字信号
会通过包的帧校验序列(FCS)来校验错误,如果是错误的也会丢弃
当 FCS 一致,即确认数据没有错误时,接下来需要检查 MAC 头部中的接收方 MAC 地址,如果是发给自己的,就缓存下来,通知操作系统进行处理
路由器此时的处理是丢掉MAC头部,然后解析IP数据报信息,然后会查找转发表开始准备转发了
而我们的计算机操作系统作为最终目的地,当然不再需要转发,会根据收到的MAC数据进行处理
网卡每收到一个MAC帧就先用硬件检查MAC帧中的目的地址。
如果是发往本站的帧则收下,然后再进行其他的处理。
否则就将此帧丢弃,不再进行其他的处理。这样做就不浪费主机的处理机和内存资源。
这里“发往本站的帧”包括以下三种帧:
- (1)单播(unicast)帧(一对一),即收到的帧的MAC地址与本站的硬件地址相同。
- (2)广播(broadcast)帧(一对全体),即发送给本局域网上所有站点的帧(全1地址)。
- (3)多播(multicast)帧(一对多),即发送给本局域网上一部分站点的帧。
所有的适配器都至少应当能够识别前两种帧,即能够识别单播和广播地址。有的适配器可用编程方法识别多播地址。
当操作系统启动时,它就把适配器初始化,使适配器能够识别某些多播地址。显然,只有目的地址才能使用广播地址和多播地址。
网卡需要通过中断将网络包到达的事件通知给 CPU之后,接下来,CPU 就会暂停当前的工作,并切换到网卡的任务。
然后,网卡驱动会开始运行,从网卡缓冲区中将接收到的包读取出来,根据 MAC头部的以太类型字段判断协议的种类,并调用负责处理该协议的软件。
这里,以太类型的值应该是表示 IP 协议,因此会调用 TCP/IP 协议栈,并将包转交给它。
简言之
网卡接收到数据之后,产生中断,汇报操作系统,操作系统过来读取数据,根据类型交给指定的协议栈实现部分
至此,数据已经从另一端达到了目的地计算机了
IP模块接收
当网络包转交到协议栈时,IP 模块会首先开始工作,检查 IP 头部。
IP模块首先会检查 IP 头部的格式是否符合规范,然后检查接收方 IP 地址,看包是不是发给自己的
确认包是发给自己的之后, 接下来需要检查包有没有被分片
检查 IP头部的内容就可以知道是否分片
如果是分片的包,则将包暂时存放在内存中,等所有分片全部到达之后将分片组装起来还原成原始包;
如果没有分片,则直接保留接收时的样子,不需要进行重组
到这里,我们就完成了包的接收。
IP是对运输层负责的,运输层也就是TCP或者UDP
所以接下来需要检查 IP 头部的协议号字段,并将包转交给相应的模块。
例如
如果协议号为06(十六进制),则将包转交给 TCP 模块;
如果是 11(十六进制),则转交给 UDP 模块。
简言之
协议栈的IP模块会检查IP头部
- (1) 判断是不是发给自己的;
- (2) 判断网络包是否经过分片;
- (3) 将包转交给TCP模块或UDP模块。
TCP模块接收-连接
当 TCP 头部中的控制位 SYN 为 1 时,表示这是一个发起连接的包。
这时,TCP 模块会执行接受连接的操作
不过在此之前
需要先检查包的接收方端口号,并确认在该端口上有没有与接收方端口号相同且正在处于等待连接状态的套接字。
- 如果指定端口号没有等待连接的套接字,则向客户端返回错误通知的包。
- 如果存在等待连接的套接字,则为这个套接字复制一个新的副本
并将发送方 IP 地址、端口号、序号初始值、窗口大小等必要的参数写入这个套接字中
同时分配用于发送缓冲区和接收缓冲区的内存空间。
然后生成代表接收确认的 ACK 号,用于从服务器向客户端发送数据的序号初始值,表示接收缓冲区剩余容量的窗口大小,并用这些信息生成 TCP 头部,委托IP 模块发送给客户端 。
这不就是上面说过的TCP的三次握手嘛
这是其中的第二步,收到“TCP 头部中的控制位 SYN 为 1 时”是第一步
这个包到达客户端之后,客户端会返回表示接收确认的 ACK 号,当这个ACK 号返回服务器后,连接操作就完成了
TCP模块接收-传输
然后就是数据的传送阶段了
数据包重复着上面的过程,TCP 模块会检查收到的包对应哪一个连接
在服务器端,可能有多个已连接的套接字对应同一个端口号,因此仅根据接收方端口号无法找到特定的套接字。
这时我们需要根据 IP 头部中的发送方 IP 地址和接收方 IP 地址,以及 TCP 头部中的接收方端口号和发送方端口号共 4 种信息,找到上述4 种信息全部匹配的套接字。
简言之
发送方 IP 地址和接收方 IP,发送方端口和接收方端口唯一确定一条连接(另外其实包含协议,应该是五个确定一条连接)
找到 4 种信息全部匹配的套接字之后
TCP 模块会对比该套接字中保存的数据收发状态和收到的包的 TCP 头部中的信息是否匹配,以确定数据收发操作是否正常。
具体来说,就是根据保存的上一个序号和数据长度计算下一个序号
并检查与收到的包的 TCP 头部中的序号是否一致 。如果两者一致,就说明包正常到达了服务器,没有丢失。
这时
TCP模块会从包中提出数据,并存放到接收缓冲区中,与上次收到的数据块连接起来。这样一来,数据就被还原成分包之前的状态了。
当收到的数据进入接收缓冲区后,TCP 模块就会生成确认应答的 TCP头部,并根据接收包的序号和数据长度计算出 ACK 号,然后委托 IP 模块发送给客户端。
这将又是一条漫长的旅途同发送数据类似,不过我们根本感觉不到。
web应用程序接收
收到的数据块进入接收缓冲区,意味着数据包接收的操作告一段落了。
接下来
应用程序会调用 Socket 库的 read来获取收到的数据,这时数据会被转交给应用程序。
如果应用程序不来获取数据,则数据会被一直保存在缓冲区中
但一般来说,应用程序会在数据到达之前调用 read 阻塞式的等待数据到达
在这种情况下,TCP 模块在完成接收操作的同时,就会执行将数据转交给应用程序的操作。
接收到请求后,会根据URI转换为实际的文件,这些事情web服务器会进行处理
如果 URI 指定的文件内容为 HTML 文档或图片,那么只要直接将文件内容作为响应消息返回客户端就可以了。
但 URI 指定的文件内容不仅限于 HTML 文档,也有可能是一个程序。
在这个情况下,服务器不会直接返回文件内容,而是会运行这个程序,然后将程序输出的数据返回给客户端
而响应的内容就是HttP协议中对于响应的约束。
总结
如下图所示,数据就是这样一层层的不断进行数据封装传递。
应用层协议的软件,根据目的地的IP地址(如果是域名需要先进行DNS解析)以及端口号,再加上其他的信息按照HTTP协议组装成HTTP请求数据。
然后借助于操作系统提供的Socket接口,调用TCP模块对数据进行处理,添加头部,分片等
TCP想要发送数据前,还需要建立连接,也就是Socket的连接过程,这个建立连接的过程的数据发送流程与数据传送阶段的流程相同
他们都是TCP组装好数据然后交由IP模块进行处理
IP模块根据目的地IP地址,借助于操作系统的路由表选择下一跳路由,而路由表又是根据各种路由选择协议自学习而来的
得到了下一跳的路由,再根据ARP协议就可以查询得到下一站的物理地址
应用程序借助于操作系统建立Socket连接,会分配端口,也就是源端口
此时就有了源IP地址,目的IP地址,源端口,目的端口,源MAC,目的MAC
然后操作系统与网卡驱动配合将数据封装成以太网帧,并且借助于网卡将数据转换为光电信号发送出去
路由器的端口有网卡和MAC,所以可以作为接收方
网卡将光电信号转换为数据,然后进行数据校验,MAC检验,确认是发给自己的
然后就根据路由表进行转发,这个路由表跟操作系统的路由表一样的逻辑原理
这一步又跟操作系统IP模块的处理差不多,找到了下一跳IP然后ARP协议查找MAC,然后更改源MAC和目的MAC
完成数据在链路层的传递
最终通过一段一段的链路到达目的地主机
主机的网卡类似路由器,也是光电信号转换接收,然后检验,MAC检验,然后通知操作系统
操作系统解析MAC数据报,然后IP模块接收,然后TCP/UDP,然后最终到达WEB应用程序。
这就是大致,大致的网络通信过程。
参考:计算机网络(谢希仁)、网络是怎样连接的(户根勤,周自恒(译者))
转载务必注明出处:程序员潇然,疯狂的字节X,https://www.crazybytex.com/thread-34-1-1.html