传输层
概述
概念:为运行在不同端系统上的 进程 提供了逻辑通信 (Logic Communication) (进程的概念详见操作系统)
注:网络层是为不同的 端系统 之间提供逻辑通信
单位:==报文段== (Segement)
注:UDP 协议是不对应用层交付的报文做任何处理的:所以也可以认为单位是报文
协议:
- UDP 协议:用户数据报协议
- TCP 协议:传输控制协议
应用层协议 传输层协议 SMTP TCP HTTP TCP Telnet TCP FTP TCP DNS UDP SMNP UDP DHCP UDP RIP UDP BGP TCP 功能
细节:
==端对端传输==
路由器 不会检查数据报的数据部分(传输层报文段) 仅检查数据报首部
解释:(1) 路由器仅实现到网络层是无法检测传输层的数据的 (2) 问题:传输层的数据出现问题 网络层是无法检测到 的
多路复用&多路分解
端口号:
定义:进程在网络通信时用于标识自己的编号
长度:16位比特 (最大端口数量 2^16^)
分类:
- 服务端:(1) 周知端口号 0~1023 (2) 登记端口号 1024 ~ 49151
- 客户端:客户端程序参与网络通信使用的编号 49152 ~ 65536
细节:
端口号不是进程号,多个进程可以同时使用一个端口号:使得多个进程的数据可以封装在同一个报文段中
解释:不同的操作系统分配进程标识符格式是不同的,为了保证统一性设计了端口号
应用层协议 FTP TELNET SMTP DNS HTTP SNMP RIP 端口号 21 23 25 53 80 161 520 套接字(Socket):用于标识端系统上具体某个进程
UDP 套接字:<目的
IP地址 , 目的端口号>TCP 套接字:<源
IP地址,源端口号,目的IP地址 , 目的端口号>不同源主机发送的报文段不会因为 相同的目的IP地址和端口号 被定向到同一主机的同一进程:因为源主机 IP 地址和端口号不同,建立的连接也不同
多路复用:传输层从不同的套接字中接收 报文 并为其添加首部形成 报文段 (不同的应用程序可以采用相同的传输层协议传输数据)
多路分解:(1) 传输层从网络层中接收 数据报 并拆封将 报文段 交付给正确的套接字 (2) 套接字最后把数据分发给相应的集成

传输层协议
UDP 协议
功能:(提供的是传输层 最低限度 的功能)
多路复用&多路分解
差错检测:校验和 (首部校验和不具有差错纠正的功能)
注:UDP 协议丢弃受损的报文段并且向上层发出警告:不具有差错的恢复能力
特点:
无连接:(1) 进程之间通信不需要提前建立连接 (2) 减少传输的时延,适用于实时通信
不提供可靠传输:报文段可能 受损 + 丢失 + 失序 + 重复到达
面向报文:不对应用层交付的报文作任何处理直接封装后放入数据报中
如果报文容量过大,会导致网络层需要对其进行分片
如果报文容量过小,会导致网络层数据报的首部相对较大
首部开销小:==8B==
支持==非一对一==通信
报文段格式
源端口号:服务器返回数据时可以直接从发送的报文段中提取源端口号作为目的端口号
目的端口号:
报文总长度:首部长度 + 数据长度
校验和
计算过程
(1) 源端口号 + 目的端口号 + 总长度 + 数据部分 + ==伪首部==(不包括校验和字段)
(2) 二进制求和后 取反码 得到校验和
(3) 如果求和过程出现 溢出,需要采用 ==回卷== 操作
伪首部:==源 IP 地址 + 目的 IP 地址 + 0 + 协议字段( UDP 协议是 17)+ UDP 报文长度== (伪首部只在计算校验和的时候出现,不会被封装进数据报中)
回卷:最高位溢出的结果 加到 最低位去
问题:
数据链路层采用 CRC 校验了,网络层采用首部校验和校验了,为什么传输层还要采用校验和校验?
(1) 数据链路层和网络层的校验不能够保证发现所有的错误 (2) 网络层向传输层交付数据时仍然可能发生错误
为什么传输层的校验和要使用伪首部
==伪首部检验 IP 地址== -> 网络层在拆卸 IP 数据报的过程中 IP 地址仍然是可能发生变化的,传输层需要防止网络层交付错误的 IP 地址
==伪首部检验网络层数据报是否交付错误== -> TCP/UDP 协议和 IP 协议完全可能不在同一个协议栈上,IP 数据报不一定是交付给 TCP/UDP 的
细节:

TCP 协议
基本概念
概念:面向连接(虚连接)的传输层协议
功能:
特点:
面向连接
- 连接仅建立在两个进程之间,是 点对点 的情况 (多播协议是不可能在 TCP 下实现的)
- 进程双方在通信时需要 提前建立连接 才能够发送数据
- 请求建立连接的进程称为 客户端 ,响应连接建立的进程称为 服务端
- TCP 连接是 虚连接:网络层之下都是不会感受到 TCP 连接的存在
可靠传输:保证报文段能够正确到达
全双工通信:进程双方可以同时发送数据和接收数据,接收和发送双方==都具有接收窗口和发送窗口==
面向字节流:将应用层的交付的数据 按照字节隐式地编号,每次将一定数量的字节封装成报文段发送
报文格式:
源端口号 目的端口号(2B)
序号(4B):每次发送的报文段中 ==第一个字节的编号== 就是序号
注:初始序号通常随机:(1) 避免分组重排序:防止滞留在网络中的分组在某个时刻到达导致传递不必要的信息 (2) 保证网络安全
确认号(4B):每次确认报文段中希望得到的 ==下一个字节的编号== (序号 + 数据长度 = 确认号)
首部长度(4bit):单位 ==4B==
窗口大小(2B):接收方将当前 缓存空间 中的 窗口大小 值放入 确认报文 中返回给发送方
控制位:
URG:(1)URG= 1 传输的数据是紧急数据时 (2) 紧急传输的数据不需要在 缓存空间 中排队ACK:(1)ACK= 1 连接建立过程中服务端回复确认报文段时 (2) 连接建立后所有报文段的 ACK 必须被置为 1 (无论报文段时确认报文段还是发送报文段)PSH:不需要在接收窗口中等待应用层的处理,直接交付给应用层处理RST:连接出现严重错误,需要重新建立连接SYN:(1)SYN= 1 客户端请求建立连接 & 服务器端同意建立连接FIN:(1)FIN= 1 客户端请求结束连接 & 服务器端同意结束连接
图示

功能
连接管理
概念:连接建立 + 连接维护 + 连接释放
前提:TCP 是全双工通信 -> ==有两条通信信道==
过程:
连接建立:服务器长期处于监听状态
客户端发送 SYN = 1
- 客户端进入 ==SYN_SENT 状态==
- 等待服务器响应;如果超时则利用重传机制,重传 SYN = 1
- 不携带任何数据,但是消耗序号
服务端响应 ACK = 1和 SYN = 1
服务器端进入 ==SYN_RCVD 状态==
等待客户端响应;如果超时则利用重传机制,重传 SYN = 1,ACK = 1
不携带任何数据,但是消耗序号
开始分配 ==TCP 缓存== 和 ==变量==
- 客户端响应 ACK = 1
- 客户端进入 ==ESTABLISHED 状态==
- 可以携带数据发送
- 开始分配 ==TCP 缓存== 和 ==变量== 4. 服务器端接收到响应进入 ==ESTABLISHED 状态==
* 细节问题
1. seq = x:x 的值是随机分配的
2. 为什么是三次握手,不是两次,四次或者更多次
* 非本质回答:资源浪费
客户端发送的连接建立请求可能在网络中滞留,导致延时到达服务器;
客户端选择重传连接建立请求,然后传输数据,最后关闭连接;
但是,之前延时到达的服务器的连接请求在关闭连接后到达了;
这时,服务器端就会认为客户端又开始请求建立连接了,就会发送响应 SYN = 1 ACK = 1;
如果是两次握手,导致连接建立,但是客户端并不想要建立连接,所以就不会回复,导致服务器端会长时间的等待直到超时,浪费资源
如果是三次握手,那么服务器端就会因为客户端不回复知道他不想建立连接,就不会一直等待
* 本质回答:==三次握手是理论保证可靠传输的最小值==
一次握手:客户端发送请求,显然他根本不知道服务器到底接收到没有,所以需要等待服务器的回复 -> 一次握手显然不可靠
两次握手:服务器端回复了客户端,那么服务器端也不清楚客户端到底接收到没有,所以需要再次等待客户端的回复-> 两次握手显然也不太可靠
三次握手:客户端回复了服务器,已经基本确认两端都是可以连通的,可靠连接可以基本建立了
四次握手:客户端也不清楚服务器端是否接收到自己的再次回复,所以再次等待服务器端的回复 -> 显然相对于三次可靠性更高了
....
实际上,我们可以设计非常多次的握手来保证可靠传输,但是三次就是理论上保证可靠传输的最小值
* 连接断开:客户端数据发送完毕
1. 客户端发送 FIN = 1
* 客户端进入 ==FIN-WAIT1状态==
* 客户端等待服务器的响应;超时则利用重传机制
* 不携带任何数据,消耗序号
2. 服务端响应客户端 ACK = 1
* 服务器端进入 ==CLOSE-WAIT 状态==
* 服务器允许客户端释放连接 -> <a style="color:red;">释放客户端到服务器端的单向连接</a>
* 客户端连接释放之后不可以再发送数据;服务器端仍然可以发送数据
* 不携带任何数据,消耗序号
<a style="color:red;">上述两次握手经过后 TCP 连接处于半关闭状态,这期间服务器端仍然可能向客户端发送数据</a>
3. 服务端发送 FIN = 1
* 服务器端进入 ==LAST-ACK状态==
* 服务器不再向客户端发送数据后准备关闭连接
* 服务器端等待客户端响应;超时则利用重传机制
4. 客户端响应 ACK = 1
* 服务器端进入 ==CLOSED状态==,客户端进入 ==TIME-WAIT状态==
* 服务器端到客户端的连接还没有释放
5. 客户端等待时间结束(2MSL)
* 客户端进入 ==CLOSED状态==
* 服务器端到客户端的连接彻底释放
6. TCP 连接关闭完成
* 细节问题
1. 为什么是四次挥手不是三次挥手?
* 连接建立实际上也可以看做四次握手,而是三次握手的原因,是因为 ACK + SYN 一起发送简化了过程
* 连接关闭时两者不可以一起发送,因为确认客户端到服务器端的连接关闭后,服务器端到客户端的连接并不会立刻关闭 -> 造成四次挥手
2. 为什么最后客户端需要等待一段时间之后才能关闭呢?
* 因为客户端响应的 ACK 很有可能产生丢失,导致服务器端到客户端连接并没有关闭,如果此时客户端先行关闭,会导致服务器挂起
* 所以客户端需要等待服务器先行关闭,自己再关闭,最后服务器端到客户端的连接才算彻底释放 -> TCP 关闭完成
3. 为什么一定要等待 2MSL 长度的时间呢?
* 报文段最长存活时间是 MSL
* ?
4. 如果服务器第一次响应客户端的报文段丢失,会出现什么情况呢?
图示:
- 连接建立

- 连接断开

SYN 洪泛攻击
- 概念:ddos 攻击的一种
- 过程:
- 攻击者发送 SYN = 1 的第一个报文段
- 服务端接收第一个报文段后回复给攻击者相应的确认报文段
- 攻击者此时不再回复服务器
- 服务器没有接收到回复会不断地发送确认报文段,希望得到攻击者的响应
- 服务器由于得不到攻击者的响应,连接挂起,最后因为超时而断开连接
- 后果:消耗 CPU 和 内存,导致服务器宕机
可靠传输

基本机制
前提:信道是不可靠的:导致分组在传输过程中可能 受损 + 丢失 + 失序 + 延迟
引入:所以我们需要一种机制来确保即使在传输过程中出现了这些问题最终仍然能够将分组正确交付给接收方
核心:自动请求重传协议 (Automatic Repeat reQuest)
确认机制
正面确认(
ACK) & 负面确认(NAK)正面确认:接收方正确收到发送方发送的分组之后反馈给接收方的分组
负面确认:接收方收到错误的分组后反馈给接收方的分组 (实际协议中通常不使用这种机制)
2. 正面确认 + 序号 机制可以代替负面确认:接收方可以通过分组的序号确认当前的分组是否是自己想要的分组,如果不是则重复发送相同的确认分组
差错检测:需要使用差错检测检查分组是否受损
序号:(1) 发送方发送的分组和接收方发送的确认分组都需要编号 (2) 接收方正确收到分组后返回的确认分组和接收分组的序号一致
1* TCP 的实际实现中是根据报文段中第一个字节编号作为整个报文段的编号
2* 序号是可以 复用 的:序号复用可能会导致分组重排序的问题
注:上述机制已经解决了分组可能产生的失序和受损的问题,但是没有解决丢失和延迟到达的问题
重传机制
超时重传:发送方在规定的等待时间内没有接收到确认分组就会重传分组
定时器:(1) GBN 协议中仅使用 一个定时器 (2) SR 协议中 每个分组都拥有一个定时器 (3) TCP 实际实现中也 仅使用一个定时器
计算方式:(指数加权移动平均)
(1) TimeoutInterval = EstimatedRTT + 4DevRTT
(2) EstimatedRTT = $(1-\alpha)EstimatedRTT + \alphaSampleRTT$ ($\alpha$ 通常取 0.125)
(3) 4DevRTT= $(1- \beta)DevRTT + \beta|SampleRTT - EstimatedRTT|$ ($\beta$ 通常取 0.25)
快速重传:发送方在重复接收到 3 个相同的
ACK分组 后就会自动触发重传重传时机:(1) 数据分组丢失 (2) 数据分组延时到达 (3) 数据分组乱序达到 (4) 确认分组延时到达 (5) 确认分组丢失
停止-等待协议:
- 内容:(1) 发送 每发送一个分组 都需要等待接收方返回确认分组 (2) 规定时间内没有接收到确认分组就会重传
- 优点:非常简单
- 缺点:效率非常低:$\frac{\frac{L}{R}}{RTT+\frac{L}{R}}$ (注:这里的 RTT 指的是分组中的 最后一个比特 被发送到接收确认分组的 第一个比特 的时间)
流水线协议
内容:允许发送方 同时发送多个分组 而不需要等待接收方的确认分组
优点:效率相对停止-等待协议提高很多:$\frac{\frac{nL}{R}}{RTT+\frac{L}{R}}$ (注:这个公式为什么是这样的?好好想想,否则流水线的题目是算不出来的)
缺点:(1) 需要控制发送方在每个时刻同时发送的分组的数量 -> 如果不加以限制会造成网络拥塞 (2) 分组可能乱序到达 -> 接收方如何接收?
注:(1) GBN 协议和 SR 协议都采用滑动窗口机制对发送的分组数量加以限制 (2) GBN 丢弃乱序到达的分组 SR 缓存乱序到达的分组
协议:(1) [GBN 协议](#GBN 协议) (2) [SR](#SR 协议)
GBN 协议
内容:
发送方拥有发送窗口:(1) 已经发送但未确认的分组 (2) 即将要发送的分组
注:定义:(1) 最早已发送未确认的分组序号为 base (2) 即将要发送的第一个分组序号为 nextseqnum (3) 窗口长度为 N
发送方 已发送但未确认的分组数量 不可以超过当前滑动窗口的大小 (nextseqnum - base ≤ N)
发送方接收到 确认序号 ≥ base 的确认分组时就会将窗口向前移动 确认序号 - base + 1 个位置 (滑动窗口的含义)
发送方如果在规定时间没有接收到相应的确认分组就会 重传所有已发送但未确认的分组 (就是后退 N 步的含义)
接收方 仅接收按序到来的分组 而对于乱序到来的分组直接抛弃并且发送 上次发送的确认分组
累积确认(Cumulative Acknowlegment):
- 内容:
- 接收方连续按序接收到 多个分组 后 仅返回一个确认分组 用于通知发送方已正确接收到所有分组 (确认分组的序号是最后一个按序到达的分组的序号)
- 发送方接收到确认分组后就认为该 确认序号之前 所有没有被确认的分组都已经被正确接收 (所以滑动窗口会向前移动多个位置)
- 累积确认的目的就是为了加速传输的过程,没有必要对每个分组都进行确认
- 内容:
优点:实现简单 -> 接收方不需要缓存任何乱序到的分组
缺点:后退 N 步造成很多无意义的重传:即使正确到达接收方的分组也可能因为乱序而不被接收,所以发送方只能够重发所有未被确认分组
窗口大小:$1 < W_t < 2^n - 1$ ($n$ 是比特的个数)
细节:
- 发送方的应用层在要求发送数据时,发送方首先检查窗口是否已满:(是否已满就是判断当前已发送但未确认的分组是否达到滑动窗口大小)
- 所有分组共享一个定时器
SR 协议
内容:
发送方:
(1) 发送方拥有发送窗口:已经发送但未确认的分组 + 即将要发送的分组 (其余定义和 GBN 一致)
(2) 发送方 已发送但未确认的分组数量 不可以超过当前滑动窗口的大小 (nextseqnum - base ≤ N)
(3) 发送方仅在接收到 确认序号 == base 的确认分组时才会将滑动窗口向前移动 下一个仍未被确认的分组的序号 - base 个位置
(4) 发送方在规定时间内没有接收到相应分组的确认时 仅重传没有被确认的分组
接收方:
(1) 接收方也拥有接收窗口:缓存所有乱序到达的分组并且对每个乱序到达的分组都会返回确认分组
(2) 接收方仅在接收到 序号 == 最早期待的分组序号 才会将滑动窗口向前移动 下一个被期待的分组序号 - 最早期待的分组序号 个位置
选择重传:
- 无论是否按序到达分组接收方都会返回相应的确认分组让发送方得知哪些分组已经被正确接收哪些还没有被正确接收
- 发送方在重传分组的时候就 可选择性地重传没有被接收的分组 而不是重传所有已发送但未被确认的分组
优点:不会造成非常多无意义的重传
缺点:在序号复用的情况下重传的分组和新发的分组可能无法别识别出来
窗口大小:
- 接收窗口和发送窗口大小通常一致
- $Ws + Wr ≤ 2^n$
细节:
TCP 协议
核心:GBN 协议 + SR 协议的 混合体 :采用 累积确认 + 选择重传
累积确认 + 选择重传:接收方会缓存乱序到达的报文段但是 不会对乱序到达的报文段返回相应的确认,而是持续的返回期望接收到报文段的确认 (累积确认)
面向字节:(1) 应用层交付的每个报文中的比特都被隐式地 编号 (2) 每次将一定数量的字节封装成报文段发送
- 报文段序号(seq):报文段中第一个字节的编号
- 确认报文段确认号(ack):期望收到的下一个字节的序号 seq + data.length
发送缓存:发送窗口(已发送未被确认字节 + 即将发送的字节) + 应用层交付的但还不可以被发送的字节
接收缓存:接收窗口(已收到的字节 + 仍未收到的字节) + 已经确认但未被应用层使用的字节
通信过程:
流量控制
原因:发送方发送的速率太快导致 接收方来不及接收处理 (局部性造成的)
核心:滑动窗口机制
内容:
- 接收窗口大小 rwnd:接收缓存 - 应用层尚未处理的字节数量
- 接收方通过在确认报文段中 填写窗口大小字段 通知发送方当前还可以接收的字节数量
- 从而达到控制发送方的发送速率的目的
问题:$rwnd = 0$ 时发送方就会停止发送数据,但是接收方处理完数据之后并不会通知发送方,那么发送方怎么才能够得知接收方已经处理完数据了呢?
核心:设置定时器
方式:定时器倒计时结束后发送方发送 不带数据的报文段:
(1) 如果接确认报文段的窗口大小字段为 0 则接收方没有处理完
(2) 如果接确认报文段的窗口大小字段不为 0 则接收方已经处理完成:发送方可以重新开始发送数据了
拥塞控制
概念:网络中的各个发送方的发送速率太快导致 路由器来不及接收处理 (全局性 造成的)
核心:慢开始 + 拥塞避免 + 快重传 + 快恢复 (慢开始和拥塞避免是 TCP 拥塞控制的 强制部分)
内容:
慢开始:“探测”当前网络的通信状况
(1) $cwnd_0 = 1$:发送方在刚开始仅发送一个 报文段 (字节数量 =
MSS) 用于试探网络当前的堵塞状况(2) 发送方成功接收到接收方返回的确认报文后更新 $cwnd$ 的值
(3) $cwnd = 2^{n-1}$ ($n$ 是发送的轮次):发送方发送的报文段数量开始成 指数级增长
(4) 发送方结束指数级增长的情况:1) 进入拥塞避免状态 2) 进入快重传机制 3) 出现网络拥塞
(5) 出现网络拥塞之后:使 $ssthresh$ = $cwnd / 2$ 再将 $cwnd$ 的值重新置为 $1$ 重新开始 慢启动
拥塞避免:$cwnd = ssthresh$ 时发送方结束指数增长开始线性增长:经过每个轮次之后 $cwnd++$
快重传:发送方在接收到 $3个ACK$ 之后重传当前发送的报文段并且进入 快恢复
快恢复:(1) 使 $ssthresh$ = $cwnd / 2$ 再将 $cwnd$ 的值重新置为 $ssthresh$ (2) 每经过一个轮次 $cwnd++$
细节:窗口字段大小 = $min(rwnd, cwnd)$


