我们即将进入计算机网络世界的"交通管理局"——传输层!系好安全带,我们这就把枯燥的协议变成刺激的冒险故事!
🚀 传输层服务
想象你正在网购,传输层就是那个神秘的物流中心。它拍着胸脯保证:"亲!您的数据包我们一定使命必达!"
但仔细看它的服务清单:
1. 快递分拣服务:不仅送快递,还要把不同形状的包裹(视频/文字/游戏数据)分类到正确的货架(应用程序)
2.特快专递与平邮选择:着急的用"顺丰加急"(TCP可靠传输),不重要的用"普通包裹"(UDP尽最大努力)
3.破损包赔服务:发现包裹损坏立即要求重发(丢包重传)
4.智能限流系统:根据收件人仓库容量调整发货速度(流量控制)
传输层的核心作用
- 为应用层提供端到端的逻辑通信服务
- 与网络层的区别(网络层:主机到主机;传输层:进程到进程)
- 屏蔽底层网络细节,提供可靠/不可靠传输
突然,快递员小明抱着箱子冲进来:"经理!收件人仓库爆仓了!" 这就引出了...
🎭多路复用与解复用
想象你在大学食堂,所有窗口都喊着:"9527号!你的麻辣香锅好了!" 这里的玄机是:
- 多路复用:食堂阿姨把30份外卖装进1辆餐车(把多个应用数据打包进1个网络层数据包)
- 解复用:每个宿舍楼阿姨根据外卖单号分拣(通过端口号区分不同应用程序)
多路复用(Multiplexing)
- 定义:在发送端,传输层将来自多个应用进程的数据合并到一个网络层(IP)数据报中,通过同一网络连接发送。
- 目的:提高网络资源利用率,避免为每个应用进程单独建立物理连接。
- 实现方式:通过源端口号标识不同应用进程的数据。
多路分解(Demultiplexing)
- 定义:在接收端,传输层根据报文头部信息(如端口号)将数据正确分发到目标应用进程。
- 目的:确保数据准确送达目标进程,避免混淆。
- 实现方式:通过目的端口号匹配目标进程。
实现机制
1. 关键标识符
传输层通过以下两个字段实现复用与分解:
- 端口号(Port):16位整数(0~65535),唯一标识主机上的应用进程。
- 知名端口:0~1023(如HTTP:80、HTTPS:443)。
- 注册端口:1024~49151(如MySQL:3306)。
- 动态端口:49152~65535(临时分配给客户端进程)。
- IP地址:区分不同主机。
2. 多路复用的过程(发送端)
- 应用进程通过套接字(Socket API)(见后文详细介绍套接字)发送数据。
- 传输层为数据添加源端口号(标识发送进程)和目的端口号(标识接收进程)。
- 多个套接字的数据被合并到同一网络层数据报中发送。
示例:
- 主机A同时运行浏览器(端口1234)和邮件客户端(端口5678),两者通过同一网络接口发送数据。
- 传输层将两者的数据分别标记为
源端口:1234
和源端口:5678
,合并后通过IP层发送。
📮 第3站:UDP协议
认识一下UDP先生,他的工作信条是:"我送了,收到算你幸运,没收到的...随缘吧~"
- 工作日常:不签合同(无连接)、不记客户信息、包裹扔到门口就跑
- 必杀技三连:DNS查询("百度的IP是多少?")、视频会议(丢几帧无所谓)、在线游戏(延迟比准确重要)
UDP优点
UDP报文段格式
UDP报文段由8字节固定头部和数据部分组成,结构如下
字段说明:
- 源端口号(Source Port):发送方端口(可选,全0表示无端口)。
- 目的端口号(Destination Port):接收方端口(必需)。
- 长度(Length):整个UDP报文的总字节数(最小为8,即仅有头部)。
- 校验和(Checksum):用于差错检测(IPv4可选,IPv6强制启用)。
检验和
UDP的可靠传输方式
当某应用程序运行在 UDP 上时,该应用程序可能得到可靠数据传输吗?如果能,如何实现?
虽然UDP本身是一种无连接的、不可靠的传输层协议,但可以通过在应用层实现额外的机制来提供可靠数据传输。以下是常见的实现方法:
1. 校验和与差错检测
在UDP数据包中添加校验和字段,接收方通过校验和验证数据完整性。若校验失败,可请求重传
2. 序列号与确认机制(ACK)
-
序列号:为每个数据包分配唯一序列号,确保数据有序性和去重
-
确认应答:接收方发送ACK确认已接收的数据包,发送方未收到ACK则触发重传
3. 超时重传
发送方设置计时器,若超时未收到ACK,则重传数据包
4. 流量控制与拥塞控制
-
滑动窗口:动态调整发送速率,避免接收方缓冲区溢出
-
拥塞控制:根据网络状况调整发送速率(如模仿TCP的拥塞算法)
5. 选择性重传
仅重传丢失或损坏的数据包,而非全部数据,减少带宽浪费
6. 数据分片与重组
将大数据分片传输,接收端按序列号重组,确保完整性和顺序
🛡️ 第4站:可靠传输原理RDT
这套机制就像一个强迫症快递系统:
1. 编号强迫症:每个包裹贴唯一序号(就像你给女朋友每天的情书编号)
2. 确认强迫症:必须收到"已签收"短信才发下一个(累计确认)
3. 定时强迫症:每发一个包裹就启动沙漏(超时重传)
4. 滑动窗口魔法:允许连续发N个包裹不用等确认(就像自动扶梯不断滚动)
可靠数据传输(Reliable Data Transfer, RDT)是网络协议设计的核心问题,其核心目标是在不可靠的信道上实现数据的无差错、无丢失、无重复、按序交付。
一、RDT的核心挑战
-
信道不可靠性:
-
比特差错(Bit Error)
-
分组丢失(Packet Loss)
-
分组乱序(Out-of-Order)
-
分组重复(Duplication)
-
-
关键需求:
-
无差错:通过校验和(Checksum)检测错误。
-
完整性:通过确认(ACK)和重传(Retransmission)保证数据不丢失。
-
有序性:通过序号(Sequence Number)避免乱序和重复。
-
二、RDT协议演进
1. RDT 1.0:理想信道(无差错)
- 假设:信道完全可靠(无差错、无丢失)。
- 行为:
- 发送方直接发送数据。
- 接收方直接接收,无需反馈。
- 问题:现实中不存在理想信道。
2. RDT 2.0:比特差错信道
- 新增机制:
- 差错检测:校验和(Checksum)。
- 确认与重传:
- 接收方返回ACK(确认)或NAK(否定确认)。
- 发送方收到NAK后重传分组。
- 缺陷:
- ACK/NAK可能损坏,导致发送方无法判断是否需重传。
- 解决方案:RDT 2.1引入序号(0和1)。
3. RDT 2.1:处理ACK/NAK损坏
- 改进:
- 为分组添加1比特序号(0或1)。
- 接收方通过序号判断是否为重复分组:
- 若收到重复ACK/NAK,忽略或重发确认。
- 行为示例:
- 发送方发送分组0,等待ACK0。
- 若收到ACK0损坏,超时后重传分组0。
- 接收方发现重复分组0,丢弃并重发ACK0。
4. RDT 2.2:无NAK的确认
- 优化:
- 取消NAK,仅用ACK。
- 接收方通过带序号的ACK(如ACK0、ACK1)隐式表示是否需重传。
- 例如:收到分组0后返回ACK0;若期望分组1却收到重复分组0,则再次返回ACK0。
5. RDT 3.0:丢包信道(超时重传)
- 新增机制:
- 超时计时器(Timeout Timer):
- 发送方在发送分组后启动计时器。
- 若超时未收到ACK,重传分组。
- 序号扩展:仍用1比特序号(0和1交替)。
- 超时计时器(Timeout Timer):
- 关键场景:
- 分组丢失:超时后重传。
- ACK丢失:超时后重传,接收方通过序号去重。
- 缺陷:
- 停等协议(Stop-and-Wait)效率低(每次只能发一个分组)。
三.流水线可靠数据传输(Pipelined RDT)
流水线可靠数据传输(Pipelined RDT)是通过允许发送方连续发送多个分组而不需等待确认,显著提升信道利用率的机制。其核心思想是用滑动窗口协议替代停等协议(Stop-and-Wait)。
流水线RDT的两种实现方式
1. 回退N步(Go-Back-N, GBN)
-
核心规则:
- 发送方:
- 维护一个大小为N的窗口,窗口内分组可连续发送。
- 若超时未收到ACK,重传所有未确认的分组(从最早未确认的分组开始)。
- 接收方:
- 只按序接收分组,丢弃乱序分组。
- 返回累计ACK(如ACK3表示分组0-3已正确接收)。
- 发送方:
-
示例场景:
- 窗口大小N=4,发送分组0-3。
- 分组1丢失,接收方丢弃分组2-3,持续发送ACK0。
- 发送方超时后重传分组1-3。
-
缺点:
- 单个分组丢失可能导致大量冗余重传(效率低)。
2. 选择重传(Selective Repeat, SR)
-
核心规则:
- 发送方:
- 为每个分组单独维护计时器,仅重传超时的分组。
- 接收方:
- 缓存乱序到达的分组,返回独立ACK(如ACK0、ACK2)。
- 当窗口内所有分组按序到达后,一并交付上层。
- 发送方:
-
示例场景:
- 窗口大小N=4,发送分组0-3。
- 分组1丢失,但分组2-3被接收方缓存并返回ACK2、ACK3。
- 发送方仅重传分组1,接收方收齐后交付分组0-3。
-
优点:
- 仅重传丢失分组,效率高。
-
挑战:
- 需维护多个计时器,接收方需缓存乱序分组(实现复杂)。
机制 | Go-Back-N (GBN) | Selective Repeat (SR) |
---|---|---|
ACK类型 | 累计ACK(ACKn表示0-n已接收) | 独立ACK(每个分组单独确认) |
重传范围 | 丢失分组及其后所有分组 | 仅丢失分组 |
接收方行为 | 丢弃乱序分组 | 缓存乱序分组 |
实现复杂度 | 简单 | 复杂(需缓存和多个计时器) |
适用场景 | 低丢包率环境 | 高丢包率或长RTT环境 |
窗口大小与序号空间
-
序号空间要求:
-
GBN:序号空间 ≥ 窗口大小N(避免新旧分组序号混淆)。
-
SR:序号空间 ≥ 2×窗口大小N(防止ACK延迟导致歧义)。
-
例如:若N=4,序号至少需要8(0-7)。
-
-
-
为什么SR需要更大序号空间?
-
若序号空间=窗口大小,可能因ACK延迟导致发送方误判旧分组为新分组(参见“窗口同步问题”)。
-
流水线RDT与TCP的关系
TCP的可靠传输机制结合了GBN和SR的思想:
- 累计ACK:类似GBN,ACK号表示期望的下一个字节序号。
- 选择重传:通过SACK(Selective ACK)选项实现部分SR功能(缓存乱序数据)。
- 动态窗口:通过拥塞控制算法(如Reno、Cubic)调整窗口大小。
🚂 第5站:TCP协议
TCP就像一列严谨的高铁:
- 购票实名制:三次握手建立连接(客户端:"能买票吗?" 服务端:"可以,请付款" 客户端:"已付款")
- 车厢结构:每个数据包都像标准集装箱(20字节固定头部+数据)
- 智能调速:根据铁轨状况自动加速/减速(滑动窗口动态调整)
- 优雅告别:四次挥手断开连接(像极了情侣分手时的"你是个好人..."循环)
TCP(Transmission Control Protocol)是互联网核心协议之一,位于传输层,提供面向连接的、可靠的、基于字节流的数据传输服务。
一、TCP的核心特性
- 面向连接
- 通信前需通过三次握手建立连接,结束后通过四次挥手释放连接。
- 可靠传输
- 通过序号、确认、重传、流量控制、拥塞控制确保数据无差错、不丢失、不重复、按序到达。
- 全双工通信
- 双方均可同时发送和接收数据。
- 基于字节流
- 数据被视为无结构的字节序列,由TCP分段为报文段(Segment)传输。
- 流量控制与拥塞控制
- 动态调整发送速率,避免接收方溢出或网络拥塞。
二.TCP报文段结构
三.TCP连接
1.三次握手
为什么需要三次握手?
- 防止历史重复连接初始化导致的资源浪费。
- 同步双方的初始序列号(ISN)。
2.四次挥手
四、TCP可靠传输机制
1. 序号与确认机制
-
序列号:每个字节唯一编号,报文段携带第一个字节的序号。
-
确认号(ACK):接收方返回
ACK=y+1
表示已正确接收序号y
及之前的所有数据。
2. 超时重传
-
RTO(Retransmission Timeout):动态计算超时时间(基于RTT)。
-
快速重传:收到3个重复ACK时立即重传(无需等待超时)。
3. 滑动窗口协议
- 发送窗口:允许未确认的字节范围(受接收方窗口和拥塞窗口限制)。
- 接收窗口:通告剩余缓冲区大小(流量控制)。
五、流量控制与拥塞控制
1. 流量控制
-
通过接收窗口(rwnd)限制发送速率,避免接收方缓冲区溢出。
-
零窗口探测:当
rwnd=0
时,发送方定期发送探测报文。
2. 拥塞控制
-
慢启动(Slow Start):窗口指数增长(直到阈值
ssthresh
)。 -
拥塞避免(AIMD):窗口线性增长,发生拥塞时减半。
-
快速恢复:针对丢包类型(超时或冗余ACK)调整策略。
六、TCP的优缺点
优点
-
可靠性高,适合文件传输、网页浏览等场景。
-
动态适应网络状况(拥塞控制)。
缺点
-
首部开销大(至少20字节)。
-
建立/释放连接耗时(握手和挥手延迟)。
-
不适合实时应用(如视频通话,推荐用UDP+QUIC)。
🚧 第6站:拥塞控制
想象十一黄金周的高速公路:
- 慢启动策略:新车刚上路先试探性加速(窗口大小指数增长)
- 拥塞避免模式:当接近堵车临界点时改为谨慎加速(线性增长)
- 快速重传机制:连续收到3个"前方事故"警报立即减速(收到3个重复ACK)
- 快速恢复模式:事故解除后恢复限速行驶(不回归到初始速度)
拥塞控制是TCP的核心机制之一,用于**动态调整发送速率**,避免网络因流量过载而崩溃。其目标是在**高吞吐量**和**低延迟**之间取得平衡,同时保证**公平性**(多流共享带宽)。
一、拥塞控制的基本原理
1. 为什么需要拥塞控制?
• 网络拥塞的表现:
• 路由器缓冲区溢出 → 丢包
• 排队延迟增加 → 高延迟
• 吞吐量下降
• 根本原因:
发送方的总发送速率超过网络的承载能力(带宽+缓冲区)。
2. 拥塞控制的实现方式
TCP通过调整**拥塞窗口(cwnd, Congestion Window)**控制发送速率:
• **cwnd**:发送方在未收到ACK前允许发送的最大数据量(单位:字节或报文段)。
• **实际发送窗口** = `min(cwnd, 接收方通告窗口rwnd)`。
## **二、经典拥塞控制算法**
1. Tahoe(1988)
• **核心机制**:
• **慢启动(Slow Start)**:cwnd指数增长(`cwnd *= 2`每RTT)。
• **拥塞避免(Congestion Avoidance)**:cwnd线性增长(`cwnd += 1`每RTT)。
• **检测到拥塞时**:
◦ 丢包(超时或冗余ACK) → 将`cwnd`重置为1,重新慢启动。
◦ 设置`ssthresh`(慢启动阈值)= `cwnd/2`。
• **问题**:
超时后激进降为1,导致吞吐量骤降。
### **2. Reno(1990)**
• **改进点**:
• **快速重传(Fast Retransmit)**:收到3个重复ACK时立即重传丢失报文。
• **快速恢复(Fast Recovery)**:
◦ 重传后不重置`cwnd`,而是设为`ssthresh + 3`(补偿冗余ACK)。
◦ 进入拥塞避免阶段(线性增长)。
• **算法流程**:
```text
1. 慢启动:cwnd指数增长,直到cwnd >= ssthresh。
2. 拥塞避免:cwnd线性增长。
3. 快速重传:收到3个重复ACK时重传,不等待超时。
4. 快速恢复:cwnd = ssthresh + 3,继续传输。
5. 超时:cwnd = 1,ssthresh = cwnd/2,重新慢启动。
```
• **局限性**:
多个报文丢失时可能失效(需触发超时)。
### **3. NewReno(1999)**
• **改进点**:
• 在快速恢复阶段**记录部分ACK**(Partial ACK),避免多次丢包导致多次重传。
• 仅当所有丢失报文被确认后,才退出快速恢复。
### **4. CUBIC(2008,Linux默认)**
• **核心思想**:
用**立方函数**替代线性增长,更公平且适应高带宽延迟积(BDP)网络。
• `cwnd = C × (t - K)^3 + W_max`
◦ `t`:距离上次拥塞事件的时间。
◦ `K`:计算得到的稳定点。
• **特点**:
◦ 对长距离高速网络(如10Gbps)更高效。
◦ 减少RTT不公平性(传统AIMD对短RTT流更有利)。
三、拥塞控制的关键机制
1. 慢启动(Slow Start)
• **目的**:快速探测可用带宽。
• **规则**:
• 初始`cwnd = 1 MSS`(最大报文段大小)。
• 每收到一个ACK,`cwnd += 1 MSS` → **每RTT增长一倍**。
• 当`cwnd >= ssthresh`时,转入拥塞避免阶段。
• **触发场景**:
• 连接建立时。
• 超时重传后。
2. 拥塞避免(AIMD)
• **目的**:避免激进增长导致拥塞。
• **规则**:
• 每RTT `cwnd += 1 MSS`(线性增长)。
• 检测到拥塞时:
◦ `ssthresh = max(cwnd/2, 2)`
◦ `cwnd = ssthresh`(Reno)或`cwnd = 1`(Tahoe)。
3. 快速重传与快速恢复
• **快速重传**:
• 收到3个重复ACK时,立即重传丢失报文(无需等待超时)。
• **快速恢复**:
• 重传后`cwnd = ssthresh + 3`(补偿冗余ACK)。
• 继续发送新数据(避免空窗期)。
4. 超时处理
• **触发条件**:
报文段超时未确认(RTO计时器到期)。
• **行为**:
• `cwnd = 1 MSS`(回到慢启动)。
• `ssthresh = cwnd/2`。
四、拥塞控制的衡量指标
1. **吞吐量(Throughput)**:
• 实际有效数据传输速率。
• 理想吞吐量 ≈ `cwnd / RTT`。
2. **公平性(Fairness)**:
• 多流共享带宽时是否均衡(如CUBIC比Reno更公平)。
3. **收敛速度**:
• 从拥塞中恢复的快慢(如BBR比CUBIC更快)。
📕第7站:传输层与网络层
🎮 简单挑战:来当TCP指挥官!
🎮 终极挑战01:来当15秒TCP指挥官!
🎮 终极挑战02:来当15秒TCP指挥官!
看完这些,是不是觉得传输层就像网络世界的交通管理局+快递公司+交警大队综合体?下次当你视频卡顿时,可以中二地喊一句:"一定是TCP的拥塞窗口又缩小了!" 保证周围人向你投来"这人一定很懂电脑"的崇拜目光~ 🚦📦💻
编程实践
套接字(Socket)
套接字的核心作用
进程间通信:通过IP地址 + 端口号唯一标识网络中的进程。
协议选择:支持TCP(可靠流式传输)或UDP(不可靠数据报传输)。
资源管理:操作系统通过套接字描述符管理连接、缓冲区等资源。
套接字的工作流程
TCP套接字 | UDP套接字 |
---|---|
1. 创建Socket (socket() ) | 1. 创建Socket (socket() ) |
2. 绑定端口 (bind() ) | 2. 绑定端口 (bind() ) |
3. 监听连接 (listen() ) | 3. 直接收发数据 (sendto() /recvfrom() ) |
4. 接受连接 (accept() ) | |
5. 数据读写 (send() /recv() ) | |
6. 关闭连接 (close() ) | 4. 关闭套接字 (close() ) |
关键函数与参数
函数 | 作用 | 关键参数 |
---|---|---|
socket() | 创建套接字 | domain (如AF_INET)、type (如SOCK_STREAM/SOCK_DGRAM) |
bind() | 绑定IP和端口 | sockaddr_in 结构体(含IP/端口) |
listen() | 设置TCP服务端监听队列长度 | backlog (最大等待连接数) |
connect() | TCP客户端发起连接 | 服务端地址和端口 |
accept() | TCP服务端接受连接 | 返回新套接字描述符 |
send() /recv() | TCP数据收发(面向连接) | 缓冲区指针、长度标志 |
sendto() /recvfrom() | UDP数据收发(无连接) | 目标地址结构体 |
四元组(Quadruplet)
1. 定义
四元组是唯一标识一个网络连接的四个关键参数:
-
源IP地址:发送方的IP地址。
-
源端口号:发送方的端口号。
-
目的IP地址:接收方的IP地址。
-
目的端口号:接收方的端口号。
2. 作用
-
唯一性:四元组组合可以唯一确定一个网络连接(如TCP连接或UDP数据流)。
-
多路复用:传输层通过四元组将数据正确分发到目标进程(如一台服务器的多个HTTP连接)。
-
NAT/防火墙:设备(如路由器)通过四元组映射管理内外网通信。
3. 示例
-
一个TCP连接的四元组:
[源IP:192.168.1.2, 源端口:54321, 目的IP:93.184.216.34, 目的端口:80]
表示从本地主机访问example.com
的HTTP服务。
4.题目
四元组在套接字中的应用
1. TCP套接字示例
// 服务端绑定四元组中的源IP和端口
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {.sin_port = htons(8080), .sin_addr.s_addr = INADDR_ANY};
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
// 客户端连接后形成完整四元组
connect(client_fd, "93.184.216.34", 80); // 四元组: [客户端IP:端口, 服务端IP:80]
2. UDP套接字示例
# UDP客户端发送数据时动态指定目的IP和端口
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(b"Hello", ("93.184.216.34", 53)) # 四元组临时形成