计算机网络之TCP/UDP篇(上)

大家好,这里是编程Cookbook。本文详细介绍计算机网络中的TCP/UDP协议相关的内容,包括单不限于基础概念、连接的建立与断开、TCP可靠传输的实现等。



TCP/UDP 基础概念

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

什么是 TCP 连接?

TCP(Transmission Control Protocol,传输控制协议)是面向连接的、可靠的、基于流的传输层协议。TCP 报文段如下所示:

TCP 报文段

TCP 报文段的首部通常为 20 字节(无选项时),最大可扩展至 60 字节。具体结构如下:

字段长度(字节)说明
源端口(Source Port)2发送方的端口号(如 54321
目的端口(Destination Port)2接收方的端口号(如 80 表示 HTTP)
序列号(Sequence Number)4本报文段数据的第一个字节的编号(用于数据排序)
确认号(Acknowledgment Number)4期望收到的下一个字节的序号(用于确认接收)
数据偏移(Data Offset)4 bitsTCP 首部长度(以 4 字节为单位,最小 5 → 20 字节)
保留(Reserved)6 bits未使用,必须置 0
控制标志(Flags)6 bits用于连接控制(如 SYN, ACK, FIN
窗口大小(Window Size)2接收方的可用缓冲区大小(流量控制)
校验和(Checksum)2校验首部 + 数据的完整性(含伪首部,类似 UDP)
紧急指针(Urgent Pointer)2仅当 URG=1 时有效,指向紧急数据的末尾
选项(Options)可变(0-40字节)可选字段(如 MSS、窗口缩放因子、时间戳等)
填充(Padding)可变确保选项字段对齐 4 字节边界
数据(Data)可变上层应用数据(如 HTTP 请求、文件内容等)

TCP 连接的特点:

  • 面向连接:通信前,必须通过“三次握手”建立连接,确保双方准备好数据传输。
  • 可靠传输:TCP 采用 确认机制(ACK)、超时重传、流量控制、拥塞控制,确保数据不丢失、不重复、按序到达。
  • 基于流:TCP 以字节流(Byte Stream)形式传输数据,没有明确的消息边界,需要应用层自行处理分包和粘包。
  • 全双工:双方可以同时发送和接收数据。
  • 有序传输:TCP 通过 序列号(Sequence Number) 确保数据按正确顺序到达。

TCP 连接建立过程(简要)

  1. 三次握手:保证双方通信能力,并初始化必要的参数(如序列号)。
  2. 数据传输:通过滑动窗口、超时重传、ACK 确保可靠传输。
  3. 四次挥手:确保数据完整性后关闭连接。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

什么是 UDP 连接?

UDP(User Datagram Protocol,用户数据报协议)是无连接的、不可靠的、基于报文的传输层协议。 其数据报格式包含 首部(Header)数据部分(Data)。此外,在计算校验和时还会用到 伪首部(Pseudo Header)。UDP 数据报如下所示:

UDP 数据报由 8字节首部 + 数据部分 组成,具体结构如下:

UDP数据报格式

伪首部 仅用于 校验和计算不会真正传输。它的作用是确保数据报被正确路由到目标 IP 和端口,防止 IP 欺骗或错误转发:

UDP 数据报的首部和伪首部

UDP 的特点:

  • 无连接:发送数据前不建立连接,接收方随时可以处理数据。
  • 不可靠:不保证数据到达,不保证数据顺序,也不提供重传机制。
  • 基于报文:数据以独立的 UDP 报文(Datagram) 发送,每个报文是完整的,没有流的概念。
  • 低开销:UDP 头部仅 8 字节,较 TCP(20 字节)开销小,适用于低延迟场景。
  • 适合实时传输:UDP 适用于 音视频、在线游戏、DNS 查询 等场景,即使部分数据丢失,也不会影响整体体验。

UDP 传输过程:

  1. 发送端直接将数据封装成 UDP 数据报,通过 IP 层发送给目标主机。
  2. 接收端从 UDP 缓冲区获取数据报,并交给应用层处理(但可能丢包、乱序)。

TCP 和 UDP 对比

对比项TCP(传输控制协议)UDP(用户数据报协议)
是否连接面向连接,需建立连接(3 次握手)无连接,直接发送数据
可靠性可靠传输,保证数据不丢失、不重复、按序到达不可靠传输,可能丢包、乱序、重复
数据边界面向字节流,无明确的消息边界,可能粘包面向数据报,有独立的数据包边界
传输效率开销较大,需要维护连接和状态轻量级,无需连接管理,低延迟
流量控制通过滑动窗口调整传输速率无流量控制,可能导致接收方过载
拥塞控制通过 AIMD 算法防止网络拥塞无拥塞控制,可能造成网络拥塞
应用场景需要高可靠性,如 HTTP、FTP、数据库需要低延迟,如视频流、DNS、VoIP

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!


TCP 是用来解决什么问题的?

TCP 主要用于解决以下问题:

  1. 可靠传输:确保数据能够按序、完整地传输,避免数据丢失、重复或乱序。
  2. 流量控制:通过滑动窗口机制,控制发送方的发送速率,避免接收方缓冲区溢出。
  3. 拥塞控制:通过拥塞窗口和算法(如慢启动、拥塞避免),避免网络拥塞。
  4. 连接管理:通过三次握手和四次挥手,确保连接的建立和释放是可靠的。

典型应用场景

  • 文件传输(如 FTP)
  • 网页浏览(如 HTTP/HTTPS)
  • 电子邮件(如 SMTP)
  • 数据库访问

UDP 是用来解决什么问题的?

UDP 主要用于解决以下问题:

  1. 低延迟传输:无需建立连接,直接发送数据,适合对实时性要求高的场景。
  2. 简单高效:头部开销小,传输效率高,适合轻量级通信。
  3. 广播和多播:支持一对多通信,适合广播和多播场景。

典型应用场景

  • 实时音视频传输(如 VoIP、视频会议)
  • 在线游戏
  • DNS 查询
  • 广播和多播应用

TCP 和 UDP 分别对应的常见应用层协议有哪些?

协议类型常见应用层协议
TCPHTTP/HTTPS(网页浏览)、FTP(文件传输)、SMTP(电子邮件)、SSH(远程登录)、Telnet
UDPDNS(域名解析)、DHCP(动态主机配置)、SNMP(网络管理)、TFTP(简单文件传输)

为什么要 TCP,IP 层实现控制不行吗?

首先需要明白TCP,UDP 和 IP 协议之前的区别:

  • TCP 适合可靠传输,提供流量控制、拥塞控制,保证数据有序。
  • UDP 适合实时传输,低延迟、高吞吐,但可能丢包、乱序。
  • IP 层只是尽力传输,TCP 是在其基础上提供可靠性。

IP 层(互联网协议)只负责无连接、尽力而为(Best-effort)的数据传输,但它本身存在以下问题:

  1. 不可靠:IP 数据报可能丢失、乱序、重复,应用层需要额外处理。
  2. 无流量控制:IP 层不会限制发送速率,可能导致接收方过载。
  3. 无拥塞控制:IP 层不会检测网络拥塞,可能导致全网性能下降。

TCP 之所以存在,是为了弥补 IP 层的不足,提供可靠、稳定的传输。
如果没有 TCP,应用层需要自己处理丢包、重传、乱序等复杂问题,大大增加了开发难度。


关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

TCP 连接建立与断开

TCP 三次握手(Three-Way Handshake)

TCP 是面向连接的协议,在数据传输前,通信双方需要通过 三次握手(Three-Way Handshake) 建立连接,确保双方都具备发送和接收数据的能力。

用“三次握手”建立 TCP 连接

三次握手的过程

假设 客户端(Client) 要与 服务器(Server) 建立 TCP 连接,三次握手的步骤如下:

  1. SYN = 1,seq = x。
  2. SYN = 1,ACK = 1,seq = y,ack = x + 1。
  3. ACK = 1,seq = x + 1,ack = y + 1。

1️⃣ 第一次握手(Client → Server,发送 SYN)

  • 客户端发送一个 SYN(同步) 报文,请求建立连接,并携带一个 初始序列号 ISN(Initial Sequence Number)
  • SYN=1, seq=x
  • 此时,客户端进入 SYN-SENT 状态。

2️⃣ 第二次握手(Server → Client,发送 SYN-ACK)

  • 服务器收到 SYN 请求后,回应一个 SYN-ACK 报文,表示同意连接,并指定自己的初始序列号 ISN(y)。
  • SYN=1, ACK=1, seq=y, ack=x+1
  • 此时,服务器进入 SYN-RECEIVED 状态。

3️⃣ 第三次握手(Client → Server,发送 ACK)

  • 客户端收到 SYN-ACK 后,回复一个 ACK 报文,确认连接建立,并表明自己可以发送数据了。
  • ACK=1, seq=x+1, ack=y+1
  • 客户端进入 ESTABLISHED(已建立连接) 状态。
  • 服务器收到 ACK 后,也进入 ESTABLISHED 状态,连接正式建立。

📌 完成三次握手后,双方可以正式开始数据传输。


TCP 四次挥手(Four-Way Handshake)

TCP 连接关闭时,需要四次挥手(Four-Way Handshake) 来保证数据完全传输,并确保双方都同意断开连接。

用“四次挥手”释放 TCP 连接

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

四次挥手的过程

假设 客户端(Client) 先发起关闭连接的请求,四次挥手的步骤如下:

  1. FIN = 1,seq = u。
  2. ACK = 1,seq = v,ack = u + 1。
  3. FIN = 1,ACK = 1,seq = w,ack = u + 1。
  4. ACK = 1,seq = u + 1,ack = w + 1。

1️⃣ 第一次挥手(Client → Server,发送 FIN)

  • 客户端发送 FIN(Finish) 报文,表示不再发送数据,但仍可以接收数据。
  • FIN=1, seq=u
  • 客户端进入 FIN-WAIT-1 状态。

2️⃣ 第二次挥手(Server → Client,发送 ACK)

  • 服务器收到 FIN 后,发送一个 ACK 确认。
  • ACK=1, seq=v, ack=u+1
  • 服务器进入 CLOSE-WAIT 状态,客户端进入 FIN-WAIT-2 状态。
  • 服务器仍然可能需要处理未完成的任务,因此连接暂时不会关闭

3️⃣ 第三次挥手(Server → Client,发送 FIN)

  • 服务器处理完数据后,向客户端发送 FIN 报文,表示自己也不再发送数据
  • FIN=1, seq=w, ack=u+1
  • 服务器进入 LAST-ACK 状态。

4️⃣ 第四次挥手(Client → Server,发送 ACK)

  • 客户端收到 FIN 后,回复一个 ACK 报文,表示确认断开
  • ACK=1, seq=u+1, ack=w+1
  • 客户端进入 TIME-WAIT 状态,等待 2MSL(最大报文生存时间) 后,才真正关闭。
  • 服务器收到 ACK 后,立即进入 CLOSED(关闭) 状态,连接完全关闭。

📌 为什么服务器的 ACK 和 FIN 不能合并?

  • 服务器在接收到 FIN 后,可能仍然有未发送的数据,所以需要 先发送 ACK,处理完数据后,再发送 FIN

TCP 初始序列号 ISN

ISN 的定义

  • ISN(Initial Sequence Number) 是 TCP 连接建立时,每个通信方选定的起始序列号,用于数据传输中的字节编号

    如在 TCP 三次握手 过程中:

    • 客户端 发送 SYN 请求,并携带自己的 ISN(x)
    • 服务器 回复 SYN-ACK,并携带自己的 ISN(y)

ISN 的作用

  1. 解决 TCP 可靠传输中的数据编号问题TCP 以字节为单位 进行数据传输,每个字节都需要一个序列号。ISN 作为初始编号,确保每个 TCP 段都有唯一的序列号,方便数据接收端按序重组数据
  2. 防止 TCP 连接中的数据包混淆:TCP 连接断开后,可能仍有旧的 TCP 报文在网络中滞留。TCP 采用动态 ISN 生成,让每次连接的 ISN 随机变化,防止旧连接数据包干扰新连接。

ISN 的取值

  • TCP 初始序列号 ISN(Initial Sequence Number) 并不是固定的,而是动态生成的。

ISN 的生成方式

  • 传统方法:每次连接 ISN 可能固定为 0,但这样容易被攻击者预测,造成数据包劫持
  • 现代方法:ISN 通常使用时间戳加随机数,防止连接劫持:
    ISN = 当前时间戳 + 随机增量
    
  • 现代操作系统(如 Linux、Windows)采用基于时间的 ISN 生成算法,让 ISN 随着时间增加,确保安全性

TCP 三次握手时,发送 SYN 之后就宕机了会怎么样?

假设客户端发送 SYN 之后,宕机了:

  • 服务器收到了 SYN,并回复 SYN-ACK,但客户端已经宕机,无法回复 ACK
  • 服务器会一直等待 ACK,但由于没有响应,会重传 SYN-ACK 若干次(通常是 3-6 次)。
  • 最后,服务器会超时,进入 CLOSED 状态,释放资源。

注意:

  • 每次 SYN-ACK 重传的间隔不是固定的,而是 指数退避(Exponential Backoff) 的策略(1s → 2s → 4s → 8s → 16s → 32s),每次重传的间隔会逐渐增加。

📌 影响:

  • 服务器的资源(如连接队列)会被占用,可能导致 SYN Flood 攻击。

SYN Flood 攻击

SYN Flood(SYN 泛洪攻击) 是一种 DoS(拒绝服务攻击),利用 TCP 三次握手的机制,导致服务器资源耗尽。

攻击原理
  • 攻击者伪造大量 SYN 请求,但不发送 ACK,导致服务器一直等待(SYN-RECEIVED 状态),耗尽服务器的资源(如半连接队列),无法处理新的请求,造成拒绝服务(DoS)。
防御措施

1. 增加半连接队列大小

  • 原理:通过增大服务器的半连接队列(SYN 队列)容量,可以暂时缓解大量 SYN 报文导致的队列溢出问题。
  • 实现
    • 调整操作系统的 TCP 参数,例如 net.ipv4.tcp_max_syn_backlog(Linux 系统)。
    • 增大服务器的内存资源,以支持更大的队列。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!


2. 减少 SYN+ACK 重试次数

  • 原理:当服务器发送 SYN+ACK 后未收到客户端的 ACK 时,会进行多次重试。减少重试次数可以更快地释放半连接资源。
  • 实现
    • 调整操作系统的 TCP 参数,例如 net.ipv4.tcp_synack_retries(默认是5,Linux 系统)。

3. 启用 SYN Cookie 机制

  • 原理:SYN Cookie 是一种防御 SYN Flood 攻击的技术。服务器在收到 SYN 报文后,不立即分配资源,而是通过加密算法生成一个 Cookie 值作为初始序列号。只有收到合法的 ACK 报文后,服务器才会分配资源
  • 实现
    • 在 Linux 系统中,启用 SYN Cookie:sysctl -w net.ipv4.tcp_syncookies=1
    • 思想是:服务器不再维护 SYN 半连接队列,而是基于客户端的 SYN 报文计算出一个“Cookie”(即特定格式的 ISN,初始序列号),并在 SYN+ACK 报文中返回给客户端。客户端在第三次握手(ACK 报文)中返回这个 Cookie,服务器通过计算验证它的正确性,再正式建立连接。
  • 优点
    • 无需维护半连接队列,节省资源。
    • 有效抵御伪造源 IP 的 SYN Flood 攻击。

半连接状态分配的典型资源有哪些?

  • 内存资源:服务器需在内存中开辟空间记录半连接相关信息,如客户端 IP 地址、端口号、连接请求时间、序列号等。
  • 连接队列资源:服务器设置了专门的半连接队列(SYN 队列)。

三次握手过程中可以携带数据吗?

简短回答理论上可以,但通常不携带,实际应用中只有第三次握手可能携带数据。


三次握手过程分析

在 TCP 三次握手的过程中,只有第三次握手才能发送数据:

  • 普通 TCP

    客户端 -> 服务器: SYN
    服务器 -> 客户端: SYN+ACK
    客户端 -> 服务器: ACK
    客户端 -> 服务器: 数据包
    
    • 需要 额外一次 RTT 之后才能发送数据。
  • 第三次握手携带数据

    客户端 -> 服务器: SYN
    服务器 -> 客户端: SYN+ACK
    客户端 -> 服务器: ACK + 数据包
    
    • 直接在第三次握手的 ACK 报文中携带数据,减少一次 RTT

为什么第一次、第二次握手不能携带数据?

  • TCP 规定 SYN 报文 不能携带数据,因为此时连接还未建立,无法确认对方是否能正确接收数据
  • 服务器收到 SYN 后,需要分配资源,并在 SYN + ACK 里回复自己的初始序列号,因此 不能提前接收数据

为什么第三次握手可以携带数据?

  • 连接已经建立(服务器收到 ACK 之后,连接状态变为 ESTABLISHED)。
  • 服务器已经具备接收能力,理论上可以直接处理数据。
  • 减少一次 RTT(往返时延),提高传输效率。

特殊情况:TCP Fast Open(TFO)

TCP Fast Open(TFO) 是 TCP 的一个优化,它允许 在第一步 SYN 报文中携带数据,但前提是:

  • 客户端和服务器都支持 TFO
  • 客户端之前已经和服务器通信过,并缓存了 TFO Cookie
  • 服务器只有在确认 TFO Cookie 合法后,才会处理数据
  • SYN 携带的数据在服务器接收到 SYN+ACK 之后才能被处理

TFO 的工作流程

  1. 第一次握手(SYN)

    • 客户端发送 SYN 报文,并在报文中携带一个特殊的 TFO Cookie(由服务器在之前的连接中生成)。
    • 客户端可以在 SYN 报文中携带数据(例如 HTTP 请求)。
  2. 第二次握手(SYN+ACK)

    • 服务器验证 TFO Cookie 的合法性。
    • 如果 Cookie 有效,服务器可以在 SYN+ACK 报文中携带响应数据。
  3. 第三次握手(ACK)

    • 客户端发送 ACK 报文,确认服务器的响应。
    • 连接正式建立。

除了四次挥手,还有什么方法断开连接?

  1. RST(Reset)强制断开

    • 直接发送 RST 报文,立即终止连接,不等待对方确认。
    • 适用于异常情况下的连接终止(如端口错误、程序崩溃)。
  2. 超时自动关闭

    • 如果长时间没有数据传输,连接可能被 超时机制(Keep-Alive 机制) 自动关闭。可以命令设置 TCP Keepalive 参数:

      sysctl -w net.ipv4.tcp_keepalive_time=600 # 600 秒后开始检测
      sysctl -w net.ipv4.tcp_keepalive_intvl=75 # 每次检测间隔 75 秒
      sysctl -w net.ipv4.tcp_keepalive_probes=9 # 最多检测 9 次


TCP 挥手的 TIME_WAIT 状态

简短回答:
TIME_WAIT 状态的存在是为了 确保对方正确关闭连接避免旧连接影响新连接。TCP 在主动关闭连接的一方需要等待 2MSL(最大报文生存时间的两倍) 后才能彻底释放连接资源,防止未收到的 FIN 报文或延迟的旧数据干扰新连接。

1. 什么是 TIME_WAIT 状态?
  • TIME_WAIT 是 TCP 连接断开过程中的一个状态,出现在主动关闭方(即先发送 FIN 报文的一方)。
  • 当主动关闭方发送 FIN 报文并接收到对方的 FIN + ACK 后,会进入 TIME_WAIT 状态。
  • TIME_WAIT 状态会持续 2MSL(Maximum Segment Lifetime,最大报文生存时间的两倍),之后连接才会彻底关闭。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

2. 为什么需要 TIME_WAIT 状态?
1. 确保对方正确关闭连接(可靠性保证)
  • 在四次挥手中,主动关闭方发送最后一个 ACK 报文后,需要等待一段时间,以确保对方能够收到这个 ACK
  • 如果 ACK 丢失,对方会重传 FIN 报文,主动关闭方可以重新发送 ACK

📌 示例:如果 TIME_WAIT 过早释放

1.  客户端 -> 服务器: FIN
2.  服务器 -> 客户端: ACK
3.  服务器 -> 客户端: FIN (ACK 丢失)
4.  客户端已关闭(但服务器仍在等待 ACK)
5.  服务器重发 FIN,客户端已经不在,服务器永远等待超时

🔹 TIME_WAIT 让客户端有机会重新发送 ACK,防止这种情况发生!


2. 避免旧连接数据影响新连接(延迟数据问题)
  • 在网络中,可能存在延迟的报文(即旧连接的报文)。
  • 如果立即关闭连接,这些延迟报文可能会被误认为是新连接的报文,导致数据混乱。
  • TIME_WAIT 状态确保旧连接的报文在网络中完全消失,避免干扰新连接。

📌 示例:如果 TIME_WAIT 过早释放

1.  旧连接(A)正在传输数据,但关闭过早,没有进入 TIME_WAIT。
2.  旧连接(A)上的某个数据包在网络中延迟到达。
3.  服务器开启新连接(B),端口号与旧连接相同。
4.  旧数据包误入新连接(B),导致数据混乱!

🔹 TIME_WAIT 确保旧连接的残余数据在 2MSL 期间自然消亡,避免影响新连接!


3. TIME_WAIT 的持续时间—— 2MSL
  • MSL(Maximum Segment Lifetime):报文在网络中的最大生存时间,通常为 30 秒到 2 分钟。
  • 2MSL:确保报文在网络中完全消失的时间。

在 Linux 系统中,TIME_WAIT 的默认持续时间是 60 秒(即 2MSL=60 秒)。


4. TIME_WAIT 导致的问题及其解决方案

高并发服务器(如 Nginx 代理、大量短连接应用)中,TIME_WAIT 可能导致 大量端口占用,系统资源耗尽

在高并发场景下,可以通过以下方式优化 TIME_WAIT 状态:

(1)启用 TCP 重用(SO_REUSEADDR 和 SO_REUSEPORT)
  • 允许重用处于 TIME_WAIT 状态的端口
  • 在 Linux 系统中,可以通过以下代码启用:
    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    
(2)调整 TCP 参数
  • 减少 TIME_WAIT 状态的持续时间或者 TIME_WAIT 连接的最大数量,超出后直接关闭最早的连接
    sysctl -w net.ipv4.tcp_tw_reuse=1      # 允许重用 TIME_WAIT 状态的连接
    sysctl -w net.ipv4.tcp_tw_recycle=1    # 快速回收 TIME_WAIT 状态的连接(不推荐)
    sysctl -w net.ipv4.tcp_fin_timeout=10  # 减少 FIN_WAIT_2 状态的超时时间
    
(3)使用长连接
  • 减少短连接的频繁创建和关闭,从而减少 TIME_WAIT 状态的数量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值