Loading... ## 传输层提供的服务 ### 端口 传输层实现了 “端到端”(进程到进程)的通信。传输层在 TCP (或 UDP ) 报文段的首部,指明源端口、目的端口。 假设:进程 1、5 正在用 TCP 通信;2、6 正在用 TCP 通信;3、7 正在用 UDP 通信;4、8 正在用 UDP 通信: ![image-20241108013503200.png][1] - 两台主机的端口号是相互独立的。 - TCP、UDP 的端口号也是相互独立的。 - 当两个进程之间想要通信时,需要指明: 1. 使用哪种传输层协议; 2. 本进程绑定的端口号; 3. 对方的 IP 地址和端口号; ### 套接字 (socket) 套接字是一个数据结构,包含了通信所需要的 IP 地址和端口号,这样编程的时候就不需要每次都写目的 IP 和端口号了。 ### 可靠传输和不可靠传输 - 有连接传输:传输前先打招呼,先确认对方已经准备好接收数据。传输结束时也要告知对方已结束。 - 无连接传输:不打招呼,直接把数据传给对方。 ![image-20241108022216957.png][2] - 可靠传输:接收方使用 “确认机制” 让发送方知道哪些数据已被正确接收,可靠传输使用的一定是有连接传输。 - 不可靠传输:接收方无论收没收到数据、数据是否正确,都不给发送方反馈。使用无连接传输。 ### 汇总 ![image-20241108014052661.png][3] ## UDP ### UDP 和 TCP 对比 ![image-20241108022528475.png][4] - UDP 首部很小,只占 8B 。 - UDP 每次传输一个完整的报文,不支持报文自动拆分、重装。 - UDP 是无连接的、不可靠的(可靠性可以交给应用层处理),也不支持拥塞控制。 - UDP 支持一对一(封装成单播 IP 数据报)、一对多传输(封装成广播/多播 IP 数据报)。 ![image-20241108022723187.png][5] - TCP 首部更大,占 20~60B 。 - TCP 支持报文自动拆分、重装,因此可以传输长报文。 - TCP 是有连接的、可靠的、支持拥塞控制。 - TCP 仅支持一对一传输(因为通信双方的传输层必须先建立连接)。 ### UDP 数据报 ![image-20241108023309388.png][6] 受限于 IP 数据报的数据长度,实际 UDP 最大长度不能超过 65515 。 #### 示例 ![image-20241108023609264.png][7] ### UDP 校验 #### 基本思想 将数据部分按照 16bit 切分,然后加和,最后逐位取反得到校验和。校验时将整个部分按照 16bit 切分相加,若结果全为 1 则表明没有比特错误。 ![image-20241108023955476.png][8] #### 校验和生成 ![image-20241108024254634.png][9] 1. 传输层的UDP协议在计算检验和之前,先添加伪首部。 伪首部中的 UDP 长度和实际首部中的长度一样。 2. 把伪首部、首部、数据部分以 16bit 为一组,进行二进制加法(最高位产生的进位需要回卷)。 3. 将最终的加法结果逐位取反,就得到 16bit 检验和,将其填入 UDP 首部。 4. 去掉伪首部,并将UDP数据报交给网络层,封装成 IP 数据报。 #### 接收方校验 1. 网络层向传输层递交 UDP 数据报。 2. 传输层在 UDP 数据报之前,添加伪首部。 3. 把伪首部、UDP 首部、数据部分以 16bit 为一组,进行二进制加法(最高位产生的进位需要回卷)。 4. 如果加法结果为全 1,说明没有比特错误,于是接收该 UDP 数据报,并根据目的端口号,向应用层递交报文。如果加法结果不是全 1 ,说明有差错,于是丢弃该 UDP 数据报。 ### 小结 ![image-20241108024547713.png][10] ![image-20241108024554317.png][11] > IP 数据报中首部校验和的计算方式也是该方法。但 IP 数据报不添加伪首部,数据部分也不参与计算。 ## TCP ### 通信流程 TCP 协议的三大阶段: - 建立连接(三次握手) - 数据传输 - 释放连接(四次挥手) ![三次握手与四次挥手][12] 建立一次 TCP 连接可以传输多个报文。假设:建立 TCP 连接时,双方协商 MSS=1000B(Maximum Segment Size,最大段长): > TCP 并不会强制要求每个 TCP 段都满载数据,只要别超过 MSS 就行。 > > TCP 的首部不记入 MSS 限制。 ![image-20241108201451313.png][13] - 每次建立TCP连接,可以传输多个报文(双向) - TCP 是**面向字节流的**(而 UDP 是面向报文的)。无论传输多少个报文,在 TCP 协议看来都是一连串字节流。 ### TCP 报文段 不用记忆每个字段在什么位置(考试会给示意图),但需要理解每个字段有什么作用。 ![image-20241108205706612.png][14] - **源端口**:发送方进程的端口号。 - **目的端口**:接收方进程的端口号。 - **序号** (seq) :用于标记数据部分第一个字节在原始字节流中的位置。 - 起始 “序号” 是发送方自己设置的,不一定从 0 开始。 - **确认号** (ack 或 ack_seq) :用于反馈,表示序号在该确认号之前的所有字节都已正确收到。 - **数据偏移** (Data Offset) :4bit,表示 TCP 首部长度,以 x4B 为单位。 - 4bit,取值范围 0~15,因此 TCP 首部最长 = 15 $\times$ 4 = 60B 。 - 在TCP首部中,并不会专门记录 TCP 数据部分长度(会根据IP首部、TCP首部的信息算出来) - **保留位**:6bit,暂时没啥用,通常全部置为 0 。 - **URG** (不常考) :1bit,URG=1 时,紧急指针有效。表示这是紧急数据,应尽快插队发送。 - **ACK** :1bit(记为大写ACK)。ACK=0 时,ack_seq 无效;ACK=1 时,ack_seq 有效。 - 只有握手 1 的 ACK=0 ,其他所有的 TCP 报文段 ACK 都为 1 。 - 注意区分大写的 ACK 与小写的 ack 。 - **PSH** (不常考) :1bit,PSH=1 时,表示希望接收方尽快回复(用于交互式通信)。 - **RST** (不常考) :1bit,RST=1 时,表示出现严重差错(如主机崩溃),必须释放连接。也可用于拒绝一个非法报文段(如恶意的黑客攻击)。 - **SYN** :1bit,当 SYN =1 时表示这是一个连接请求或连接接受报文。 - 只有握手 1 、握手 2 的 SYN=1,其他所有 TCP 报文段都是 SYN=0 。 - **FIN** :1bit,当 FIN=1 时,表明此报文段的发送方的数据已发送完毕,要求释放传输连接。 - 只有挥手 1 、挥手 3 的 FIN=1 ,其他所有 TCP 报文段都是 FIN=0 。 - **窗口** (rwnd 或 rcvwnd) :16bit,表示接收窗口的大小。即从本报文段首部中的ack_seq算起, 接收方还能接收多少数据(以字节为单位)。 - 这个字段是实现 “流量控制” 的关键。 - **检验和** (不常考) :原理与 UDP 相似,计算检验和之前也需要添加 12B 伪首部(只需将 UDP 伪首部的协议字段的 17 改成 6,UDP 长度字段改成 TCP 长度)。 - **紧急指针** (不常考) :紧急数据专用序号,原理与上面那个 “序号” 字段相同。 - **选项**:可以为空,也可以非空。 - 参考上面三次握手和四次挥手的图。建立 TCP 连接时,在握手 1 、握手 2 选项中协商 MSS(Maximum Segment Size,最大段长)。 - MSS 的值表示在接下来的数据传输中,一个 TCP 报文段最多携带多少数据(首部不算在内) - 通常MSS不会设置太大,以免在 IP 层被分片。 ### TCP 连接管理 #### 建立连接 ![image-20241109012412638.png][15] 1. 客户向服务器发送连接请求报文段。该报文段首部中 SYN = 1,同时选择一个初始序号 seq = x 。TCP 规定,SYN 报文段不能携带数据,**但是要消耗一个序号**。此时服务器进入 SYN-RCVD (同步收到) 状态。 2. 服务器收到连接请求报文段后,若统一建立连接,则向客户机发回确认,并为该 TCP 连接分配缓存和变量。在确认报文段中,将 SYN 和 ACK 都置为 1 ,**确认号是请求报文中的 seq 加一**,即 ack = x + 1 ,同时自己也选择一个初始序号 seq = y 。注意,确认报文段不能携带数据,**但也要消耗掉一个序号**。此时服务器进入 SYN-RCVD (同步收到) 状态。 3. 当客户机收到确认报文段后,还要向服务器给出确认,并分配缓存和变量。确认报文段的 ACK 位置 1 ,确认号 ack = y + 1 ,序号 seq = x + 1 。该报文可以携带数据,**若不携带数据则不消耗序号**。此时客户机进入 ESTABLISHED (已建立连接) 状态。 4. 当服务器收到来自客户机的确认后,也进入 ESTABLISHED 状态。 **考点**: - 在 TCP 全过程中,只有握手 1 、握手 2 的 SYN=1 。 - 在 TCP 全过程中,只有握手 1 的 ACK=0 。 - 握手 1 、握手 2 不能携带数据(只有 TCP 首部),但是仍要消耗一个序号。 - 从发出握手 1 ,到客户端进程可以发送数据,至少需要多久?—— 1RTT 。 - 从发出握手 1 ,到服务器进程可以发送数据,至少需要多久?—— 1.5RTT 。 #### 释放连接 ![image-20241109013702246.png][16] 1. 客户机发送连接释放报文段,并停止发送数据,主动关闭 TCP 连接。该报文段的终止位 FIN 置 1 ,序号 seq = u ,它等于前面已传送过的数据的最后一个字节的序号加一。FIN 报文即使不懈怠数据,**也要消耗一个序号**。此时,客户机进入 FIN-WAIT-1 (终止等待 1) 状态。 由于 TCP 是全双工的,因此对面还可以发送数据。 2. 服务器收到连接释放报文后立即发出确认。确认号 ack = u + 1 ,序号 seq = v ,等于前面已传送过数据的最后一个字节的序号加一。然后服务器进入 CLOSE-WAIT (关闭等待) 状态。 此时从客户机到服务器这个方向的连接就释放了,TCP 连接处于**半关闭状态**。但此时服务器可能还在发送信息,即服务器到客户机这个方向的连接并未关闭,所以客户机收到来自服务器的确认后,进入 FIN-WAIT-2 (终止等待2) 状态,等待服务器发出的连接释放报文。 3. 若服务器已经没有要想客户机发送的数据,就通知 TCP 释放连接。此时其发出 FIN = 1 的连接释放报文,该报文的序号 seq = w ,需要重复上次已发送的确认号 ack = u + 1 。此时服务器进入 LAST-ACK (最后确认) 状态。 4. 客户机收到连接释放报文段后,必须发出确认,之后进入 TIME-WAIT (时间等待) 状态。该报文的却认为 ACK 置 1 ,确认号 ack = w + 1 ,序号 seq = u + 1 。服务器收到该确认报文段后就进入 CLOSED (连接关闭) 状态。 客户机进入 TIME-WAIT 状态后,还要经过 2MSL (Maximum Segment Lifetime, 最长报文段寿命) 后,才进入 CLOSED 状态。 > MSL 是由 TCP 协议规定的一个固定时间长度。 **考点**: - 在 TCP 全过程中,只有挥手 1、挥手 3 的 FIN=1 。 - 挥手 1 、挥手 3 即使不携带数据,也要消耗一个序号。 - 挥手 2 可以携带数据,挥手 4 不可以携带数据。 - 如果服务器进程收到挥手 1 时,已经没有待传送数据,那么可以连续发出挥手 2 、挥手 3 。 - 发出/收到某个挥手报文段前后,TCP 状态如何变化? #### 小结 ![image-20241109023022773.png][17] ### TCP 可靠传输 #### 序号 TCP 握手阶段确定起始序号和窗口大小 ![image-20241109030429097.png][18] #### 确认机制 **累计确认**: ![image-20241109030542692.png][19] **捎带确认**: ![image-20241109030559251.png][20] #### 超时重传 报文段丢失的情况: ![image-20241109030642979.png][21] ACK 丢失的情况: ![image-20241109030701271.png][22] #### 快重传与立即确认机制 ![image-20241109030755406.png][23] #### 小结 ![image-20241109030307193.png][24] ### TCP 流量控制 ![image-20241109030321859.png][25] ### TCP 拥塞控制 - 流量控制:控制端到端的数据发送量,是 “局部的” 。 - 拥塞控制:控制整个网络中每台主机的数据发送量,降低路由器负载,是 “全局的” 。 如何判断网络拥塞? - 发出的每个报文段,都能顺利地收到 ACK 确认 —— 不拥塞 - 发出的报文段未能按时收到 ACK ,引发超时重传 —— 严重拥塞 - 收到冗余 ACK ,引发快重传 —— 有点拥塞 如果检测到网络拥塞怎么办? - 迅速减少发送的数据量 (限制发送窗口) - 严重拥塞就迅速缩小拥塞窗口 - 有点拥塞就适当缩小拥塞窗口 #### 慢开始和拥塞避免算法 **慢开始算法**的思路是:当发送方刚开始发送数据时,因为不清楚网络的负荷情况,若立即将大量数据注入网络,则有可能引发网络拥塞。具体方法是先发送少量数据探测一下,若没有发生拥塞,则将拥塞窗口翻倍。 **拥塞避免算法**的思路是:让拥塞窗口 cwnd 缓慢增大。具体做法是,没经过一个往返时延 RTT 就把发送方的拥塞窗口 cwnd 加 1 ,而不是加倍。 ![image-20241109191918699.png][26] > ssthresh :慢开始门限 (正规翻译) ,拥塞控制阈值 (真题术语) 。 - 慢开始算法:cwnd 值从1开始,每收到⼀个 ACK ,就让 cwnd+1(当 cwnd < ssthress 时适⽤) - 拥塞避免算法:在⼀个 RTT 内,即使收到多个 ACK ,也只能让 cwnd+1(当 cwnd $\geq$ ssthress 时适⽤) 无论在那个阶段,只要发送方判断网络出现拥塞 (未按时收到确认) ,就要首先把慢开始门限 ssthresh 设置为出现拥塞时的发送方的 cwnd 值的一半,但不能小于 2 。然后把拥塞窗口 cwnd 重新设置为 1 ,执行慢开始算法。 #### 快重传和快恢复算法 ![image-20241109193220440.png][27] - 快重传:当发送⽅收到三个确认号相同的冗余ACK时,⽴即重传对应报⽂段。 - 快恢复算法:⼀旦发⽣快重传,就将阈值、cwnd 都设为当前 cwnd 的⼀半,然后切换到为 “拥塞避免算法” #### 拥塞控制考题特点 1. 常与 “流量控制” 综合考察,此时需要考虑接收窗口大小。 2. 通常只涉及单向传输(TCP 连接双方只有一方发送数据)。 3. 通常默认每个 TCP 报文段都以最大段长 MSS 满载数据。 4. 拥塞窗口的大小常以 “MSS的倍数” 作为单位。 5. 接收方收到一个报文段,会 “立即确认” ,而不是 “推迟确认” 。 [1]: https://blog.domineto.top/usr/uploads/2024/11/1172455113.png [2]: https://blog.domineto.top/usr/uploads/2024/11/4181162404.png [3]: https://blog.domineto.top/usr/uploads/2024/11/3272263416.png [4]: https://blog.domineto.top/usr/uploads/2024/11/2335962377.png [5]: https://blog.domineto.top/usr/uploads/2024/11/38658768.png [6]: https://blog.domineto.top/usr/uploads/2024/11/2645185314.png [7]: https://blog.domineto.top/usr/uploads/2024/11/4086340642.png [8]: https://blog.domineto.top/usr/uploads/2024/11/2663973333.png [9]: https://blog.domineto.top/usr/uploads/2024/11/1027647862.png [10]: https://blog.domineto.top/usr/uploads/2024/11/3090621418.png [11]: https://blog.domineto.top/usr/uploads/2024/11/1938443346.png [12]: https://blog.domineto.top/usr/uploads/2024/11/4203029224.png [13]: https://blog.domineto.top/usr/uploads/2024/11/1966207843.png [14]: https://blog.domineto.top/usr/uploads/2024/11/188841862.png [15]: https://blog.domineto.top/usr/uploads/2024/11/2933962526.png [16]: https://blog.domineto.top/usr/uploads/2024/11/2091628358.png [17]: https://blog.domineto.top/usr/uploads/2024/11/1620027511.png [18]: https://blog.domineto.top/usr/uploads/2024/11/1921466086.png [19]: https://blog.domineto.top/usr/uploads/2024/11/426217303.png [20]: https://blog.domineto.top/usr/uploads/2024/11/666402917.png [21]: https://blog.domineto.top/usr/uploads/2024/11/3069217359.png [22]: https://blog.domineto.top/usr/uploads/2024/11/4118692349.png [23]: https://blog.domineto.top/usr/uploads/2024/11/3049092016.png [24]: https://blog.domineto.top/usr/uploads/2024/11/2063744232.png [25]: https://blog.domineto.top/usr/uploads/2024/11/1258826022.png [26]: https://blog.domineto.top/usr/uploads/2024/11/90761623.png [27]: https://blog.domineto.top/usr/uploads/2024/11/197805532.png 最后修改:2024 年 11 月 12 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏