目录
UDP协议
UDP协议的核心特点
-
**无连接:**通信前无需建立连接(如TCP的三次握手),直接发送数据,知道对端的IP和端口号就直接进行数据传输,不需要建立连接。
-
不可靠传输
-
不确认数据是否被接收,不重传丢失的包。如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息。
-
若应用需要可靠性,需在应用层自行实现(如添加确认机制)。
-
-
面向数据报
-
每次发送/接收都是一个完整的报文,保留数据边界(与TCP的字节流模式不同)。
-
应用层交给UDP多长的报文,UDP就原样发送,既不会拆分,也不会合并,这就叫做面向数据报。
-
比如用UDP协议传输100个字节的数据:如果发送端调用一次sendto,发送100字节,那么接收端也必须调用对应的一次recvfrom,接收100个字节;而不能循环调用10次recvfrom,每次接收10个字节。
-
-
无拥塞控制与流量控制
- 无论网络状况如何,UDP始终以恒定速率发送数据,可能加剧网络拥塞。
UDP协议格式
UDP数据报由头部(Header) 和**数据部分(Payload)**组成,结构如下:
0 15 16 31
+----------------+----------------+
| 源端口 | 目的端口 | → 各占2字节(16位)
+----------------+----------------+
| 长度 | 校验和 | → 各占2字节(16位)
+----------------+----------------+
| 数据部分(Payload) | → 可变长度(由应用层决定)
字段详解
-
源端口(Source Port)
-
2字节(16位),标识发送方的应用程序端口号(如客户端程序端口)。
-
可选 :若无需回复(如广播/多播),可设为
0
。
-
-
目的端口(Destination Port)
-
2字节(16位) ,标识接收方的应用程序端口号(如DNS服务默认端口
53
)。 -
必填:确保数据到达正确的应用程序。
-
-
长度(Length)
-
2字节(16位),表示整个UDP数据报的总长度(包含头部+数据部分)。
-
最小值 :
8
字节(仅头部,无数据)。 -
最大值 :理论为
65535
字节,但受IP协议MTU限制(通常不超过1500
字节)。
-
-
校验和(Checksum)
-
2字节(16位),用于验证数据在传输过程中是否出错。
-
计算范围:头部+数据部分+伪头部(伪头部包含IP地址等信息,增强校验可靠性)。
-
IPv4中可选 :可设为
0
表示禁用校验(但实际应用通常启用)。 -
IPv6中强制启用:必须计算校验和。
-
UDP如何决定将有效载荷交付给上层的哪一个协议?
UDP 协议通过 目标端口号(Destination Port) 决定将有效载荷(Payload)交付给上层的哪一个协议或应用程序。
-
端口号的作用
-
端口号是16位整数(0~65535),唯一标识主机上的一个应用程序或服务。
-
每个UDP数据包的头部包含 目标端口号,接收方操作系统根据该字段将数据分发给对应的上层协议或应用。
-
-
操作系统的工作流程
-
步骤1:接收数据包
主机通过网络接口接收到UDP数据包后,IP层会剥离IP头部,将UDP数据报传递给传输层。
-
步骤2:检查目标端口
操作系统读取UDP头部的 目标端口号,查找本地是否有应用程序绑定了该端口。
-
步骤3:交付数据
-
若找到匹配的应用程序,将有效载荷(Payload)传递给该应用程序。
-
若未找到匹配的应用程序,丢弃数据包(可能返回ICMP"端口不可达"错误)。
-
-
-
端口号的绑定
-
应用程序需通过系统调用(如
bind()
)显式绑定到某个端口,才能监听该端口的UDP数据。 -
例如:
-
DNS服务器绑定到端口
53
。 -
DHCP客户端绑定到端口
68
,服务器绑定到端口67
。
-
-
UDP的缓冲区
UDP的缓冲区管理相对简单,主要依赖操作系统为每个UDP套接字分配的接收缓冲区 和发送缓冲区。
接收缓冲区(Receive Buffer)
-
作用:临时存储接收到的UDP数据报,等待应用程序读取。
-
工作流程:
-
当网络接口收到UDP数据包时,操作系统将其放入对应套接字的接收缓冲区。
-
若应用程序调用
recvfrom()
或recvmsg()
读取数据,缓冲区中的数据会被取出并交给应用层。 -
缓冲区溢出 :若缓冲区已满,新到达的数据包将被直接丢弃,且不会通知发送方。
-
-
关键特点:
-
无流量控制:发送方无法感知接收方缓冲区的状态。
-
数据报边界保留 :每个
recvfrom()
调用返回一个完整的数据报(与TCP的字节流模式不同)。
-
发送缓冲区(Send Buffer)
-
作用:暂存应用程序待发送的UDP数据报。
-
工作流程:
-
应用程序调用
sendto()
发送数据时,数据被复制到发送缓冲区。 -
操作系统将数据封装为UDP报文,尽快交给网络层发送。
-
发送限制 :若发送速率超过网络接口的处理能力,缓冲区满时后续的
sendto()
调用可能返回错误(如EWOULDBLOCK
或直接丢弃数据)。
-
-
关键特点:
-
无拥塞控制:UDP不会因网络拥塞降低发送速率。
-
立即发送倾向:多数实现会尽快清空发送缓冲区,而非等待数据累积。
-
缓冲区大小的配置
UDP缓冲区大小可通过系统调用动态调整,直接影响应用的性能和可靠性:
// 设置接收缓冲区大小(单位:字节)
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size));
// 设置发送缓冲区大小
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size));
-
默认值:不同操作系统的默认值不同(Linux通常为几十KB到几百KB)。
-
调整原则:
-
高吞吐场景:增大接收缓冲区以减少丢包(如视频流服务器)。
-
低延迟场景:减小缓冲区以避免数据堆积(如实时游戏)。
-
-
限制:缓冲区最大值受操作系统内核参数限制。
基于UDP的应用层协议
1. 经典协议:简单高效型
(1) DNS(Domain Name System)
-
用途:域名解析(将域名转换为IP地址)。
-
选择UDP的原因:
-
查询-响应模型简单,单次交互完成(请求+响应)。
-
快速响应优先(避免TCP三次握手延迟)。
-
-
可靠性处理:
-
若响应超过512字节,自动回退到TCP。
-
客户端超时重试机制(如未收到响应,重新发送查询)。
-
(2) DHCP(Dynamic Host Configuration Protocol)
-
用途:动态分配IP地址、子网掩码等网络配置。
-
选择UDP的原因:
-
使用广播/多播(UDP天然支持,而TCP无法直接广播)。
-
局域网内通信,可靠性要求较低(可依赖重试机制)。
-
(3) SNMP(Simple Network Management Protocol)
-
用途:网络设备监控与管理。
-
选择UDP的原因:
-
轻量级轮询(如定期获取设备状态)。
-
允许少量丢包(状态信息可周期性更新)。
-
(4) TFTP(Trivial File Transfer Protocol)
-
用途:简单文件传输(如网络设备固件升级)。
-
选择UDP的原因:
- 协议设计极简(代码实现轻量)。
-
可靠性处理:
- 应用层实现ACK确认和超时重传(类似停等协议)。
2. 实时传输协议:容忍丢包,优先低延迟
(1) RTP(Real-time Transport Protocol)
-
用途:实时音视频传输(如视频会议、流媒体)。
-
关键设计:
-
时间戳与序列号:支持接收端按序播放和抗抖动缓冲。
-
结合RTCP(RTP Control Protocol)反馈丢包、延迟等网络状态。
-
-
丢包处理:
-
前向纠错(FEC):发送冗余数据包,允许部分恢复丢失数据。
-
选择性重传:仅重传关键帧(如I帧)。
-
(2) WebRTC(Web Real-Time Communication)
-
用途:浏览器间实时音视频通信。
-
底层协议:基于UDP,结合RTP/RTCP和自定义拥塞控制(如GCC算法)。
-
可靠性权衡:
-
音频:允许丢包,依赖抖动缓冲和插值补偿。
-
关键信令(如SDP交换):通过可靠通道(如HTTPS/TCP)传输。
-
典型协议对比
协议 | 用途 | 可靠性实现 | 性能特点 |
---|---|---|---|
DNS | 域名解析 | 超时重试 | 微秒级响应 |
QUIC | HTTP/3传输 | 自定义重传+拥塞控制 | 多路复用,低延迟 |
RTP | 实时音视频 | 选择性重传+FEC | 毫秒级延迟,容忍丢包 |
KCP | 游戏网络 | 快速重传 | 激进的低延迟优化 |
TCP协议
TCP全称为"传输控制协议(Transmission Control Protocol)",TCP协议是当今互联网当中使用最为广泛的传输层协议,没有之一。
TCP协议被广泛应用,其根本原因就是提供了详尽的可靠性保证,基于TCP的上层应用非常多,比如HTTP、HTTPS、FTP、SSH等,甚至MySQL底层使用的也是TCP。
TCP协议的核心特点
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议,其核心特点是为不可靠的网络环境提供可靠的数据传输。
-
面向连接 :通过三次握手 建立连接、四次挥手断开连接,确保通信双方可靠地启动和终止会话。
-
可靠传输 :基于确认应答(ACK) 、超时重传 、序列号 和校验和机制,保证数据无丢失、无重复、无错误且按序到达。
-
流量控制 :通过滑动窗口动态调节发送速率,防止接收方缓冲区溢出。
-
拥塞控制 :采用慢启动 、拥塞避免 、快速重传/恢复等算法,自适应调整发送速率,避免网络拥堵。
-
全双工通信:支持双方同时收发数据,适用于双向交互场景(如HTTP、实时通信)。
-
基于字节流 :传输数据无明确边界,需应用层处理粘包/拆包问题(如添加长度字段或分隔符)。
TCP协议格式
TCP协议格式如下:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源端口(16) | 目的端口(16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 序列号(32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 确认号(32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数据偏移 | 保留 | 标志位 | 窗口大小(16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 校验和(16) | 紧急指针(16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 选项与填充(可变长度) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数据 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
字段 | 位数 | 作用说明 |
---|---|---|
源端口(Source Port) | 16位 | 发送方的端口号,标识发送进程(范围0-65535)。 |
目的端口(Destination Port) | 16位 | 接收方的端口号,标识接收进程。 |
序列号(Sequence Number) | 32位 | 数据段的第一个字节的编号,用于数据重组和确认。 |
确认号(Acknowledgment Number) | 32位 | 期望接收的下一个字节的序列号,用于确认已收到的数据(仅在ACK标志置1时有效)。 |
数据偏移(Data Offset) | 4位 | 表示TCP头部的长度(以4字节为单位),用于定位数据部分的起始位置。 |
保留(Reserved) | 6位 | 保留位,通常置0。 |
控制标志(Flags) | 6位 | 控制报文行为的关键标志位(见下方详细说明)。 |
窗口大小(Window Size) | 16位 | 接收方通告的可用缓冲区大小,用于流量控制(滑动窗口机制)。 |
校验和(Checksum) | 16位 | 用于检测头部和数据的传输错误(覆盖伪头部、TCP头部和数据)。 |
紧急指针(Urgent Pointer) | 16位 | 当URG标志置1时有效,指向紧急数据的末尾位置。 |
控制标志(Flags)
每个标志位占1位,共6位:
-
URG(Urgent):紧急指针有效,表示数据需要优先处理。
-
ACK(Acknowledgment):确认号有效,表示这是一个确认报文。
-
PSH(Push):要求接收方立即将数据提交给应用层,无需等待缓冲区填满。
-
RST(Reset):重置连接,通常用于异常终止。
-
SYN(Synchronize):发起连接请求,用于三次握手建立连接。
-
FIN(Finish):终止连接请求,用于四次挥手释放连接。
可选字段(Options)
-
长度可变(0-40字节),以4字节对齐。
-
常见选项:
-
MSS(Maximum Segment Size):协商最大报文段长度。
-
Window Scaling:窗口缩放因子,扩展窗口大小的范围。
-
Timestamp:时间戳,用于计算往返时延(RTT)和防止序列号回绕。
-
SACK(Selective ACK):选择性确认,提高重传效率。
-
确认应答机制
TCP协议的确认应答机制(ACK)是确保数据传输可靠性的核心机制,其工作原理和关键点如下:
1. 基本工作原理
-
数据包与ACK的交互 :发送方发送带有序列号(SEQ) 的数据包,接收方成功接收后回复确认号(ACK=下一个期望的SEQ)。例如,发送方发送SEQ=1001、长度100字节的数据,接收方回复ACK=1101,表示已正确接收并期待下一个数据包从1101开始。
-
累积确认 :ACK号为连续接收的最高序列号+1。若接收方收到SEQ=1001-2000后收到SEQ=3001-4000(中间有缺口),仍会回复ACK=2001,提示发送方重传2001-3000的数据。
2. 关键机制与优化
-
超时重传 :发送方启动定时器,若未及时收到ACK,则重传数据。超时时间(RTO)基于**动态计算的往返时间(RTT)**调整。
-
快速重传 :收到3次重复ACK(如连续收到ACK=2001)时,立即重传缺失数据,无需等待超时。
-
延迟ACK :接收方延迟发送ACK(通常≤500ms),尝试捎带数据以减少包数量。例如,若接收方需发送数据,将ACK与数据合并发送。
-
选择确认(SACK) :通过TCP选项允许接收方报告非连续接收的数据块(如ACK=2001且SACK=3001-4000),发送方仅重传缺失部分(2001-3000),提升效率。
3. 异常处理与流量控制
-
乱序处理:接收方缓存非连续数据,持续发送当前ACK(如缺口存在时重复ACK=2001),触发发送方重传。
-
流量控制 :ACK携带窗口大小(Window) ,告知发送方可接收的数据量,实现滑动窗口机制,防止接收方过载。
4. 示例场景
-
正常传输:
-
发送方:SEQ=1001(100字节) → 接收方:ACK=1101
-
发送方:SEQ=1101(200字节) → 接收方:ACK=1301
-
-
丢包与快速重传:
-
发送方发送SEQ=1001、2001、3001(各100字节),2001丢失。
-
接收方收到1001后回复ACK=2001,后续收到3001时仍回复ACK=2001。
-
发送方收到3次ACK=2001,立即重传SEQ=2001的数据。
-
5. 总结
-
可靠性保障:通过ACK确认、超时重传、快速重传确保数据完整到达。
-
效率优化:延迟ACK减少开销,SACK避免冗余重传,滑动窗口控制流量。
-
动态适应:RTO和窗口大小根据网络状况实时调整,平衡吞吐量与延迟。
连接管理机制
TCP(传输控制协议)是一种面向连接 、可靠的传输层协议,其连接管理机制是保障数据可靠传输的核心基础。
为何TCP必须基于连接?
TCP的可靠性机制(如序列号确认、超时重传、流量控制、拥塞控制等)本质上是基于连接而非主机到主机的。这种设计通过为每个连接分配独立的资源和状态管理,确保了数据的可靠传输和隔离性。
一、资源隔离与状态独立
-
独立缓冲区 :每个连接维护独立的发送和接收缓冲区,避免多客户端数据混杂。若所有客户端共享一个缓冲区,数据可能互相覆盖或乱序,导致应用层无法正确解析。
-
序列号空间 :每个连接有独立的序列号空间(初始序列号随机生成),确保数据按序到达且无冲突。
-
状态机管理 :每个连接拥有独立的状态机(如
ESTABLISHED
、TIME_WAIT
),实现连接的建立、维护、关闭的全生命周期管理。
二、多路复用与多路分解
-
端口与四元组 :TCP通过**四元组(源IP、源端口、目的IP、目的端口)**唯一标识连接,传输层根据端口号将数据分发到正确的应用进程(多路分解)。若没有连接,无法区分不同客户端或会话的数据。
-
并发处理:服务器通过为每个客户端创建独立连接,实现高并发通信。例如,Web服务器可同时处理数千个HTTP请求,每个请求对应一个TCP连接(或长连接复用)。
三、端到端可靠传输的粒度
- 进程到进程的可靠传输 :TCP的可靠性是面向应用进程而非主机的。例如,同一主机的两个进程(如浏览器和数据库客户端)通过不同端口建立独立连接,各自维护传输状态,互不干扰。
四、总结
TCP的可靠性机制与连接强绑定 ,通过为每个连接分配独立资源(缓冲区、序列号、状态机),确保数据隔离、按序传输和错误恢复。这种设计解决了多客户端并发场景下的数据混乱问题,并为性能优化(如连接复用)和安全管理(如加密隔离)提供了基础。若取消连接概念,TCP将无法实现其承诺的可靠性,成为无序、不可控的数据传输协议。因此,连接是TCP实现可靠传输的核心抽象,也是其区别于无连接协议(如UDP)的根本特征。
三次握手
TCP三次握手(Three-Way Handshake)是建立TCP连接的核心过程,其目的是同步双方的初始序列号(ISN) 、协商连接参数 ,并确认双方的通信能力。
双方在进行TCP通信之前需要先建立连接,建立连接的这个过程我们称之为三次握手。
三次握手由客户端(主动发起方)和服务器(被动接受方)共同完成,过程如下:
步骤 | 方向 | 报文内容 | 状态变化 |
---|---|---|---|
1 | 客户端 → 服务器 | SYN=1 ,携带随机初始序列号 Seq=x |
客户端进入 SYN_SENT 状态 |
2 | 服务器 → 客户端 | SYN=1 ,ACK=1 ,携带随机初始序列号 Seq=y 和确认号 Ack=x+1 |
服务器进入 SYN_RCVD 状态 |
3 | 客户端 → 服务器 | ACK=1 ,携带确认号 Ack=y+1 (可携带数据) |
双方进入 ESTABLISHED 状态 |
-
第一次握手(SYN)
客户端发送SYN报文(SYN=1),携带初始序列号(ISN),表示请求建立客户端到服务器方向的连接。
-
第二次握手(SYN-ACK)
服务器回应SYN-ACK报文(SYN=1, ACK=1),包含自己的ISN,并确认客户端的ISN(ACK=客户端ISN+1)。此步骤同时完成两项操作:
-
确认客户端的SYN(ACK),建立客户端→服务器连接。
-
发送自己的SYN,请求建立服务器→客户端连接。
-
-
第三次握手(ACK)
客户端发送ACK报文(ACK=1),确认服务器的ISN(ACK=服务器ISN+1),完成服务器→客户端连接的建立。
注意:
-
三次握手并非分别建立两个独立连接,而是通过三次交互确保双方同时具备收发能力。
-
服务器在第二次握手的SYN-ACK是合并操作,并非先响应客户端再发起新请求。
为什么是三次握手?
TCP采用三次握手(Three-Way Handshake)来建立连接,主要是为了确保通信双方的双向通信能力、同步初始序列号(ISN),并防止历史重复报文导致的连接混乱。三次握手是可靠性和效率的最佳平。
一、核心目标
三次握手需解决以下问题:
-
验证双向通信能力:确保客户端和服务器均具备发送和接收数据的能力。
-
同步初始序列号(ISN):为后续数据传输的有序性和可靠性奠定基础。
-
避免历史报文干扰:防止网络中的旧连接报文被错误接收。
二、为什么不是两次握手?
假设采用两次握手(客户端发送SYN
,服务器回复SYN-ACK
即认为连接建立):
-
问题1:服务器单方面认为连接已建立
若客户端的
SYN
因网络延迟未到达服务器,客户端会重发新的SYN
。如果旧的SYN
随后到达服务器,服务器会回复SYN-ACK
并认为连接已建立,但客户端因已发起新连接而忽略该回复。此时服务器会为"幽灵连接"分配资源,导致资源浪费(半连接队列堆积)。 -
问题2:无法确认客户端的接收能力
服务器无法确定客户端是否能收到自己的
SYN-ACK
报文。若客户端因网络故障未收到回复,服务器会一直等待不存在的连接,造成资源泄漏。
三次握手通过客户端的最终确认(第三次ACK
),确保双方均确认对方的通信能力,避免上述问题。
三、为什么不是四次握手?
三次握手已能实现所有必要验证:
-
客户端发送
SYN
→ 证明客户端发送能力正常。 -
服务器回复
SYN-ACK
→ 证明服务器接收和发送能力正常。 -
客户端回复
ACK
→ 证明客户端接收能力正常。
若增加第四次握手(如服务器再回复一个ACK
),只会重复确认已知信息,增加网络开销和延迟,无实际收益。
套接字和三次握手之间的关系
套接字是应用程序与TCP协议栈交互的接口 ,而 三次握手是TCP通过套接字建立连接的具体过程。
三次握手由 客户端主动调用connect()
触发,内核通过套接字完成协议交互:
步骤 | 套接字操作 | 内核行为 |
---|---|---|
1 | 客户端调用connect() |
发送SYN 报文,套接字状态变为SYN_SENT 。 |
2 | 服务器内核收到SYN ,若半连接队列未满,回复SYN-ACK 。 |
套接字状态变为SYN_RCVD ,连接暂存于半连接队列(SYN Queue)。 |
3 | 客户端内核收到SYN-ACK ,发送ACK 。 |
客户端套接字状态变为ESTABLISHED ,服务器将连接移入全连接队列(Accept Queue)。 |
关键点:
-
客户端通过
connect()
触发三次握手,该函数阻塞至握手完成或超时。 -
服务器的
accept()
仅从全连接队列中取出已建立的连接,不参与握手过程。// 应用程序无需直接处理三次握手的细节,套接字API已将其抽象为简单操作:
// 客户端
int client_fd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字
connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 触发三次握手// 服务端
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(server_fd, backlog); // 开始监听
int client_fd = accept(server_fd, NULL, NULL); // 从全连接队列取出已建立的连接
套接字是应用程序与TCP协议栈交互的桥梁,三次握手则是通过套接字触发的底层连接建立过程。
-
开发者视角 :只需调用
connect()
和accept()
,无需关心握手细节。 -
系统视角 :内核通过套接字管理连接状态、队列和协议交互。
理解二者关系,有助于定位连接问题(如队列溢出、握手超时)并优化高并发服务的性能。
四次挥手
TCP四次挥手(Four-Way Handshake)是TCP协议中关闭连接的标准流程 ,其核心目标是确保双方数据完整传输并安全释放资源。
四次挥手由主动关闭方(如客户端)和被动关闭方(如服务器)共同完成,流程如下:
步骤 | 方向 | 报文内容 | 状态变化 |
---|---|---|---|
1 | 客户端 → 服务器 | FIN=1 ,携带序列号 Seq=u |
主动方进入 FIN_WAIT_1 状态 |
2 | 服务器 → 客户端 | ACK=1 ,确认号 Ack=u+1 |
被动方进入 CLOSE_WAIT 状态 |
3 | 服务器 → 客户端 | FIN=1 ,携带序列号 Seq=w (可能携带数据) |
被动方进入 LAST_ACK 状态 |
4 | 客户端 → 服务器 | ACK=1 ,确认号 Ack=w+1 |
主动方进入 TIME_WAIT 状态 |
1. 第一次挥手(FIN):客户端 发送FIN报文(FIN=1
),携带当前序列号(Seq=u
),表示主动关闭本方发送通道 ,进入FIN-WAIT-1
状态。
2. 第二次挥手(ACK):服务器 收到FIN后,立即返回ACK报文(ACK=1
),确认号为u+1
(ACK=u+1
),表示已确认客户端的关闭请求。
-
服务器进入
CLOSE-WAIT
状态,仍可向客户端发送未完成的数据。 -
客户端收到ACK后,进入
FIN-WAIT-2
状态,等待服务器关闭反向通道。
3. 第三次挥手(FIN):服务器 完成数据发送后,发送FIN报文(FIN=1
),携带序列号Seq=v
,请求关闭反向通道 ,进入LAST-ACK
状态。
4. 第四次挥手(ACK):客户端 收到FIN后,返回ACK报文(ACK=1
),确认号为v+1
(ACK=v+1
),进入TIME-WAIT
状态。
-
服务器收到ACK后,立即关闭连接 (进入
CLOSED
状态)。 -
客户端需等待
2MSL
(Maximum Segment Lifetime)时间后关闭连接,确保最后一个ACK到达服务器,防止旧报文干扰新连接。
注意:
-
双向独立关闭
TCP是全双工协议,每个方向需独立关闭:
-
第一次挥手关闭客户端→服务器方向。
-
第三次挥手关闭服务器→客户端方向。
-
-
ACK与FIN分离的原因
-
第二次挥手仅确认客户端的FIN,不携带FIN,因为服务器可能仍有数据需发送(需等待数据传输完成才能关闭反向通道)。
-
若服务器无数据需发送,可合并第二次和第三次挥手(FIN与ACK同报文发送),但实际实现中通常分开。
-
-
TIME-WAIT状态的作用
-
确保最后一个ACK可靠到达服务器(若丢失,服务器会重传FIN)。
-
等待
2MSL
(通常为1-4分钟)以消除网络中残留的旧报文,避免与新连接冲突。
-
-
状态转换
-
客户端:
FIN-WAIT-1
→FIN-WAIT-2
→TIME-WAIT
→CLOSED
-
服务器:
CLOSE-WAIT
→LAST-ACK
→CLOSED
-
误区澄清
-
四次挥手的必要性:并非所有场景必须四次挥手。若服务器无数据需发送,可能合并第二次和第三次挥手(FIN+ACK同报文发送),但标准流程仍按四次分析。
-
主动关闭方的角色:客户端或服务器均可作为主动关闭方,取决于谁先发起关闭请求。
-
TIME-WAIT的争议 :
TIME-WAIT
可能导致端口资源占用,但牺牲资源换取可靠性是TCP的核心设计原则。
为什么是四次挥手?
TCP是全双工协议,客户端与服务器各自拥有独立的发送和接收通道 。断开连接时,双方需分别关闭自己的发送通道:
-
第一次挥手 :主动关闭方(如客户端)发送
FIN
,表示本方不再发送数据 ,但仍可接收数据。 -
第三次挥手 :被动关闭方(如服务器)发送
FIN
,表示本方不再发送数据 ,但此前可能仍需处理剩余数据。
为什么不能合并为三次挥手?
如果被动关闭方在收到FIN
时已无数据要发送 ,理论上可将第二次挥手(ACK
)和第三次挥手(FIN
)合并为一个报文(即ACK+FIN
),变为三次挥手。但实际场景中,服务器可能需要时间处理未发送完的数据,因此标准流程设计为四次挥手,以确保可靠性。
四次挥手的本质是全双工通信下双向通道的独立关闭:
-
主动方关闭发送通道(第一次挥手)。
-
被动方确认关闭请求(第二次挥手)。
-
被动方处理数据后关闭反向通道(第三次挥手)。
-
主动方最终确认(第四次挥手)。
这种设计确保了TCP连接的可靠关闭,避免数据丢失或残留报文干扰新连接,是TCP协议高可靠性的核心体现。
流量控制
TCP流量控制(Flow Control)是一种基于接收方处理能力的动态调节机制 ,其核心目标是防止发送方发送数据过快,导致接收方缓冲区溢出 。流量控制通过**滑动窗口协议(Sliding Window Protocol)**实现。
接收窗口(rwnd)
-
定义 :接收方通过TCP报文头中的
Window
字段告知发送方当前可接收的数据量(单位:字节)。 -
动态调整:
-
接收窗口大小(rwnd) = 接收缓冲区剩余空间。
-
每次发送ACK时更新
Window
字段,反映最新的接收能力。
-
发送窗口(Send Window)
-
定义 :发送方根据接收方的
Window
值动态调整可发送的数据范围。 -
组成:
-
已发送未确认(In-flight Data):占用发送窗口,需等待ACK。
-
可发送未发送(Usable Window):允许立即发送的数据量(= rwnd - In-flight)。
-
滑动窗口机制
-
发送过程:
-
发送方根据接收方的
Window
值填充发送窗口。 -
数据发送后等待ACK,收到ACK后窗口向前滑动,释放已确认数据的空间
发送窗口范围:[Seq=1000, Seq=2000)
已发送未确认:1000~1500(500字节)
可发送未发送:1500~2000(500字节)
若收到ACK=1500且rwnd=1000,窗口更新为:[Seq=1500, Seq=2500) -
滑动窗口
发送缓冲区的三大部分
数据分区 | 描述 | 状态 |
---|---|---|
已发送且已确认 | 数据成功送达接收方,并收到ACK确认。 | 可释放缓冲区空间。 |
已发送但未确认 | 数据已发送但未收到ACK(In-flight Data),占据滑动窗口的已用部分。 | 占用窗口,需等待确认或重传。 |
未发送(待发送) | 数据在缓冲区中等待发送,根据窗口大小动态填充(滑动窗口的可用部分)。 | 可立即发送或等待窗口移动。 |
滑动窗口的两种解释
-
狭义定义 :仅指已发送但未确认的数据(第二部分)。
-
广义定义 :包含已发送未确认 + 可发送未发送的数据(第二部分 + 第三部分中允许发送的范围)。
窗口大小的决定因素
-
接收窗口(rwnd) :由接收方通过TCP报文头的
Window
字段告知,表示其剩余缓冲区大小(流量控制)。 -
拥塞窗口(cwnd):由发送方根据网络拥塞状态动态调整(拥塞控制)。
-
实际发送窗口 :
发送窗口大小 = min(rwnd, cwnd)
。
滑动过程示例
发送窗口范围:[Seq=1000, Seq=2000)
已发送未确认:1000~1500(500字节)
可发送未发送:1500~2000(500字节)
若收到ACK=1500且rwnd=1000,窗口更新为:[Seq=1500, Seq=2500)
拥塞控制
TCP拥塞控制(Congestion Control)是一种基于网络拥塞状态动态调整发送速率 的机制,其核心目标是避免网络过载,维持高吞吐量与低延迟的平衡。
拥塞窗口(cwnd)
-
定义 :发送方维护的动态窗口,表示在不引发网络拥塞的前提下,允许一次性发送的最大数据量(单位:MSS,最大报文段长度)。
-
调节依据:网络拥塞信号(如报文丢失、延迟增加)。
实际发送窗口
-
窗口大小 :
实际发送窗口 = min(cwnd, rwnd)
,其中rwnd
为接收方的流量控制窗口。 -
目标:在避免拥塞的前提下,尽可能利用网络带宽。
拥塞控制 VS 流量控制
维度 | 拥塞控制(Congestion Control) | 流量控制(Flow Control) |
---|---|---|
目标 | 防止网络链路过载。 | 防止接收方缓冲区溢出。 |
调节依据 | 网络拥塞信号(丢包、延迟)。 | 接收方的接收窗口(rwnd)。 |
控制窗口 | 拥塞窗口(cwnd)。 | 发送窗口(基于rwnd)。 |
实现机制 | 慢启动、拥塞避免、快重传等算法。 | 滑动窗口协议。 |
基于字节流
TCP协议作为面向字节流(Byte-Oriented) 的传输层协议,其核心特性是将数据视为无结构的连续字节序列,而非有明确边界的数据包。
创建TCP Socket时,内核会为每个连接维护发送缓冲区(Send Buffer) 和接收缓冲区(Receive Buffer),这些缓冲区是TCP实现可靠传输、流量控制及拥塞控制的核心组件。
发送缓冲区
-
写入数据 :应用层调用
write
/send
将数据写入发送缓冲区,函数立即返回(无需等待数据发送到网络)。 -
数据发送:TCP协议栈根据以下条件决定何时发送数据:
-
滑动窗口:接收方的剩余窗口大小(流量控制)。
-
拥塞窗口:网络拥塞状态(拥塞控制)。
-
Nagle算法:合并小数据包(默认启用),减少网络开销。
-
接收缓冲区
-
接收数据:数据从网卡到达接收缓冲区,等待应用层读取。
-
读取数据 :应用层调用
read
/recv
从接收缓冲区读取数据,可按任意字节数读取,TCP保证数据顺序与完整性。// 发送方:写入100字节(可能多次write)
write(sock, "Hello", 5);
write(sock, "World", 5);
// 接收方:可能一次读取到"HelloWorld",需应用层拆分。
char buf[10];
read(sock, buf, 10); // 读取到"HelloWorld"
实际对于TCP来说,它并不关心发送缓冲区当中的是什么数据,在TCP看来这些只是一个个的字节数据,它的任务就是将这些数据准确无误的发送到对方的接收缓冲区当中就行了,而至于如何解释这些数据完全由上层应用来决定,这就叫做面向字节流。
粘包和拆包
什么是粘包?
粘包是基于字节流的TCP协议在应用层表现出的现象 ,指发送方多次发送的数据被接收方一次性接收,导致多个独立的数据包在接收端缓冲区中"粘连"在一起,无法直接区分原始消息边界。
一、TCP的字节流特性
-
无消息边界:TCP协议将数据视为连续的字节流,不保留应用层消息的边界信息。
-
传输层分片 :TCP根据MSS(最大报文段长度)和滑动窗口动态拆分/合并数据,导致发送方的多次
send()
可能对应接收方的一次recv()
。
二、触发粘包的典型场景
-
Nagle算法 :为减少小包数量,TCP默认启用Nagle算法,合并多个小数据包(如连续调用
send("a")
和send("b")
可能合并为发送"ab"
)。 -
接收方缓冲区处理:若接收方未及时读取数据,多个报文段可能在接收缓冲区中累积,形成粘包。
-
网络传输抖动:不同报文段的传输延迟差异可能导致数据到达顺序不规律。
现象 | 描述 | 示例 |
---|---|---|
粘包 | 多个数据包被合并接收,需拆分处理。 | 发送"Hello" 和"World" ,接收"HelloWorld" 。 |
半包 | 单个数据包被拆分多次接收,需拼接处理。 | 发送"HelloWorld" ,接收"Hello" 和"World" 。 |
如何解决粘包问题?
要解决粘包问题,本质就是要明确报文和报文之间的边界。
- 对于定长的包,保证每次都按固定大小读取即可。
- 对于变长的包,可以在报头的位置,约定一个包总长度的字段,从而就知道了包的结束位置。比如HTTP报头当中就包含Content-Length属性,表示正文的长度。
- 对于变长的包,还可以在包和包之间使用明确的分隔符。因为应用层协议是程序员自己来定的,只要保证分隔符不和正文冲突即可。
可靠性和性能保障
一、可靠性保障机制
1. 数据完整性验证
-
检验和(Checksum) :
每个TCP报文包含16位校验和,用于检测数据在传输过程中的损坏(如比特翻转)。若校验失败,接收方直接丢弃报文,触发发送方重传。
2. 有序交付与重复过滤
-
序列号(Sequence Number) :
每个字节分配唯一序列号,接收方按序重组数据,解决网络乱序问题。
-
确认应答(ACK) :
接收方通过ACK告知已成功接收的数据范围(累计确认),确保发送方知晓哪些数据需重传。
3. 丢失检测与恢复
-
超时重传(RTO) :
发送方为每个未确认报文启动定时器,超时未收到ACK则重传。
-
快速重传(Fast Retransmit) :
收到3次重复ACK时,立即重传疑似丢失的报文(无需等待超时)。
4. 连接与资源管理
-
连接管理 :
通过三次握手建立连接(协商参数)、四次挥手释放连接(确保数据完整传输)。
-
流量控制(Flow Control) :
接收方通过窗口大小(
rwnd
)动态告知可接收数据量,防止缓冲区溢出。
5. 网络拥塞预防
-
拥塞控制(Congestion Control) :
动态调整拥塞窗口(
cwnd
),根据网络状态(丢包、延迟)限制发送速率,避免链路过载。- 算法:慢启动、拥塞避免、快恢复(如CUBIC、BBR)。
二、性能优化机制
1. 减少等待时间
-
滑动窗口(Sliding Window) :
允许发送方连续发送多个报文(窗口大小由
rwnd
和cwnd
决定),无需逐包等待ACK("流水线"传输)。 -
延迟应答(Delayed ACK) :
接收方延迟发送ACK(通常200ms),尝试将ACK捎带在反向数据中,减少报文数量。
2. 提升重传效率
-
快速重传(Fast Retransmit) :
通过重复ACK快速定位丢包,减少超时等待。
-
选择性确认(SACK) :
接收方明确告知丢失的报文范围,避免盲目重传已成功接收的数据。
3. 网络资源高效利用
-
捎带应答(Piggybacking) :
将ACK嵌入反向数据报文(如HTTP响应),减少独立ACK报文的数量。
-
Nagle算法 :
合并多个小数据包(如多次
write("a")
合并为单次发送),减少网络小包开销。
4. 适应高带宽网络
-
窗口缩放(Window Scaling) :
通过TCP选项扩展窗口大小(原16位窗口最大64KB),支持高带宽延迟积(BDP)网络。
-
时间戳选项(Timestamp) :
精确计算RTT(往返时间),优化超时重传判断。
基于TCP的应用层协议
-
**HTTP (80) :**传输网页内容,支持客户端与Web服务器通信。
-
**HTTPS (443):**加密版HTTP,用于安全传输网页及敏感数据。
-
**FTP (21控制端口/20数据端口):**文件上传、下载及远程文件管理。
-
**SMTP (25):**发送电子邮件(服务器间或客户端到服务器)。
-
**POP3 (110):**从服务器下载邮件到本地,离线阅读。
-
**IMAP (143):**在线管理服务器邮件(同步标记、文件夹)。
-
**SSH (22):**加密远程登录、文件传输(SCP/SFTP)、端口转发。
-
**MySQL (3306):**客户端与MySQL数据库的查询及数据交互。
-
**Redis (6379):**Redis数据库的命令请求与响应通信。
-
**gRPC (自定义端口):**高性能微服务间通信,支持流式数据传输。
-
**WebSocket (80/443):**全双工实时通信(如聊天、实时数据推送)。
-
**MQTT (1883):**轻量级物联网设备消息传递(发布-订阅模型)。