网络协议栈收发流程:一次数据怎么发出去又怎么收回来
围绕浏览器请求、TCP/IP/MAC 封装、网卡发送、交换机路由器转发和 Linux 收发流程章节,串起网络包从应用到网卡再回到应用的全过程。
相关工具
把前面的知识串成一条路
前面我们分别讲过 DNS、HTTP、TCP、IP、ARP、MAC、MTU。单独看时,每个概念都能说几句,但真实网络请求不是一层一层孤立发生的。浏览器拿到服务器 IP 后,要把 HTTP 请求交给操作系统协议栈;协议栈再把数据封装成网络包,交给网卡发出去。
资料 第 84 页讲得很清楚:通过 DNS 获取到 IP 后,HTTP 的传输工作会交给操作系统中的协议栈。协议栈内部有上下关系,上面的部分向下面委托工作,下面收到委托后继续执行。
这篇就按一发一收来讲。发送时,数据从应用程序进入内核,一路添加 TCP/IP/MAC 相关头部,最后变成电信号;接收时,电信号进入网卡和内核,一层层检查、拆头、找到 Socket,最后交回应用。
应用程序先把活交给 Socket
浏览器或其他应用程序不会直接控制网卡。它会调用 Socket 接口,把要发送的数据交给操作系统。资料 第 121 页写到,应用程序调用 Socket 发送数据包接口时,会从用户态陷入内核态中的 Socket 层,Socket 层把应用层数据拷贝到 Socket 发送缓冲区。
这个动作很关键。应用层看见的是 HTTP 请求、数据库协议、消息体;内核协议栈看见的是一段需要发送的字节数据,以及这个 Socket 对应的连接信息。后面的 TCP、IP、网卡驱动,都是在内核里接着处理。
所以排查网络问题时,应用日志只能说明“我发起了请求”或“我没有收到响应”。真正有没有进入内核发送队列、有没有被 TCP 分段、有没有查路由、有没有发到网卡,需要看更底层的状态和抓包。
传输层:TCP 或 UDP 接手
协议栈从 Socket 发送缓冲区取出数据后,会按照 TCP/IP 协议栈从上到下处理。如果使用 TCP,传输层会加 TCP 头;如果使用 UDP,就加 UDP 头。资料 第 84 页也提到,协议栈上半部分有 TCP 和 UDP,它们接受应用层委托执行收发数据的操作。
TCP 头里有源端口、目标端口、序列号、确认号、状态位、窗口大小等字段。资料 第 85 页解释过,端口号用于知道数据该交给哪个应用,序列号处理乱序,确认号处理是否收到,窗口大小用于流量控制。
如果 HTTP 请求比较长,超过 MSS,TCP 会先把数据拆成多块。资料 第 87 页说,数据会以 MSS 长度为单位拆分,每块数据加上 TCP 头,再交给 IP 模块。这一步是传输层主动控制大小,尽量避免后面 IP 层被动分片。

应用调用 Socket 后,数据依次经过传输层、网络层、ARP、网络接口层、网卡驱动和物理网卡,最终转换为电信号发送。
网络层:IP 负责远程定位和路由
TCP 报文组装好后,会交给 IP 模块。资料 第 88 页说,TCP 模块在连接、收发、断开各阶段都需要委托 IP 模块,把数据封装成网络包发送给通信对象。
IP 头里要填源 IP 和目标 IP。目标 IP 通常来自 DNS 解析结果;源 IP 则不是随便填。资料 第 90 页提到,如果客户端有多块网卡,就需要根据路由表规则判断应该使用哪块网卡发送包,从而选择对应的源地址。
IP 层还要查询路由表,决定下一跳。如果没有更具体的路由匹配,就会走默认网关。与此同时,网络层还会考虑 MTU,必要时按 MTU 大小分片。到这里,包已经知道“最终要去哪”和“下一步先交给谁”。
ARP 和 MAC:找到当前链路的下一站
IP 解决远程定位,MAC 解决当前链路的交付。资料 第 91-92 页讲到,生成 IP 头后,还需要在 IP 头前面加 MAC 头。MAC 头包含接收方和发送方 MAC 地址等信息。
发送方 MAC 比较好确定,网卡生产时已经写入。接收方 MAC 要先看下一跳是谁:如果目标在同网段,下一跳可能就是目标主机;如果跨网段,下一跳通常是默认网关。确定下一跳 IP 后,再通过 ARP 查询对应 MAC。
ARP 缓存命中时,可以直接使用缓存里的 MAC;缓存没有记录时,就发送 ARP 广播查询。拿到 MAC 后,网络接口层才能把 IP 包封装进以太网帧,准备交给网卡。
网卡驱动和物理网卡:数字变成信号
到了网卡这一步,包还只是内存里的二进制数字。资料 第 94 页说,网络包存放在内存中,不能直接发送给对方,需要把数字信息转换成电信号,才能在网线上传输。执行这个操作的是网卡,控制网卡还要靠网卡驱动程序。
网卡驱动从 IP 模块获取包后,会复制到网卡缓存区,在开头加报头和起始帧分界符,在末尾加 FCS 帧校验序列。FCS 用于检查包在传输过程中是否损坏。最后网卡把包转为电信号,通过网线或其他介质发出去。
这也是为什么“应用已经 write 成功”不等于对方已经收到。write 成功更多说明数据进入了内核或缓冲区;后面还要经历协议栈处理、驱动队列、网卡发送、链路传输和对端接收。
交换机和路由器在路上各做各的事
数据离开本机后,局域网里常先经过交换机。资料 第 95-96 页说,交换机工作在 MAC 层,是二层网络设备。它把电信号转成数字信号,检查 FCS,没有问题就放入缓冲区,再查询 MAC 地址表,把帧发到对应端口。
如果 MAC 地址表里没有目标 MAC,交换机会把包转发到除源端口以外的所有端口。等目标设备返回响应后,交换机就能学习到它的 MAC 地址和端口,下次再定向转发。
路由器则是三层设备。资料 第 97 页讲到,路由器端口有 MAC 地址和 IP 地址。它先接收发给自己 MAC 的以太网包,去掉旧 MAC 头,再根据 IP 头查路由表确定转发目标,最后在输出端口重新封装新的 MAC 头发出去。
接收流程从网卡开始
接收方向刚好反过来。资料 第 121 页先从网卡说起:网卡是专门负责接收和发送网络包的硬件。网卡收到网络包后,会通过 DMA 技术把网络包放入 Ring Buffer,也就是环形缓冲区。
如果每来一个包都触发一次完整中断处理,CPU 开销会很大。已有内容提到,Linux 2.6 引入 NAPI 机制,用中断和轮询混合方式接收网络包。它先用中断唤醒接收服务程序,然后暂时屏蔽中断,通过轮询批量处理数据,直到没有新包再恢复中断。
这段听起来偏底层,但和高并发网络性能很有关。流量很大时,减少频繁中断能明显降低 CPU 压力。网卡、驱动、内核缓冲区不是背景板,它们决定了包能不能稳定进入协议栈。

网卡收到电信号后,经 DMA、Ring Buffer、NAPI、sk_buff 和协议栈逐层处理,最终找到 Socket 并交给应用读取。
逐层拆头,直到找到 Socket
软中断会把数据从 Ring Buffer 拷贝到内核的 `sk_buff` 缓冲区,让它作为一个网络包在内核协议栈里传递。接下来先进入网络接口层,检查报文是否合法。如果不合法就丢弃;合法就识别上层协议类型,比如 IPv4 或 IPv6,并去掉帧头帧尾。
到了网络层,内核取出 IP 包,判断下一步是交给本机上层处理,还是转发出去。如果确认这个包是发给本机的,就看 IP 头里上一层协议类型是 TCP 还是 UDP,然后去掉 IP 头,交给传输层。
传输层再根据四元组找到对应 Socket:源 IP、源端口、目标 IP、目标端口。找到 Socket 后,把数据拷贝到 Socket 接收缓冲区。最后应用程序调用 `read`、`recv` 这类接口,把数据从内核接收缓冲区读到应用层。
每一层只处理自己的头
协议栈最值得记的一条原则是:每一层只处理自己该看的信息。网络接口层看 MAC 头和帧校验,IP 层看源/目标 IP 和协议号,TCP/UDP 看端口和传输层状态,应用层才关心 HTTP 报文或业务数据。
发送时是一层层加头:应用数据外面加 TCP/UDP 头,再加 IP 头,再加 MAC 头和帧相关信息。接收时则一层层拆头:网卡和链路层先检查帧,IP 层取出 IP 包,传输层找到 Socket,最后应用读到去掉协议头后的业务数据。
理解这个顺序后,抓包也会更清楚。你看到的一个包不是单层结构,而是层层套起来的结果。哪个字段异常,就大致对应哪一层的问题。
排查时按路径切开
如果应用调用失败,先问数据有没有进入 Socket。应用超时可能是代码没发出去,也可能是发出去了但收不到响应。继续往下看 TCP 连接状态、发送缓冲区、重传、MSS、窗口大小,能判断问题是不是在传输层。
如果 TCP 状态正常但包出不去,就看 IP 路由、默认网关、源地址选择、ARP 缓存、MTU。多网卡机器尤其要注意源 IP 选择,路由表决定从哪块网卡出。
如果接收端压力大,就看网卡队列、Ring Buffer、软中断、丢包统计、Socket 接收缓冲区是否堆积。很多线上“应用处理慢”其实不是应用一开始慢,而是包在内核或网卡层已经排队、丢弃或被限速。
面试里怎么讲
如果被问一次请求从应用到网卡怎么发,可以这样答:应用调用 Socket,数据进入 Socket 发送缓冲区;传输层添加 TCP/UDP 头;网络层添加 IP 头并查路由、处理 MTU;通过 ARP 获取下一跳 MAC;网络接口层封装 MAC 头和帧信息;网卡驱动通过 DMA 把数据交给网卡,物理网卡转换成电信号发送。
如果被问接收流程,就说网卡收到信号后通过 DMA 写入 Ring Buffer,内核通过中断和 NAPI 轮询批量处理包,放入 `sk_buff`,再经过网络接口层、IP 层、TCP/UDP 层逐层拆头,根据四元组找到 Socket,把数据放入接收缓冲区,应用最后通过 `read` 或 `recv` 读取。
如果被问交换机和路由器在其中的角色,可以补一句:交换机按 MAC 地址表在二层转发帧;路由器按 IP 路由表在三层选择下一跳,并在每一跳重新封装 MAC 头。
常见问题
应用调用 write 成功,是否代表数据已经到达对端?
不是。它通常只说明数据进入了本机内核或发送缓冲区,后面还要经过协议栈、驱动、网卡、链路和对端协议栈处理。
接收包时为什么要用 Ring Buffer 和 NAPI?
Ring Buffer 用来承接网卡 DMA 写入的数据;NAPI 用中断加轮询的方式批量处理网络包,减少高流量下频繁中断带来的 CPU 开销。
传输层怎么找到该交给哪个应用?
TCP/UDP 会根据四元组,即源 IP、源端口、目标 IP、目标端口,找到对应的 Socket,再把数据放入该 Socket 的接收缓冲区。