1. 数据发送流程
用户程序调用系统调用
- 应用程序调用
write()
或send()
,将数据从用户空间传递到内核。 - 系统调用接口(如
sys_sendto
或sys_write
)进入内核态。
内核查找套接字
- 内核根据文件描述符(如
sockfd
)查找对应的套接字对象。- 套接字对象中包含通信所需的元数据,例如目标地址、端口号、协议类型(TCP/UDP)等。
发送缓冲区处理
- 内核检查套接字的发送缓冲区:
- 空间足够: 数据被拷贝到发送缓冲区,系统调用立即返回,表示发送成功。
- 空间不足:
- 阻塞模式: 系统调用阻塞,直到缓冲区有足够空间。
- 非阻塞模式: 系统调用返回
-1
,并设置errno
为EAGAIN
或EWOULDBLOCK
。
协议栈处理(传输层和网络层)
-
传输层处理(TCP/UDP):
- TCP:
- 数据可能被分片(根据发送缓冲区大小和路径 MTU)。
- 每个分片添加 TCP 头部(源端口、目标端口、序列号、校验和等)。
- UDP:
- 数据直接打包为一个 UDP 报文,加上 UDP 头部(源端口、目标端口、长度和校验和)。
- TCP:
-
网络层处理(IP):
- 传输层数据被封装为 IP 数据包。
- 添加 IP 头部(源 IP、目标 IP、协议类型、TTL 等)。
- IP 数据包被交给链路层。
链路层和物理网络
-
链路层处理:
- 数据包被传递给网卡驱动。
- 网卡驱动将 IP 数据包封装为数据帧(以太网帧)。
- 添加链路层头部(包括目标 MAC 地址、源 MAC 地址等)。
-
物理网络发送:
- 网卡驱动通过网络硬件(如以太网、Wi-Fi)将数据帧发送到目标主机。
2. 数据接收流程
物理网络到链路层
-
接收数据帧:
- 目标主机的网卡硬件接收到数据帧,通过网卡驱动传递到内核。
-
链路层解析:
- 去除链路层头部(如以太网帧头部)。
- 将 IP 数据包传递给网络层。
网络层处理(IP 协议)
- IP 数据包解析:
- 检查 IP 头部的合法性(如校验和、TTL 等)。
- 根据目标 IP 地址,确认数据包是否属于本机。
- 将有效载荷传递给传输层。
传输层处理(TCP/UDP 协议)
-
TCP:
- 检查 TCP 头部(如序列号、校验和等)。
- 将数据按序放入接收缓冲区。
- 如果有丢包,发送重传请求。
-
UDP:
- 检查 UDP 头部合法性。
- 将数据直接交给应用程序。
接收缓冲区与用户程序
- 拷贝数据到用户空间:
- 应用程序调用
read()
或recv()
。 - 数据从内核的接收缓冲区拷贝到用户空间。
- 系统调用返回数据长度,表示接收完成。
- 应用程序调用
总结:完整流程
plaintext
复制代码
发送端: 应用程序调用 send()/write() ↓ 系统调用进入内核态 ↓ 内核查找套接字,检查发送缓冲区 ↓ 传输层(TCP/UDP):分片、添加头部 ↓ 网络层(IP):添加 IP 头部 ↓ 链路层(以太网):添加 MAC 头部 ↓ 网卡驱动发送到物理网络 物理网络 → 目标主机 接收端: 网卡驱动接收数据帧 ↓ 链路层:解析以太网头部 ↓ 网络层:解析 IP 头部 ↓ 传输层(TCP/UDP):解析传输层头部 ↓ 内核接收缓冲区 ↓ 应用程序调用 recv()/read() 获取数据
关键点补充
-
发送缓冲区和接收缓冲区:
- 发送缓冲区用于存储待发送数据;接收缓冲区用于存储收到的数据。
- TCP 协议确保缓冲区中的数据完整且有序,UDP 不提供可靠性保证。
-
系统调用返回时机:
- 对阻塞套接字,
send()
返回表示数据已被写入发送缓冲区。 - 对非阻塞套接字,若发送缓冲区满,
send()
立即返回EAGAIN
。
- 对阻塞套接字,
-
协议栈的分层处理:
- 每一层(传输层、网络层、链路层)负责处理对应的协议头部和功能,层层封装或解析数据。