1 TCP/IP网络模型

  • 应用层:工作在用户态,有HTTP、FTP、Telnet、DNS、SMTP协议。
  • 传输层:为应用层提供网络支持作用,有TCP和UDP协议。
  • 网络层:负责实际的传输工作,将数据从一个设备传输到另一个设备,有IP协议等。
  • 网络接口层:为网络层提供**“链路级别”**的传输服务,负责在以太网、Wifi这样的底层网络上发送原始数据包,工作在网卡这个层次,使用MAC地址标识网络上的设备。

网络接口层的传输单位是帧(frame),IP 层的传输单位是包(packet),TCP 层的传输单位是段(segment),HTTP 的传输单位则是消息或报文(message)。但这些名词并没有什么本质的区分,可以统称为数据包。

2 从键入网址到网页显示,期间发生了什么?

  1. 浏览器解析URL,生成HTTP请求

  2. 通过DNS查询真实地址:浏览器先查看自身缓存,然后询问操作系统,操作系统查看自身缓存,然后查看hosts文件,最后询问本地DNS服务器。DNS有轮询和递归两种访问策略,DNS客户端和本地服务器之间是递归策略,而本地服务器和其他DNS服务器之间是迭代轮询。

  3. 将HTTP数据报文的传输工作交给操作协调的协议栈。

    • 应用程序调用socket库,委托协议栈工作。
    • 协议栈的上半部分有两块,分别是负责收发数据的 TCP 和 UDP 协议,这两个传输协议会接受应用层的委托执行收发数据的操作。
    • 协议栈的下面一半是用 IP 协议控制网络包收发操作,在互联网上传数据时,数据会被切分成一块块的网络包,而将网络包发送给对方的操作就是由 IP 负责的。
      • 此外 IP 中还包括 ICMP 协议和 ARP 协议。
        • ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息。
        • ARP 用于根据 IP 地址查询相应的以太网 MAC 地址。
    • IP 下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收操作。
  4. 通过网卡传输二进制数据

  5. 交换机和路由器转发数据包

  6. 服务端层层拆包

  7. 返回响应走相同流程,浏览器收到响应后渲染页面

3 Linux系统是如何收发网络包的?

  • 应用程序需要通过系统调用,来跟 Socket 层进行数据交互;
  • Socket 层的下面就是传输层、网络层和网络接口层;
  • 最下面的一层,则是网卡驱动程序和硬件网卡设备;

接收网络包

NAPI机制:当网络包到达时,通过DMA技术写入置顶内存,接着网卡向CPU发起硬件中断,CPU根据中断表调用注册的中断处理函数,硬件中断处理函数首先暂时屏蔽中断,表示已经知道内存中有数据了,下次收到数据包直接写入内存,不需要通知CPU,最后发起软中断,软中断轮询处理数据,当最后一个数据不足指定大小时,恢复刚刚屏蔽的中断。

  1. 数据包首先进入网络接口层,检查合法性,不合法则丢弃,合法则找出网络包的上层协议类型(IPv4或IPv6)然后去掉帧头和帧尾,交给网络层。
  2. 网络层取出IP包,判断下一步走向(转发或处理),如果是处理则判断上层协议类型是TCP还是UDP,去掉IP头部后交给传输层。
  3. 传输层取出TCP或UDP头部,根据四元组(源IP、源端口、目的IP、目的端口)作为标识,找出对应的Socket,并把数据放入Socket的缓冲接收区。
  4. 应用层调用Socket接口,将内核Socket接收缓冲区数据拷贝至应用层缓冲区,唤醒用户进程。

发送网络包

  1. 应用程序调用Socket发送数据包接口,发生系统调用,从用户态切换为内核态,内核神器一个内核态的sk_buff,将爱过你用户待发送的数据加入到发送缓冲区。

  2. 网络协议栈从Socket发送缓冲区取出sk_buff,按照TCP/IP协议栈从上到下逐层处理。

  3. 如果是TCP协议,测绘拷贝一个sk_buff副本,因为sk_buff后续调用网络层到最终发送完成时,会被释放掉,而TCP支持丢失重传,所以在收到ACK之前sk_buff不能被删除,所以内核实际上每次调用网卡发送时,实际上传出去的是拷贝,直到收到ACK才删除。

  4. 对sk_buff填充TCP头部(sk_buff可以表示各个层的数据)

    • 当接收报文时,从网卡驱动开始,通过协议栈层层往上传送数据报,通过增加 skb->data 的值,来逐步剥离协议首部。

    • 当要发送报文时,创建 sk_buff 结构体,数据缓存区的头部预留足够的空间,用来填充各层首部,在经过各下层协议时,通过减少 skb->data 的值来增加协议首部。

  5. 然后交给网络层,在网络层里会做这些工作:选取路由(确认下一跳的 IP)、填充 IP 头、netfilter 过滤、对超过 MTU 大小的数据包进行分片。处理完这些工作后会交给网络接口层处理。

  6. 网络接口层会通过 ARP 协议获得下一跳的 MAC 地址,然后对 sk_buff 填充帧头和帧尾,接着将 sk_buff 放到网卡的发送队列中。

  7. 这一些工作准备好后,会触发「软中断」告诉网卡驱动程序,这里有新的网络包需要发送,驱动程序会从发送队列中读取 sk_buff,将这个 sk_buff 挂到 RingBuffer 中,接着将 sk_buff 数据映射到网卡可访问的内存 DMA 区域,最后触发真实的发送。

  8. 当数据发送完成以后,其实工作并没有结束,因为内存还没有清理。当发送完成的时候,网卡设备会触发一个硬中断来释放内存,主要是释放 sk_buff 内存和清理 RingBuffer 内存。

  9. 最后,当收到这个 TCP 报文的 ACK 应答时,传输层就会释放原始的 sk_buff 。

发送网络数据的时候,涉及几次内存拷贝操作?

  1. 调用发送数据的系统调用的时候,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区。
  2. 在使用 TCP 传输协议的情况下,从传输层进入网络层的时候,每一个 sk_buff 都会被克隆一个新的副本出来。副本 sk_buff 会被送往网络层,等它发送完的时候就会释放掉,然后原始的 sk_buff 还保留在传输层,目的是为了实现 TCP 的可靠传输,等收到这个数据包的 ACK 时,才会释放原始的 sk_buff 。
  3. 当 IP 层发现 sk_buff 大于 MTU 时才需要进行。会再申请额外的 sk_buff,并将原来的 sk_buff 拷贝为多个小的 sk_buff。

参考资料

图解网络介绍 | 小林coding (xiaolincoding.com)

DNS解析原理:递归 VS 迭代 - 简书 (jianshu.com)