网络与信息安全技术 第 3 篇:可靠数据传输原理与TCP拥塞控制
全文摘要
本文将带你深入理解传输层如何实现可靠的数据传输和有效的拥塞控制。你将学到传输层的服务模型、进程间通信的寻址机制、可靠数据传输的理论基础、停-等协议与滑动窗口协议的工作原理、UDP与TCP的区别、TCP连接管理的三次握手与四次挥手、TCP可靠传输的确认与重传机制、滑动窗口流量控制,以及TCP拥塞控制的四个阶段。通过阅读本文,你将掌握传输层的核心技术,理解现代互联网可靠通信的基础机制。
一、传输层的基本服务
传输层位于网络层之上、应用层之下,为端系统上的应用进程提供端到端的通信服务。传输层的关键作用是将网络层的不可靠服务(IP)转换为应用层需要的可靠服务(TCP)。
传输层的核心功能
端到端通信:传输层负责将数据从源端系统的进程传输到目的端系统的进程。这与网络层不同,网络层只负责将数据从源主机传输到目的主机,不关心进程的概念。
复用与分解:多个应用进程可以共享同一个传输层协议(多个进程复用同一个传输层接口),传输层根据端口号将收到的数据交付给正确的应用进程(分解)。
可靠传输:传输层可以提供可靠的数据传输服务,确保数据无差错、不丢失、不重复、按序到达。TCP提供可靠传输,UDP不提供。
流量控制:发送方不要发送太快,不要淹没接收方。TCP使用滑动窗口机制实现流量控制。
拥塞控制:发送方不要发送太快,避免造成网络拥塞。TCP通过拥塞窗口和网络状态反馈实现拥塞控制。
连接管理:面向连接的传输协议(如TCP)需要在数据传输前建立连接,传输后释放连接。
传输层寻址:端口号
网络层使用IP地址标识主机,传输层使用端口号标识主机上的进程。端口号是16位整数,范围是0-65535。
熟知端口(0-1023):由IANA(互联网数字分配机构)分配给特定的应用,这些端口只有系统管理员权限才能使用。常见的熟知端口包括:
- 21:FTP
- 22:SSH
- 25:SMTP
- 53:DNS
- 80:HTTP
- 443:HTTPS
注册端口(1024-49151):由IANA注册,供特定的应用程序使用。
动态端口(49152-65535):客户端程序动态选择的临时端口。
传输层服务类型
传输层提供两种服务类型:
无连接服务:发送数据前不需要建立连接,每个数据段独立路由。优点是简单高效,缺点是不保证可靠传输。UDP是无连接服务。
面向连接服务:数据传输前需要建立连接,传输过程中维护连接状态,传输后释放连接。优点是可以提供可靠传输,缺点是开销较大。TCP是面向连接服务。
二、传输层的复用与分解
复用(Multiplexing)和分解(Demultiplexing)是传输层的核心机制,使得多个应用进程可以共享网络通信。
复用的概念
复用是指发送端的多个应用进程将数据交给同一个传输层协议,传输层给每个数据段添加头部(包含源端口和目的端口),然后传递给网络层。这样,多个进程的数据可以共享同一个网络连接。
复用类似于公司的邮件收发室:多个部门的员工将需要发送的邮件交给收发室,收发室汇总后统一交给邮局发送。
分解的概念
分解是指接收端的传输层收到数据段后,检查头部中的目的端口号,将数据段交付给对应的应用进程。分解是复用的逆过程。
分解的关键是传输层如何知道应该将数据交付给哪个进程。这通过套接字(Socket)实现。套接字是进程与传输层之间的接口,每个套接字有一个唯一的标识符(IP地址+端口号)。
无连接分解
对于无连接传输(如UDP),套接字由一个二元组标识:(目的IP地址,目的端口号)。当UDP数据段到达时,传输层根据目的端口号查找对应的套接字,将数据交付给绑定了该端口的进程。
多个客户端可以同时向同一个服务器进程发送数据,服务器可以区分不同的客户端,因为每个数据段的源IP地址和源端口号不同。
面向连接分解
对于面向连接传输(如TCP),套接字由一个四元组标识:(源IP地址,源端口号,目的IP地址,目的端口号)。这是因为TCP连接是点对点的,每个连接是唯一的。
当TCP数据段到达时,传输层根据四元组查找对应的套接字。这允许一个主机同时支持多个TCP连接,甚至多个连接连接到同一个服务端口。
一个Web服务器可以同时处理数千个并发连接,每个连接有不同的客户端IP和端口,但可能都连接到服务器的80端口。服务器通过四元组区分这些连接。
三、可靠数据传输的基本原理
可靠数据传输是传输层的重要功能。在网络环境中,数据可能丢失、出错、乱序、重复,可靠数据传输协议需要处理这些问题。
不可靠信道的问题
在不可靠信道上实现可靠传输面临以下问题:
比特错误:物理信号的干扰导致比特翻转,接收方收到错误的数据。通过差错检测(如校验和)可以发现错误。
数据丢失:数据包在网络中丢失(如队列溢出、链路错误)。接收方没有收到数据,发送方不知道。需要超时和重传机制。
数据乱序:数据包经过不同路径到达,后发的可能先到。需要序号机制来排序。
数据重复:超时重传可能导致接收方收到重复的数据。需要去重机制。
可靠数据传输的机制
为解决上述问题,可靠数据传输协议使用以下机制:
差错检测:发送方在数据段中添加校验和(Checksum),接收方计算校验和,如果不匹配说明数据出错。出错的数据段被丢弃,发送方通过超时重传。
确认(ACK):接收方收到正确数据后,发送确认信息告诉发送方。确认信息包含确认序号,表示期望收到的下一个字节序号。
重传:发送方发送数据后启动定时器,如果在超时前没有收到确认,则重传该数据。
序号:每个数据字节都有序号,接收方通过序号检测数据丢失和乱序。发送方通过序号知道哪些数据已被确认。
回退N步(Go-Back-N,GBN):发送方维护一个发送窗口,窗口内的数据可以连续发送而不必等待确认。如果某个数据段超时,发送方重传从该段开始的所有未确认数据。
选择重传(Selective Repeat,SR):发送方维护发送窗口,接收方维护接收窗口。如果某个数据段出错,只重传出错的数据段,而不是重传所有未确认数据。
四、停-等协议
停-等协议(Stop-and-Wait)是最简单的可靠数据传输协议。发送方发送一个数据段后停止发送,等待接收方的确认,收到确认后再发送下一个数据段。
停-等协议的工作过程
sequenceDiagram participant Sender as 发送方 participant Channel as 信道 participant Receiver as 接收方 Note over Sender,Receiver: 停-等协议的正常操作 Sender->>Channel: 发送数据0 Channel->>Receiver: 传输数据0 Receiver->>Receiver: 检验数据正确 Receiver->>Channel: 发送ACK0 Channel->>Sender: 传输ACK0 Sender->>Sender: 收到确认,准备发送下一个 Sender->>Channel: 发送数据1 Channel->>Receiver: 传输数据1 Note over Sender,Receiver: 如果数据1出错或丢失 Sender->>Sender: 超时,重传数据1 Sender->>Channel: 重传数据1 Channel->>Receiver: 传输数据1 Receiver->>Channel: 发送ACK1 Channel->>Sender: 传输ACK1 Sender->>Sender: 收到确认,可以继续
图表讲解:这张时序图展示了停-等协议的工作流程——这是理解可靠传输基础的重要模型。
正常情况下,发送方发送数据0,接收方收到后发送ACK0确认。发送方收到ACK0后,知道数据0已正确接收,接着发送数据1。这种一问一答的模式简单明了,确保每个数据都被正确接收后才发送下一个。
但如果数据1在传输过程中出错或丢失,接收方不会发送ACK1(或发送的ACK丢失),发送方在超时后没有收到确认,就重传数据1。注意重传的是所有未确认的数据,在停-等协议中只有一个未确认数据,所以重传数据1。
停-等协议使用序号区分新旧数据。例如,ACK1表示确认序号1及之前的所有数据,期望收到序号2。序号还可以解决重复数据的问题:如果接收方收到重复的数据,可以检测到并丢弃。
停-等协议的性能分析
停-等协议的主要问题是效率低。在发送方等待确认期间,信道是空闲的,浪费了信道容量。
计算停-等协议的效率(利用率):
- 发送时延 = L / R,其中L是数据段长度,R是信道带宽
- 往返时间(RTT) = 传播时延 × 2 + 处理时延
- 总时间 = 发送时延 + RTT
- 利用率 = 发送时延 / 总时间 = (L/R) / (L/R + RTT)
假设L=1500字节,R=1Gbps,RTT=50ms:
- 发送时延 = 1500×8 / 10⁹ = 12μs
- 总时间 ≈ 50ms(RTT占主导)
- 利用率 ≈ 12μs / 50ms ≈ 0.024%
这个利用率非常低!大部分时间信道都在空闲等待。停-等协议适合长RTT、低带宽的环境,不适合现代高速网络。
五、滑动窗口协议
滑动窗口协议是停-等协议的改进,允许发送方连续发送多个数据段而不必等待每个确认,大大提高了信道利用率。
滑动窗口的基本概念
滑动窗口协议使用三个关键机制:
发送窗口:发送方维护一个发送窗口,窗口内的数据可以连续发送。窗口大小N表示最多可以有N个未确认的数据段在管道中。
累积确认:接收方发送的ACK确认序号n表示序号n及之前的所有数据都已正确接收。
窗口滑动:当收到确认后,发送窗口向右滑动,允许发送新的数据。
滑动窗口协议实现了流水线(Pipelining)效果:多个数据段同时在信道中传输,就像工厂的流水线,多个产品同时在生产线上处理,而不是一个产品完成后再开始下一个。
回退N步协议(GBN)
回退N步协议是滑动窗口协议的一种实现。
发送方:
- 维护一个大小为N的发送窗口
- 窗口基序号:最早的未确认数据段序号
- 下一个序号:下一个要发送的数据段序号
- 可以连续发送窗口内的数据段
- 收到累积确认ACK n后,窗口基序号更新为n
- 如果超时,重传从基序号开始的所有未确认数据段
接收方:
- 维护一个期望序号,表示期望收到的下一个数据段序号
- 如果收到正确序号的数据段,发送ACK,交付应用层,期望序号加1
- 如果收到错误序号的数据段,丢弃,重发最近一次的ACK
- 不缓存乱序到达的数据段
GBN的名称来源于其重传策略:如果某个数据段超时,发送方”回退”到该段,重传该段及其后的所有未确认数据段。
选择重传协议(SR)
选择重传协议是对GBN的改进,只重传出错的数据段,而不是重传所有未确认数据段。
发送方:
- 维护一个大小为N的发送窗口
- 每个数据段有独立的定时器
- 如果某个数据段超时,只重传该数据段
接收方:
- 维护一个大小为N的接收窗口
- 可以缓存乱序到达的数据段
- 收到窗口内的数据段后发送ACK
- 当窗口基序号的数据段到达时,将连续的缓存数据交付应用层
SR协议需要更大的接收窗口和更复杂的缓存管理,但减少了不必要的重传,提高了效率。
六、用户数据报协议(UDP)
UDP(User Datagram Protocol)是传输层的无连接协议,提供简单的、不可靠的数据传输服务。
UDP的特点
无连接:发送数据前不需要建立连接,没有连接建立和释放的开销。
不可靠:不保证数据送达,没有确认和重传机制。数据可能丢失、重复、乱序。
轻量级:UDP头部只有8字节,开销小。
无拥塞控制:发送速度不受网络拥塞状态影响,适合实时应用。
支持一对一、一对多、多对多通信:可以单播、广播、组播。
UDP数据报结构
UDP头部只有4个字段,共8字节:
- 源端口(16位):发送方端口号
- 目的端口(16位):接收方端口号
- 长度(16位):UDP数据报总长度(头部+数据)
- 校验和(16位):检测数据传输中的错误
UDP校验和
UDP校验和检测数据在传输过程中是否出错。校验和的计算过程:
- 将UDP数据报(包括伪头部)按16位分段
- 对所有16位字进行求和(进位加到和的低位)
- 对和取反,得到校验和
- 接收方重新计算校验和,如果为0说明数据正确
UDP的校验和是可选的(IPv4),如果校验和字段为0,表示不使用校验和。在IPv6中,UDP校验和是必须的。
UDP的使用场景
UDP适合对可靠性要求不高、对实时性要求高的应用:
实时应用:如视频会议、在线游戏、实时语音。这些应用可以容忍少量数据丢失,但不能容忍延迟。UDP的低延迟适合这类应用。
简单请求-响应:如DNS查询。请求和响应都很简单,不需要建立连接的开销。
组播:如IPTV、在线直播。将数据同时发送给多个接收者,TCP不支持组播。
七、传输控制协议(TCP)
TCP(Transmission Control Protocol)是传输层的面向连接协议,提供可靠的、面向字节流的传输服务。TCP是互联网最重要的协议之一,HTTP、FTP、SMTP等应用层协议都建立在TCP之上。
TCP的特点
面向连接:数据传输前需要建立连接,传输后释放连接。
可靠传输:通过确认、重传、序号等机制确保数据可靠送达。
面向字节流:TCP将数据看作连续的字节流,不保留应用层消息的边界。
全双工通信:连接双方可以在任何时候发送和接收数据。
流量控制:通过滑动窗口防止发送方淹没接收方。
拥塞控制:通过拥塞窗口防止网络过载。
TCP报文段结构
TCP头部比UDP复杂得多,最小20字节,包含以下字段:
- 源端口和目的端口(各16位):标识发送和接收进程
- 序号(32位):该报文段第一个字节的序号
- 确认序号(32位):期望收到的下一个字节序号
- 头部长度(4位):TCP头部的长度(以32位字为单位)
- 标志位(6位):URG、ACK、PSH、RST、SYN、FIN
- 接收窗口(16位):接收窗口大小,用于流量控制
- 校验和(16位):检测传输错误
- 紧急指针(16位):与URG标志配合使用
- 选项:可选字段,如最大报文段长度(MSS)、窗口扩大因子等
TCP连接管理
TCP是面向连接的协议,连接建立需要三个阶段,连接释放需要四个阶段。
三次握手建立连接
sequenceDiagram participant Client as 客户端 participant Server as 服务器 Note over Client,Server: TCP三次握手建立连接 Client->>Server: SYN, SEQ=x<br/>(SYN=1, SEQ=x) Note over Client,Server: 客户端进入SYN_SENT状态 Server->>Server: 分配资源<br/>准备接收数据 Server->>Client: SYN-ACK, SEQ=y, ACK=x+1<br/>(SYN=1, ACK=1, SEQ=y, ACK=x+1) Note over Client,Server: 服务器进入SYN_RCVD状态 Client->>Server: ACK, ACK=y+1<br/>(ACK=1, ACK=y+1) Note over Client,Server: 客户端进入ESTABLISHED状态 Server->>Server: 收到ACK Note over Client,Server: 服务器进入ESTABLISHED状态<br/>连接建立完成
图表讲解:这张时序图展示了TCP三次握手建立连接的过程——这是TCP协议的核心机制。
第一次握手:客户端发送SYN报文段,SYN标志置1,包含客户端的初始序号x。客户端进入SYN_SENT状态,等待服务器的确认。
第二次握手:服务器收到SYN后,分配必要的资源(如缓冲区),发送SYN-ACK报文段。SYN和ACK标志都置1,包含服务器的初始序号y和确认序号x+1。服务器进入SYN_RCVD状态,等待客户端的确认。
第三次握手:客户端收到SYN-ACK后,检查确认序号是否正确(x+1),如果正确则发送ACK报文段。ACK标志置1,确认序号为y+1。客户端进入ESTABLISHED状态,可以发送数据了。服务器收到ACK后,也进入ESTABLISHED状态,连接建立完成。
为什么需要三次握手?两次不行吗?两次握手的问题是:假设客户端发送的SYN在网络中滞留,超时后客户端重传SYN并建立连接。然后连接释放,但滞留的SYN到达服务器,服务器误以为客户端想建立新连接,分配资源并发送SYN-ACK。如果是两次握手,服务器认为连接已建立,但客户端并没有请求建立连接,服务器资源被浪费。三次握手可以防止这种情况:服务器发送SYN-ACK后,客户端不会确认,连接无法建立。
四次挥手释放连接
TCP连接是全双工的,释放连接需要双方分别关闭发送方向。连接释放需要四个步骤,因此称为四次挥手。
- 客户端发送FIN报文段,FIN标志置1,序号为u。客户端进入FIN_WAIT_1状态,表示没有数据要发送了。
- 服务器收到FIN后发送ACK,确认序号为u+1。此时连接处于半关闭状态:客户端不能发送数据,但服务器可以发送数据,客户端仍然接收数据。客户端进入FIN_WAIT_2状态。
- 服务器发送完所有数据后,发送FIN报文段,FIN标志置1,序号为v,确认序号为u+1。服务器进入LAST_ACK状态。
- 客户端收到FIN后发送ACK,确认序号为v+1。客户端进入TIME_WAIT状态,等待2MSL(最大报文段生存时间)后进入CLOSED状态。服务器收到ACK后进入CLOSED状态。
为什么客户端需要等待2MSL?原因有二:确保最后一个ACK能够到达服务器(如果丢失,服务器会重传FIN),确保当前连接的所有报文段都从网络中消失(不影响新连接)。
TCP可靠传输机制
TCP的可靠传输通过多个机制实现:
序号与确认序号:TCP对每个字节编号,接收方通过确认序号告诉发送方期望收到的下一个字节序号。
累积确认:确认序号n表示序号n及之前的所有字节都已正确接收。
重传:发送方发送数据后启动定时器,如果超时前没有收到确认,则重传该数据。TCP使用自适应的超时时间计算,根据最近的RTT动态调整。
快速重传:如果发送方收到3个重复的ACK(说明某个数据段丢失,但后续数据段到达),立即重传丢失的数据段,不必等待超时。
流量控制:通过接收窗口防止发送方淹没接收方。接收方在ACK中通告接收窗口大小,发送方的发送窗口不能超过接收窗口。
八、TCP流量控制
流量控制的目的是防止发送方发送太快,淹没接收方。TCP使用滑动窗口机制实现流量控制。
滑动窗口机制
接收方维护一个接收缓冲区,接收窗口是缓冲区中可用空间的大小。接收方在每个ACK中通告接收窗口大小。
发送方维护一个发送窗口,发送窗口大小 = min(拥塞窗口, 接收窗口)。发送方可以连续发送窗口内的数据,但必须保证未确认的数据不超过发送窗口。
当接收方缓冲区快满时,接收窗口变小,发送窗口也相应变小,发送速度降低。当接收方缓冲区有空间时,接收窗口变大,发送窗口也相应变大,发送速度提高。
零窗口问题
当接收方缓冲区满时,接收窗口为0,发送方停止发送。发送方定期发送零窗口探测报文段,询问接收方窗口是否变大。接收方返回ACK,包含新的接收窗口大小。
如果探测报文丢失,双方可能死锁:发送方等待接收窗口变大,接收方等待发送方发送数据(以便应用层读取数据,释放缓冲区)。零窗口探测机制可以防止这种死锁。
九、TCP拥塞控制
拥塞控制的目的是防止过多的数据注入网络,造成网络拥塞(路由器缓冲区溢出,导致丢包)。TCP通过拥塞窗口控制发送速率。
拥塞控制是一个全局问题,涉及网络中所有主机、路由器、链路。与流量控制不同,流量控制是点对点的问题,只涉及发送方和接收方。
拥塞控制的四个阶段
flowchart TB subgraph TCP[拥塞窗口变化曲线] direction LR subgraph SlowStart[慢启动阶段] direction TB SS1[cwnd=1 MSS<br/>指数增长] SS2[cwnd=2 MSS] SS3[cwnd=4 MSS] SS4[cwnd=8 MSS<br/>达到ssthresh] end subgraph CA[拥塞避免阶段] direction TB CA1[cwnd=ssthresh<br/>线性增长] CA2[cwnd=ssthresh+1 MSS] CA3[cwnd=ssthresh+2 MSS<br/>检测到丢包] end subgraph FastRetransmit[快重传/快恢复] direction TB FR1[收到3个重复ACK<br/>cwnd减半] FR2[进入快恢复<br/>cwnd线性增长] end end SlowStart --> CA CA --> FastRetransmit FastRetransmit --> CA style SlowStart fill:#c8e6c9 style CA fill:#fff9c4 style FastRetransmit fill:#ffcdd2
图表讲解:这张图展示了TCP拥塞控制的四个阶段——这是TCP性能优化的核心机制。
慢启动阶段
连接建立时,拥塞窗口cwnd初始化为1MSS(最大报文段长度),慢启动阈值ssthresh初始化为较大值(如64KB)。
在慢启动阶段,每收到一个ACK,cwnd增加1MSS。这意味着每经过一个RTT,cwnd翻倍(指数增长)。慢启动的名称是反讽的,实际上增长很快!
当cwnd达到ssthresh时,进入拥塞避免阶段。如果在慢启动阶段检测到拥塞(超时),ssthresh设置为cwnd/2,cwnd重置为1MSS,重新开始慢启动。
拥塞避免阶段
在拥塞避免阶段,每经过一个RTT,cwnd增加1MSS(线性增长)。增长速度比慢启动慢很多,这是为了谨慎地探测网络容量。
如果检测到拥塞(超时),ssthresh设置为cwnd/2,cwnd重置为1MSS,重新开始慢启动。
快重传与快恢复
如果发送方收到3个重复的ACK(说明某个数据段丢失,但后续数据段到达,网络可能没有严重拥塞),执行快重传:立即重传丢失的数据段。
然后执行快恢复:ssthresh设置为cwnd/2,cwnd设置为ssthresh(而不是重置为1MSS),进入拥塞避免阶段。快恢复保留了cwnd的一部分,避免了从1MSS重新开始的性能损失。
拥塞控制算法的演进
TCP拥塞控制算法经历了多次演进:
Tahoe:最早的版本,超时或3个重复ACK都执行慢启动。
Reno:改进了Tahoe,3个重复ACK执行快恢复,只有超时才执行慢启动。
NewReno:改进了Reno,可以处理多个数据段丢失的情况。
Vegas:基于延迟的拥塞控制,通过RTT的变化检测拥塞,而不是等到丢包才反应。
CUBIC:Linux默认的拥塞控制算法,使用三次函数增长cwnd,适合高带宽延迟积网络。
BBR:Google提出的拥塞控制算法,基于带宽和RTT的测量,而不是丢包率,在高带宽延迟积网络中表现优异。
十、知识总结表
| 协议/机制 | 主要特点 | 适用场景 | 优缺点 |
|---|---|---|---|
| UDP | 无连接、不可靠、轻量级 | 实时应用、DNS查询、组播 | 优点:简单高效、低延迟 缺点:不可靠 |
| TCP | 面向连接、可靠传输 | Web、邮件、文件传输 | 优点:可靠、有序 缺点:开销大 |
| 停-等协议 | 简单、利用率低 | 理论教学 | 优点:简单 缺点:效率极低 |
| GBN | 累积确认、批量重传 | 一般场景 | 优点:比停-等高效 缺点:重传多 |
| SR | 选择重传、缓存乱序 | 高可靠需求 | 优点:效率高 缺点:复杂 |
| TCP慢启动 | 指数增长cwnd | 连接建立初期 | 优点:快速占用带宽 缺点:可能造成拥塞 |
| TCP拥塞避免 | 线性增长cwnd | 稳定传输阶段 | 优点:避免拥塞 缺点:收敛慢 |
常见问题解答
Q1:为什么需要传输层,网络层不够吗?
答:网络层(IP)提供主机到主机的通信,但应用进程运行在端系统上,需要进程到进程的通信。
传输层提供进程寻址(端口号)和复用/分解功能。
此外,网络层只提供不可靠的数据报服务,许多应用需要可靠的、有序的、面向连接的服务,传输层在网络层之上提供这些服务。传输层还可以提供流量控制和拥塞控制,这些是网络层没有的。
总之,传输层弥补了网络层服务和应用需求之间的差距。
Q2:TCP和UDP如何选择?
答:选择TCP还是UDP取决于应用需求。
如果需要可靠传输(如文件传输、邮件、Web),必须使用TCP,因为UDP不保证数据送达。如果可以容忍少量数据丢失但对延迟敏感(如视频会议、在线游戏),选择UDP,因为TCP的重传机制会增加延迟。
如果需要多播或广播(如IPTV),只能使用UDP,因为TCP不支持。如果实现简单性很重要(如DNS查询),选择UDP,因为TCP的连接建立和释放有开销。
在实际应用中,很多实时应用会在UDP之上实现可靠性机制,结合两者的优点。
Q3:为什么TCP连接建立需要三次握手,释放需要四次挥手?
答:连接建立需要三次握手是为了同步双方的初始序号,并防止失效的连接请求报文段突然又传送到服务器。
两次握手无法确认双方的接收能力都正常,可能导致资源浪费或连接混淆。
连接释放需要四次挥手是因为TCP是全双工通信,双方都需要关闭发送方向。当一方发送FIN时,表示没有数据要发送了,但可能还需要接收数据。只有双方都发送FIN并收到ACK后,连接才完全释放。
因此,释放连接需要四个步骤(FIN-ACK, FIN-ACK),而不是三个。
Q4:TCP如何保证数据的可靠传输?
答:TCP通过多个机制保证可靠传输。
序号和确认序号确保数据按序到达,接收方可以通过序号检测丢失和乱序。校验和检测数据传输中的错误。超时和重传机制处理数据丢失,发送方在超时前未收到确认则重传。
累积确认提高效率,一个ACK确认多个数据段。快速重传机制在收到3个重复ACK时立即重传,不必等待超时。流量控制通过滑动窗口防止发送方淹没接收方。
这些机制共同作用,确保数据无差错、不丢失、不重复、按序到达。
Q5:什么是TCP的粘包问题,如何解决?
答:TCP是面向字节流的,不保留应用层消息的边界。
多个小的消息可能被合并成一个TCP报文段发送(粘包),一个大的消息可能被拆分成多个TCP报文段发送。应用层需要处理消息边界问题。
解决方法有:固定长度消息(每个消息长度固定,不足则补空格)、分隔符(使用特殊字符如换行符分隔消息)、长度字段(消息头部包含消息长度,接收方按长度读取)。
使用应用层协议(如HTTP、Redis RESP协议)通常已经处理了粘包问题。在设计自定义协议时,需要明确指定消息边界机制。