一不小心又鸽了这么久,,,咳咳,这节我们来继续了解计网中的网络数据通信基本流程,也就是一个数据包是怎么在网络上传输并被对端接收使用的
一、网络数据通信基本流程
首先我们要清楚一个概念,我在上一篇内容中也提到过,那就是应用层数据会先在协议栈中进行层层处理,形成数据帧或者数据包等数据单元,最后在物理层通过编码、调制与光电转换等步骤,使其变成能够在信道上传输的信号,这个协议栈上处理的过程就是封装
1.1 封装
再展开来说,应用程序获取到用户输入,构造一条应用层报文 (遵守应用层协议,先留个扣子),这类报文往往是结构化数据,发送前需要将其编码为字节序列(这个过程叫做序列化/编码);应用程序调用传输层接口,把数据交给传输层,传输层为其添加首部(包含源端口、目的端口等字段),形成TCP段或UDP数据报 ;传输层构造好数据后,继续调用网络层的API,把传输层的数据段交给网络层继续处理,网络层给这个数据段加上IP报头形成IP数据包 ;接下来IP协议继续调用数据链路层的API,把IP数据包交给数据链路层,数据链路层的核心协议"以太网",会在网络层数包的基础上继续加上链路层首部和校验和形成以太网数据帧;最后由网卡在物理层把比特编码到介质上,以电信号/光信号(无线则为射频电磁波)形式发出。此时一个用户输入就完成了层层封装并在信道中传输了
从上层到下层,数据经过协议栈的一系列加工,完成了封装;数据经过信道的传输,传送到对端后需要从下到上依此解析,这就是分用
1.2 分用
数据现在已经达到了接收方的网卡(此时数据是电信号或光信号),网卡将这个信号转成二进制比特流,交给上层数据链路层处理;数据链路层按照以太网协议进行解析,先把帧头和校验和去掉并验证校验和(验证通过才上交,不通过直接丢弃),剩下的载荷往上传递给网络层;网络层拿到数据后,按照IP协议的格式解析,再把载荷数据交给传输层;传输层拿到数据,按照TCP或者UDP协议解析,取出载荷交给应用层(上面我们提到,传输层报头中有源端口和目的端口,通过目的端口号传输层得知数据该交给哪个应用程序);最后应用层拿到数据并解析,得到发送方输入的信息
当然,实际通信过程中也不是所有数据都会经过这五层协议的封装和分用
还记得之前我们提到的几种网络设备,其中交换机和路由器就只需要经过一部分协议栈的封装分用
1.3 二层转发
交换机,只需要封装分用到数据链路层即可(交换机工作在数据链路层)
主机的数据------>交换机,交换机收到后交给物理层处理、数据链路层处理,重新构造出新的以太网数据帧------>发给下一个设备
因为在数据链路层中得到的以太网数据帧的帧头信息足以支持交换机进行下一步工作
1.4 三层转发
类似的,路由器只需要封装分用到网络层即可(路由器工作在网络层)
主机的数据------>路由器,路由器收到后交给物理层、数据链路层、网络层解析,更新必要字段后转发IP包,并重新二次封装------>发送给下一个设备
当然,也有只工作在物理层的,例如集线器、网线延长线等,工作在传输层的,防火墙、网关等,这里就不一一介绍了
理解了封装分用等过程,你可能会有一个疑问,这一层一层又是报头又是帧头的,明明看到的都是二进制数据,这是怎么拆出来的呢?这就涉及到我们常说的协议部分了
二、协议
我们上面提到的以太网协议、TCP协议、IP协议等都做了一件事情------约定好格式
换句话说,就是规定了在这串比特,哪几位表示什么
协议格式都是提前约定好的,解析完全依赖固定结构和关键字段,换句话说,协议让我们能够"按位置读",而不是看内容猜
比如一般一个数据帧可能长这样:
- 前14字节是以太网头
- 接下来至少20字节是IP头
- TCP头至少20字节
- 剩下的全是上层数据
这些全都是写进RFC标准中的,所以接收方在解析时逻辑非常简单:我在第N层,按照第N层协议格式读字段
有了协议,我们再来重新看一下上面数据前后的准备工作
2.1 数据链路层
网卡从物理层拿到一串比特流后,第一件事是:这是不是一个以太网帧
以太网帧结构:
cpp
| 目的 MAC | 源 MAC | Type | Payload | FCS |
关键在Type字段:
- 0x0800 → IPv4
- 0x86DD → IPv6
- 0x0806 → ARP
交换机/网卡就是靠这个字段判断载荷应该交给哪个上层协议处理
如果帧格式不对或者CRC校验失败,这个数据包就会被直接丢弃,网络层都见不到它
2.2 网络层
前面我们说,数据链路层靠以太网头里的Type字段判断载荷是ipv4 / ipv6 / ARP
那问题又来了,以太网把这个数据包交给IPV4之后,IPV4又怎么知道上面是TCP还是UDP?还是什么别的东西呢?
答案很简单,IP头里也有一个Type一样的字段,只不过它叫Protocol(协议号)
IPV4头长这样
cpp
| Version | IHL | ... | Total Length |
| ... |
| TTL | Protocol | Header Checksum |
| Source IP |
| Destination IP |
| (Options 可选) |
几个比较关键的字段:
- IHL:IP头长度
- Total Length:整个IP包的长度(报头+数据载荷)
- Protocol:决定交给谁(常见的:1--->ICMP,6--->TCP,17--->UDP)
- TTL:每经过一个路由器减1,减到0就丢弃(这个字段的作用是防止包在网络中绕圈圈绕到噶)
所以网络层的分用逻辑也很朴素:我是IP,我按照固定格式读字段,我看到Protocol等于6,那就把载荷交给TCP,看到Protocol等于17,就把载荷交给UDP
2.3 传输层
好了,现在TCP或UDP拿到数据了,那它怎么知道这段数据要交给哪个应用?
靠的就是前面提到的端口号
UDP头很短,大概长这样
cpp
| Source Port | Destination Port |
| Length | Checksum |
| Payload ... |
TCP头稍微长一点,其实我们关心的也就几个
cpp
| Source Port | Destination Port |
| Sequence Number |
| Acknowledgment Number |
| Flags (SYN/ACK/FIN...) | Window |
| ... |
| Payload ... |
重点还是那句:IP负责找到机器,端口负责找到程序
接收方传输层解析到目的端口后,汇报数据投递给对应的socket(可以理解为这个端口对应的接受队列/缓冲区),然后应用程序再从socket里把数据读走
到这里,我们之前讲的那句IP+端口确定唯一一个通信端点想必你已经理解了
2.4 应用层
好,现在数据历经层层拆解终于到了应用程序
但还有最后一个问题,传输层交付的其实就是一堆字节(TCP是字节流,UDP是数据报,本质都是字节),那应用程序怎么知道这堆字节代表什么呢?
答案就是:应用层协议
比如HTTP协议规定:请求头长什么样,请求头有哪些字段,请求体怎么解析
DNS也有规定:报文头有哪些字段,Query/Answer怎么组织,资源记录怎么表示
自己定义的私有协议也是一样,最常见的就是TLV(type-length-value)那一套
换句话说,应用层负责的不是怎么送到,而是数据的语义是什么,以及怎么把字节解析成结构化内容
总结一下,为什么我们能按位置读?为什么wireshark、fiddler等抓包工具能可视化数据包?本质就是协议把结构都写死了,抓包工具只是替我们做了一个可视化而已
三、同网段?跨网段?
上面讲的是数据是怎么被封装、分用的,但现实网络里还有一个很关键的问题:发送方到底把这个以太网帧发给谁的MAC?
这里必须分两种情况:同一个局域网和跨网段通信
3.1 同网段
ARP+交换机
假设A要给B发数据,A和B在同一个局域网
A知道B的IP,但不知道B的MAC,怎么办?数据链路层没法处理了这个数据包了呀,没有MAC地址,路由器或者交换机不知道这个数据包接下来该访问哪个接口了
这时候就轮到ARP出场了
- A发送ARP广播:这是谁的IP?把你的MAC告诉我(一般A是路由器,发送一个广播包,局域网内的所有节点都能接收到这个广播包)
- B收到后回ARP响应:我的我的,这是我的MAC
- A缓存一份(ARP表),然后封装以太网帧发给B
- 交换机根据MAC地址表,把帧转发到B所在端口
所以同网段通信中,交换机干的就是:看MAC------>转发
3.2 跨网段
目标IP是在另一个局域网内,但目标MAC是默认网关
重点来了,如果A要访问一个外网IP(比如你要访问某个服务器),A会先判断:这个目标IP在不在我这个网段?
判断方法也很朴素:IP&子网掩码,网络号相同就是同网段,不同就是跨网段
如果发现目标不在本地网段,则A不会直接找目标主机的MAC,而是把二层帧发给默认网关(也就是路由器的LAN口)
所以跨网段通信时,流程是:
- A发现目标IP不在本地网段
- A先ARP默认网关的IP, 拿到网关MAC
- A封装以太网帧:目的MAC=网关MAC;目的IP仍为原始的服务器IP
- 帧到了路由器,路由器拆二层------>看三层目的IP------>查路由表------>重新封装二层------>发往下一跳
换句话说:跨网段时,IP的目的地是最终主机,但以太网帧的目的MAC永远是下一跳
结束!