为了解决网络互联中异构设备的兼容性问题,并解耦复杂的网络包处理流程,OSI 模型把网络互联的框架分为应用层、表示层、会话层、传输层、网络层、数据链路层以及物理层等七层,每个层负责不同的功能。其中,应用层,负责为应用程序提供统一的接口。表示层,负责把数据转换成兼容接收系统的格式。会话层,负责维护计算机之间的通信连接。传输层,负责为数据加上传输表头,形成数据包。网络层,负责数据的路由和转发。数据链路层,负责 MAC 寻址、错误侦测和改错。物理层,负责在物理网络中传输数据帧。但是 OSI 模型还是太复杂了,也没能提供一个可实现的方法。
所以,在 Linux 中,我们实际上使用的是另一个更实用的四层模型,即 TCP/IP 网络模型。TCP/IP 模型,把网络互联的框架分为应用层、传输层、网络层、网络接口层等四层,其中,应用层,负责向用户提供一组应用程序,比如 HTTP、FTP、DNS 等。传输层,负责端到端的通信,比如 TCP、UDP 等。网络层,负责网络包的封装、寻址和路由,比如 IP、ICMP 等。网络接口层,负责网络包在物理网络中的传输,比如 MAC 寻址、错误侦测以及通过网卡传输网络帧等。
Linux 网络栈
有了 TCP/IP 模型后,在进行网络传输时,数据包就会按照协议栈,对上一层发来的数据进行逐层处理;然后封装上该层的协议头,再发送给下一层。
其中:传输层在应用程序数据前面增加了 TCP 头;网络层在 TCP 数据包前增加了 IP 头;而网络接口层,又在 IP 数据包前后分别增加了帧头和帧尾。
网络包的接收流程
我们先来看网络包的接收流程。当一个网络帧到达网卡后,网卡会通过 DMA 方式,把这个网络包放到收包队列中;然后通过硬中断,告诉中断处理程序已经收到了网络包。接着,网卡中断处理程序会为网络帧分配内核数据结构(sk_buff),并将其拷贝到 sk_buff 缓冲区中;然后再通过软中断,通知内核收到了新的网络帧。接下来,内核协议栈从缓冲区中取出网络帧,并通过网络协议栈,从下到上逐层处理这个网络帧。比如,在链路层检查报文的合法性,找出上层协议的类型(比如 IPv4 还是 IPv6),再去掉帧头、帧尾,然后交给网络层。网络层取出 IP 头,判断网络包下一步的走向,比如是交给上层处理还是转发。当网络层确认这个包是要发送到本机后,就会取出上层协议的类型(比如 TCP 还是 UDP),去掉 IP 头,再交给传输层处理。传输层取出 TCP 头或者 UDP 头后,根据 < 源 IP、源端口、目的 IP、目的端口 > 四元组作为标识,找出对应的 Socket,并把数据拷贝到 Socket 的接收缓存中。最后,应用程序就可以使用 Socket 接口,读取到新接收到的数据了。