TCP Socket 与 UDP Socket

前言

Socket 是网络编程的基础,面试中 TCP 和 UDP Socket 的调用流程、内核行为、函数区别、缓冲区机制属于高频考点。很多同学容易混淆 accept 返回的 fd 作用、三次握手发生时机、UDP sendto/recvfrom 时序、内核缓冲区行为等细节。本文结合调用流程图,逐步骤拆解 TCP、UDP Socket 流程,梳理易错点和对比总结。

一、TCP Socket(面向连接、可靠字节流)

TCP 是面向连接的通信模型,通信前需要完成三次握手建立连接,通信结束通过四次挥手断开连接,数据是流式、无边界的,内核提供确认、重传、排序、流量控制机制。

1. 服务端调用流程

复制代码
socket() → bind() → listen() → accept() 阻塞等待连接 → read()/write() 收发数据 → close()
  1. socket():创建套接字文件描述符。此时仅仅创建一个通信"外壳",没有绑定地址,也不具备监听能力。

  2. bind():绑定固定 IP + 端口。客户端依靠这个地址找到服务端,是服务端标配;客户端一般不需要手动 bind。

  3. listen() :将 socket 转为监听 socket,内核创建连接等待队列,开始准备接收客户端连接请求。

  4. accept() :阻塞等待客户端连接。三次握手由内核协议栈自动完成 ,不是 accept 函数本身组装报文。
    握手成功之后,accept 返回一个全新的通信 fd :

    • 监听 fd(listen fd):只负责接收新连接请求,不传输业务数据,持续监听新客户端;
    • accept 返回的通信 fd:专门和这一个客户端做业务数据读写。

    ⚠️ 重点:accept 返回的 fd 只留在服务端本地使用,不会发送给客户端!

  5. read() / write():使用新的通信 fd 双向读写业务数据,TCP 是全双工,两端随时可以收发。

  6. close() :关闭 socket,触发 TCP 四次挥手断开连接;对端调用 read 会返回 0,代表读到 EOF。

2. 客户端调用流程

复制代码
socket() → connect() → write()/read() → close()
  1. socket():创建客户端套接字 fd。客户端全程只有这一个 fd,后续 connect、读写都复用它。
  2. connect():主动发起连接,发送 SYN 报文,触发三次握手第一步;内核完成剩余握手流程,连接就绪后 connect 返回成功。
  3. write() / read():和服务端双向传输流数据。
  4. close():关闭连接,向对端发送 EOF 标记。

3. 通俗类比

监听 fd = 公司前台,一直留守接待访客;

每一个客户端连接进来,前台(listen fd)分配一个业务员(accept 返回的通信 fd)一对一对接;

业务员属于服务端内部,不会交给客户端;客户端手里只有自己自带的通讯工具(客户端 socket fd)。

4. TCP 核心底层标识:四元组

一条 TCP 连接由四元组唯一标识:

客户端IP:客户端端口 <--> 服务端IP:服务端端口

内核使用通信 fd 和这条四元组关联,两端内核协议栈共同维护同一条连接状态。

5. TCP 关键考点汇总

  1. listen fd 和 accept 返回的通信 fd 不是同一个,职责分离;
  2. 三次握手发生在 connect 发起之后、accept 返回之前,在内核完成;
  3. connect 发 SYN(第一次握手),握手全部完成后 accept 才解除阻塞返回;
  4. EOF:一方 close,内核向对端缓冲区放置 EOF,对端 read 返回值为 0;
  5. TCP 是字节流,存在粘包问题。

二、UDP Socket(无连接、面向数据报)

UDP 是无连接、面向数据报的协议,没有握手、挥手流程,不维护连接状态,不保证可靠交付,每个报文都携带完整目标地址,典型应用:DNS、TFTP。

UDP 没有 listen、accept、connect(普通数据报场景),收发函数固定为 sendto / recvfrom。

1. UDP 服务端流程

复制代码
socket(SOCK_DGRAM) → bind() → recvfrom() → sendto()
  1. socket() :创建 UDP 套接字,类型为 SOCK_DGRAM;
  2. bind():绑定固定 IP+端口,客户端知道发包目标地址,UDP 服务端一般必须 bind;
  3. recvfrom():阻塞等待接收报文,同时通过出参拿到发送方的 IP+端口地址;
  4. sendto():回复数据,必须指定目标地址(就是 recvfrom 拿到的客户端地址)。

关键点:同一个 UDP socket,可以同时接收成千上万个不同客户端的报文,不需要新建 fd。

2. UDP 客户端流程

复制代码
socket(SOCK_DGRAM) → [可选 bind()] → sendto() → recvfrom()
  1. socket():创建 UDP 套接字;
  2. bind():可选!绝大多数 UDP 客户端不手动 bind;调用 sendto 时内核自动分配随机端口;只有需要固定客户端端口时才手动 bind;
  3. sendto():发送数据,参数携带服务端 IP+端口;
  4. recvfrom():接收服务端应答,获取对端地址。

3. UDP 收发时序(高频坑点)

UDP 不存在连接,sendto 和 recvfrom 没有内核强制的先后顺序,分为两种业务场景:

  1. 一问一答(主流,DNS) :客户端 sendto → 服务端 recvfrom(拿到客户端地址)→ 服务端 sendto 应答 → 客户端 recvfrom。
    为什么服务端一般先 recvfrom?服务端启动时不知道客户端地址,只能收到报文之后才能拿到地址回包,属于业务逻辑约束,不是内核强制。
  2. 主动推送场景:如果服务端提前写死客户端 IP+端口,可以直接 sendto 推送消息,客户端提前阻塞在 recvfrom 等待接收。

4. UDP 内核接收缓冲区机制(面试重点)

每个 socket 在内核都有独立的接收缓冲区,用来存放已经到达、用户进程还没有 recvfrom 取走的数据报。

类比:内核接收缓冲区 = 快递柜;recvfrom = 用户取快递;sendto = 快递员投递。

✅ 正确结论:

客户端先 sendto 发包,服务端进程还没调用 recvfrom,报文不会直接丢失;报文先存入内核接收缓冲区排队,后续调用 recvfrom 依旧可以读到。缓冲区是按数据报存储,不会粘包,一个 recvfrom 读取一个完整数据报。

❌ 错误认知:没有 recvfrom 等待,sendto 的数据包直接丢了。

缓冲区满时行为:

缓冲区存在上限(可通过 setsockopt 设置 SO_RCVBUF,受内核全局上限限制)。缓冲区装满之后,后续新来的 UDP 报文直接丢弃,无任何通知;缓冲区中已经存入的包保留,等 recvfrom 取走腾出空间后,才能接收新包。

5. UDP 和 TCP 缓冲区满行为对比

  • UDP:接收缓冲区满 → 直接丢包,发送方无感知;
  • TCP:接收缓冲区满 → 通过滑动窗口通知对端停止发送,发送方暂缓发送数据,不会丢包。

6. UDP 核心考点汇总

  1. 无连接、无握手挥手,不维护连接状态;
  2. 每个数据报独立,自带目标地址;
  3. recvfrom 两个作用:读取数据 + 获取发送端地址,用于 sendto 回包;
  4. 不提供确认、重传、排序,报文可能丢失、乱序;
  5. UDP 没有粘包问题,数据有边界。

三、TCP Socket VS UDP Socket 总对比表

对比项 TCP UDP
连接特性 面向连接,三次握手建立,四次挥手断开 无连接,每个报文独立投递
套接字函数 socket、bind、listen、accept、read/write、close socket、bind(服务端常用)、sendto、recvfrom
fd模型 服务端:监听fd + 每个客户端独立通信fd;客户端单fd 服务端单socket,接收所有客户端报文
数据形式 字节流,存在粘包 数据报,自带边界,无粘包
可靠性 可靠:确认、重传、排序、流量控制 不可靠,可能丢包、乱序
满缓冲区行为 通知对方停发,不丢包 直接丢弃新来报文,无通知
典型场景 HTTP、MySQL、文件传输 DNS、直播、音视频、TFTP

四、一句话时序总结

TCP:服务端 socket-bind-listen-阻塞accept;客户端 socket-connect;握手成功后双向读写,最后 close 挥手断开。

UDP:无连接通道,数据包独立寻址;一问一答模式下客户端先发 sendto,服务端 recvfrom 获取地址后 sendto 应答;报文先存放内核缓冲区,缓冲区溢出丢包。

五、常见面试追问清单

  1. accept 阻塞的时候,三次握手完成了吗? 答:完成了,内核完成握手,连接放入就绪队列之后 accept 才返回。
  2. 为什么 UDP 客户端可以不 bind? 答:sendto 发送时内核自动分配临时随机端口。
  3. read 返回 0 代表什么? 答:读到 EOF,对端调用 close 关闭连接。
  4. UDP sendto 对方进程没有 recvfrom,包一定丢吗? 答:不一定,报文存入内核接收缓冲区,缓冲区没满就可以后续读取;缓冲区满才丢包。
  5. TCP 监听 fd 能不能用来收发业务数据? 答:不能,listen fd 职责只有接收新连接请求。
相关推荐
|_⊙1 天前
网络层协议 IP
服务器·网络·tcp/ip
沫璃染墨1 天前
从零入门计算机网络系列(十五):TCP可靠传输机制详解——从序号与确认应答到乱序处理
服务器·网络·c++·tcp/ip·tcp
流浪0011 天前
计算机网络篇20:网络层——IP 协议的定位与 IPv4 报头字段,从 20 字节首部到 TTL 防环
tcp/ip·计算机网络·ttl·网络层协议·ip报头
zhengfei6111 天前
CVE-2026-98432:Linux 内核 TCP 释放后重用漏洞,CVSS 9.8 但补丁覆盖极不均衡
linux·运维·tcp/ip
沫璃染墨2 天前
从零入门计算机网络系列(十四):TCP报文与字节流本质——从数据封装到报文边界
linux·服务器·网络·网络协议·tcp/ip·tcp
流浪0012 天前
计算机网络篇17:传输层——TCP 超时重传、三次握手与四次挥手,TIME_WAIT 为什么要等 2MSL
网络协议·tcp/ip·计算机网络·三次握手·四次挥手·超时重传
流浪0013 天前
计算机网络篇16:传输层——TCP 报头属性字段:16 位窗口大小撑起流量控制,六个标志位标识报文类型
网络协议·tcp/ip·计算机网络·urg紧急指针·tcp报头
从琳开始9893 天前
网络原理入门(二)------OSI七层模型?TCP/IP五层(四层)模型原来是这样的
java·网络·tcp/ip·java-ee·php
honsor4 天前
SNMP 读取以太网温湿度传感器数据,监控平台对接实操
物联网·网络协议·tcp/ip