TCP和UDP
期末月叠加面试真是嘎嘎上压力,周二快手一面,呜呜呜,我要当手孝子。
我们一般说网络有七层,是指OSI模型,从下到上依次为:
- 物理层 (Physical Layer)
- 数据链路层 (Data Link Layer)
- 网络层 (Network Layer)
- 传输层 (Transport Layer)
- 会话层 (Session Layer)
- 表示层 (Presentation Layer)
- 应用层 (Application Layer)
而实际上我们采用的都是TCP/IP模型,只有四层(有时也说五层)
从下到上依次为:
- 网络接口层,也叫链路层
- 网络层
- 传输层
- 应用层
五层模型将网络接口层拆开,分为物理层、数据链路层
而TCP和UDP都是两种常见的传输层协议
TCP:面向连接的,可靠的,基于字节流的传输层通信协议
- tcp是一种面向连接的协议,在通信之前要在发送方和接收方之间建立连接,然后通信结束后关闭连接。建立和关闭的过程被称为三次握手和四次挥手,确保数据的可靠传输。
- 可靠性:它使用序列号和确认机制来确保数据包的有序性和完整性,如果数据包丢失或者损坏,tcp会重新发送丢失的数据包,直到接收方正确接收为止。
- 流量控制和拥塞控制:TCP采用流量控制和拥塞控制算法,确保数据发送的速率不会超过接收方的接收能力,并防止网络拥塞
- 有序传输:TCP确保数据包按照发送的顺序到达,并在接收方重新组装成正确的顺序
- 适用于可靠传输的场景:文件传输,电子邮件,网页浏览
UDP:无连接的,不可靠的,基于数据报的传输层通信协议
- UDP是一种无连接的协议,与tcp不同,UDP在通信之前不需要建立连接,直接发送数据包,使得UDP比TCP更加轻量级
- 不可靠性:UDP不提供可靠的数据传输。它发送数据包之后不会关心数据包是否会到达接收方,因此,如果数据包丢失或者损坏,UDP不会重新发送,也不会提供确认机制
- 速度较快:没有连接建立和确认过程,UDP传输速度较快,适用于实时传输,如实时音频,和视频流
- 无序传输:UDP不保证数据包的有序性,因此接收方接收到的数据包是无序的
- 适用于实时传输的场景:UDP适用于对数据传输可靠性不高的场景,如实时游戏、流媒体等,其中实时性比准确性更重要
两者的区别
连接性:
- TCP:面向连接,通信前需建立三次握手,确保可靠连接。
- UDP:无连接,直接发送数据,无需建立连接。
可靠性:
- TCP:提供可靠传输,通过序列号、确认机制、重传机制和流量控制确保数据无丢失、无差错、按序到达。
- UDP:不可靠,无确认机制,数据可能丢失、乱序或重复。
传输效率:
- TCP:因有连接建立、确认和重传等机制,效率较低,延迟较高。
- UDP:无额外开销,效率高,延迟低,适合实时传输。
数据传输方式:
- TCP:基于字节流,无消息边界,数据按流传输。
- UDP:基于数据报,保留消息边界,每个数据报独立。
适用场景:
- TCP:适合需要高可靠性的场景,如 HTTP、FTP、SMTP(网页浏览、文件传输、邮件)。
- UDP:适合实时性要求高、允许少量丢包的场景,如视频流、语音通话(VoIP)、DNS 查询、游戏。
服务形式:
- TCP提供一对一的全双工通信,确保数据按序、无差错、无丢失地传输。
- UDP支持一对一、一对多(多播)、多对多通信,适合广播或多播场景。
分片方式:
TCP
- 数据以字节流形式传输,无明确的消息边界。
- 分片由 TCP 协议栈自动处理,将数据分割为适合的段(Segment),每个段包含序列号以确保按序重组。
- 分片大小根据最大段大小(MSS, Maximum Segment Size)动态调整,通常由网络 MTU(最大传输单元)决定。
- 接收端会根据序列号重组数据流,处理乱序或丢失的段。
UDP
- 数据以数据报形式传输,每个数据报独立,保留消息边界。
- 分片由应用层或网络层(IP层)处理,UDP 本身不对数据报分片。
- 如果 UDP 数据报大小超过 MTU,IP 层会进行分片,接收端由 IP 层重组。
- UDP 不负责乱序或丢失的数据报重组,应用层需自行处理。
TCP的重传机制是如何实现的
在TCP通信中,每个发送的字节都有唯一的序号,而每一个接收的字节都有一个ack确认号。发送方维护了一个发送窗口,接收方维护了一个接收窗口,发送方发送完数据后等待接收方的确认。
数据发送失败后有两个重传机制,一个是超时重传,一个是快重传。
超时重传
发送方为每一个发送的数据设置一个定时器,这个定时器的时长称为超时时间(RTO,是根据往返时间RTT确定的)。发送方假设在这个RTO时间内,数据能够到达接收方并得到确认,如果在超时时间内没有收到确认ack信号,发送方就认为数据丢包或者损坏,触发超时重传。超时后,发送方重传未确认的最早数据段,并调整拥塞窗口(通常降为1 MSS,进入慢启动)。
缺点:
- RTO较长,延迟高。
- 进入慢启动
适用于连续丢包或者网络严重拥塞
快重传
接收方收到乱序数据段(Out-of-Order),重复发送最近确认的ACK(即期望的下一个字节序号)。
发送方收到3个相同ACK,推测该ACK后的数据段丢失,立即重传。
快重传常与快恢复(Fast Recovery)结合:
- 拥塞窗口减半(cwnd=cwnd/2+3⋅MSS),进入快速恢复而非慢启动。
优点:
- 比慢重传快,减少不必要的等待。
- 结合快速恢复,拥塞控制更平滑。
缺点:
- 需足够重复ACK(至少3个),对大量丢包或尾部丢包效果差。
- 可能误判(重复ACK可能由重排序引起)。
流量控制
流量控制的核心机制:滑动窗口
- 定义:TCP使用滑动窗口协议(Sliding Window Protocol)实现流量控制。接收方通过通告窗口大小(Advertised Window,简称rwnd)限制发送方的发送速率。
- 原理:
- 接收方在TCP报头中携带窗口大小字段(16位,单位字节),告知发送方其接收缓冲区剩余容量。
- 发送方根据接收方的窗口大小(rwnd)和拥塞窗口(cwnd)选择实际发送窗口:Effective Window=min(rwnd,cwnd)Effective Window=min(rwnd,cwnd)
- 发送方只能发送未确认数据和窗口允许的数据量,确保不超过接收方处理能力。
- 滑动窗口运作:
- 窗口随ACK左移,释放已确认数据的空间。
- 接收方处理数据后,窗口右移,通告新的rwnd。
2. 实现步骤
- 接收方通告窗口
- 接收方在每次ACK中携带rwnd,反映其缓冲区可用空间。
- 初始rwnd由接收方缓冲区大小决定(典型为几KB到几十KB)。
- 发送方调整发送速率:
- 发送方根据rwnd动态调整未确认数据量(in-flight data)。
- 若rwnd=0,发送方暂停发送,但可发送1字节探测包(Zero Window Probe)检查窗口是否恢复。
- 窗口更新:
- 接收方处理缓冲区数据后,发送新的ACK,更新rwnd。
- 若rwnd增大,发送方恢复或增加发送。
3. 关键特性
- 动态调整:
- rwnd随接收方缓冲区状态实时变化,适应应用层处理速度。
- 防止缓冲区溢出:
- 确保发送数据量不超过接收方缓冲区容量。
- 零窗口处理:
- 当rwnd=0,发送方等待窗口更新,定时发送探测包避免死锁。
- 与拥塞控制协同:
- 流量控制(基于rwnd)与拥塞控制(基于cwnd)结合,发送窗口取两者最小值。
TCP的拥塞控制
首先是慢启动阶段,初始阶段TCP会以较小的发送窗口开始传输数据,一般是2个或者3个四个MSS,随着每次成功收到ack信号,发送窗口每次变为上次的两倍,指数型增长,直到大于临界窗口之后,变为拥塞避免阶段,线性增长,每次增加一个MSS。
初始的临界窗口一般根据上次的拥塞点确定或者一个较大的值(16MSS)
超时重传之后,重新进入慢启动,这时临界窗口的大小是发生拥塞时未被确认的数据量的二分之一(近似为cwnd的一半),但不能小于2个MSS
而快重传,临界窗口也被设置为发生拥塞时未被确认的数据量的二分之一(近似为cwnd的一半),然后拥塞窗口设置为临界窗口加三个MSS。
什么是TCP粘包?
粘包是指在TCP传输中,多个应用层数据包在接收端被合并为一个数据块,导致无法直接区分原始数据包边界。TCP是面向流的协议,不保证数据包的独立性,发送端可能将多个小数据包合并,接收端可能将数据分段或合并处理。
粘包原因
发送端优化
:
- TCP的Nagle算法将小数据包合并,减少网络开销。
- 发送缓冲区可能一次性发送多个应用层数据。
接收端处理
:
- 接收缓冲区可能将多个数据包合并为一个读操作。
- 接收数据不按发送时的包边界分割。
网络传输
:
- 数据在网络中可能因时序或分段重组,导致粘包或分包。
粘包示例
- 发送端连续发送 “Hello” 和 “World”,接收端可能为 “HelloWorld”。
- 发送端发送 “Msg1|Msg2”,接收端可能将 “|Msg2” 分到下一次读操作。
如何解决粘包?
解决粘包需要在应用层定义协议,确保接收端能正确解析数据包边界。以下是几种常见方法:
1. 固定长度头部
- 方法:
- 每个数据包前加固定长度的头部(如4字节),表示数据包长度。
- 接收端先读取头部,解析长度,再读取指定长度的数据。
- 优点:简单,易实现。
- 缺点:头部长度限制最大数据包大小(如4字节限制4GB)。
- 示例:
- 发送格式:
[4字节长度][数据]
- 接收伪代码:length=read(4 bytes)data==read(length bytes)length=read(4 bytes)data==read(length bytes)
- 发送格式:
2. 分隔符
- 方法:
- 在数据包间插入特定分隔符(如 “\n” 或 “|”)。
- 接收端按分隔符分割数据。
- 优点:灵活,适合文本数据。
- 缺点:需确保数据内不含分隔符,或进行转义处理。
- 示例:
- 发送:”Hello\nWorld\n”
- 接收端按 “\n” 拆分。
3. 固定长度数据包
- 方法:
- 每个数据包固定长度,不足补填充(如空格或0)。
- 接收端按固定长度读取。
- 优点:实现简单,边界清晰。
- 缺点:浪费带宽,适合长度一致的场景。
- 示例:
- 每个包固定100字节,”Hello” 补95字节填充。
4. 消息格式化(如TLV或JSON)
- 方法:
- 使用结构化格式(如Tag-Length-Value或JSON)定义消息。
- 接收端按格式解析。
- 优点:支持复杂数据,扩展性强。
- 缺点:解析复杂度较高。
- 示例:
- JSON格式:
{"len":5,"data":"Hello"}
- JSON格式:
5. 应用层超时机制
- 方法:
- 在接收端设置超时,若一段时间未收到完整包,视为粘包或分包,触发重试或错误处理。
- 优点:辅助解决分包问题。
- 缺点:需调优超时时间,避免误判。