亲宝软件园·资讯

展开

自顶向下 | 带你遨游运输层

许朋友爱玩 人气:0
## 前言 > 本文已经收录到我的`Github`个人博客,欢迎大佬们光临寒舍: > > [我的GIthub博客](https://lovelifeeveryday.github.io/) ## 学习导图: ![学习导图](https://s1.ax1x.com/2020/03/18/80YN5R.png) ## 一.运输层概述 - 运输层为运行在**不同主机**上的应用程序之间提供逻辑通信 - 应用报文加上**运输层**首部形成运输层**报文段**,报文段通过网络层被封装成**网络层**分组(**数据报**)向目的地发送 Q1:**运输层和网络层的关系** - 运输层:运行在**不同主机**上的**应用程序**之间提供逻辑通信 - 网络层:提供**主机之间**的通信 举个例子来说明两者关系: > 有两个家庭,一家位于广州,一家位于北京,每家有 3个孩子。这两个家庭的孩子们喜欢彼此通信,每封信都用单独的信封通过传统的邮政服务发送。每个家庭有一个孩子**负责收发邮件**,北京家庭是 阿京,而广州家庭是 阿州。每周阿京去她所有的**兄弟姐妹那里收集邮件**,并将这些邮件交到邮递员处上。当信件到达北京家庭时,阿京也负责将信件发到她的兄弟姐妹手上,广州家庭中 阿州也负责类似工作 - 网络层——邮递员 - 运输层——阿京和阿州 - 应用程序——兄弟姐妹 - 主机——两个家庭 > 通过运输层协议,两台电脑仿佛直接相连一样。应用**无需知道底层内部实现的原理**和细节,比如怎么把远隔世界两地电脑上的数据进行相互传输 Q2:**注意点** * 运输层协议仅仅实现在网络的边缘处,例如主机,电脑,手机等。如路由器,交换机这些网络核心设备,是没有实现运输层协议的 * 每一层协议仅仅检查分组相应的协议层字段 * 最常用的两种输入层协议,`TCP`和 `UDP` * 运输层的下面是网络层,网络层的目的在于为不同的主机提供逻辑通信,而非主机上的进程 * 网络层常用协议是 `IP`,其提供的是一种不可靠的数据传输服务 * 为了做到**为不同主机 (`host`) 上的应用或者说进程提供逻辑通信**这一目的,运输层协议必须能分辨出数据的来源和去向。为此,运输层存在着两种行为,多路复用和多路分解 二.多路复用与多路分解 ---- - 多路复用:当运输层收到来自上方应用层的数据时,运输层会为数据**封上一些头部信息**,根据所有协议不同,封上的信息也不一样 > 用上面的两个家庭的例子进行形象化地阐述就是:多路复用就是阿州和阿京将兄弟姐妹的信一起交给邮递员 - 多路分解:当运输层收到下方网络层传输来的数据时,运输层会**检查**多路复用时**封上的信息**,从而正确的把数据**定向到相应的进程** Q1:如何使用运输层的协议? 操作系统提供了被称为 `socket` 的接口 `api` 供编程人员调用,对 `socket` 的形象理解是其是一种抽象,将复杂的实现 (`tcp/udp`) 协议的各种行为抽形成简单的几个函数给开发人员使用。就像浏览器将发送请求报文这一 `http` 协议规定的行为,抽象成我们只需要输入 `url` 然后回车即可 这里需要注意的一点是: - 在一般情况下,一个计算机端口只能被一个进程占用 - 一个进程可以创建多个 `Socket`,多个 `TCP` `Socket` 可以监听同一个端口,并保证接受的数据依旧是正确的 - 多个 `UDP Socket` 就无法监听同一端口 - 这其中的差异源于 `TCP` 和 `UDP` 协议的不同 >- `TCP` 是面向连接的,其有**足够状态**的信息来分辨数据来源,后定向到正确的 `socket` >- `UDP` 不需要维持连接,仅仅通过**端口号**来决定数据的去向,所以会导致冲突 三.`UDP` 和`TCP`的多路复用和分解 ------------ Q1:`UDP`的多路复用和分解 一个 `UDP Socket` 通过一个二元组 (目的 **`IP` 地址**,目的**端口号**) 来标识,当输入层收到数据时,通过检查这个二元组,来定向数据该去往哪一个 `UDP Socket`。这也是多个 `UDP Socket` 无法监听同一个端口的原因 Q2:**`TCP` 的多路复用分解** 一个 `TCP Socket` 通过一个四元组 (源 `IP`,源端口,目的 `IP`,目的端口号) 来标识,这也解释为什么多个 `TCP Socket` 可以监听同一个端口,尽管目的 `IP`和目的端口号是一样的,但是源 `IP`和源端口的组合总是不同的 ![TCP UDP Socket对比](https://s1.ax1x.com/2020/03/18/8095RK.png) 四.`UDP` --- ### 4.1 `UDP`基本概念 相比于 `TCP`来讲,`UDP`是一个简单的协议,就是把网络层 `IP` 提供的服务封装了下,实现了多路复用和分解,提供了端到端进程间的通信和错误检验服务 相对于 `TCP` 来说: 缺点: * `UDP` 是不可靠的传输服务 * 没有流量和拥塞控制 优点: * 能够够精细的控制数据的发送时间和速率 * 无需事先建立连接 * 无连接状态 * 分组首部开销小 **`UDP`报文端结构:** ![UDP 报文段结构](https://s1.ax1x.com/2020/03/18/80CQeJ.jpg) * 源端口号:发送方的端口号 * 目的端口号:接收方端口号 * 长度:包括首部在内的报文长度 * 检验和:用来**差错检验**。只发现错误不纠正,**错了就扔**。然后**重发** ### 4.2 可靠数据传输 Q1:**数据传输可能遇到的问题:** * 传输中数据被损坏 * 数据丢失 * 数据可能乱序到达 Q2:**解决方法:** * 检验和 * 序号 * 定时器 * 肯定和否定反馈分组 Q3:**如何在保证可靠性的前提下,提高其性能?** * 通过引入流水线 (`pipelining`) 技术 引入流水线导致了: * 序号范围需要增加 * 收发双方可能需要缓存乱序到达的分组 * 以上两个的具体实现取决于传输协议**如何处理分组丢失、损坏的问题** (是选择回退 N 步,还是选择重传) Q4:**如何处理分组丢失、损坏的问题** A.回退 `N` 步 - 其核心在于,发送方会维持一个窗口,发送方能发送的数据量**取决于窗口长度**,并且当丢失时会重送所有未确认的分组 - 接收方会**丢弃乱序**到达的缓存 - 特点: 1.**累计**性 `ACK` 2.单一定时器 B.选择重传 - 核心在于,收发双方都会维持一个窗口,并且尽力保证窗口的状态是同步的,因此当分包丢失时,发送方**只会重送丢失的分组** - 接收方会缓存乱序到达的分组 - 特点: 1.**独立**性 `ACK` 2.多个定时器 五.`TCP` --- ### 5.1 `TCP`基本概念 A.特点: * **面向连接** * **全双工**的 * **点对点**,不存在一次发送将数据传递给多个接收方、 * 在合适的时候发送 **发送缓存** 里的数据 * 为每个数据封上一个 `TCP` 头部 * `TCP` 连接的每一端都具有**发送缓存和接受缓存** B.报文段结构 ![TCP报文段结构](https://s1.ax1x.com/2020/03/18/80PoHH.png) 部分参数解释: * 序号 (`seq`) :所带数据的第一个比特的序号,同时也是接收方期待的序号,等于接收方回复报文中的 `ACK`(n) 中的 n * 确认号 (`ack`) : 对于一个 `ACK`(n) 来说,告诉对方 n-1 前的数据已经收到,下一次期待的序列号为 n * `ACK` :指示,用于指示报文中确认号字段的值是有效的 * `PSH` :指示,立即发送_发送缓存_里的数据 * `RST` :指示,用于强制关闭连接 * `SYN` : 指示,用于握手阶段也就是建立连接的阶段 * `FIN` :指示,用于正常关闭连接 * 接受窗口 :用于 `TCP` 的流量控制功能 ### 5.2 可靠数据传输 * `TCP` 协议为数据的每一 `Byte` 都编号,而非针对报文段 * 总是维持最老未经确认的 1 `Byte` 的计时器 * 每一次**超时重置**的**计时器时间会加倍** * 其**错误恢复机制**是**回退 N 步和选择重传的混合体** >- 不会丢弃乱序到达的分组,而是缓存起来 > - 采用累计性 `ACK` > - 只会重传丢失报文段中的数据 - 快速重传:当连续收到 4 个相同的 `ACK` 时 (一个正常的 `ACK`,三个正常 `ACK` 的重复),会触发快速重传,立即重发分组 ### 5.3 流量控制 - 为了防止过高数据流量导致接收者的接受缓存爆掉,接收者会在其 `TCP`报文中通过 **接受窗口** 指示发收者还能发送多少数据 接受窗口 (`rwnd`) 公式: * `rwnd = RcvBuffer - [LastByteRead - LastbyteRead]` * 且:`LastByteSent - LastByteAcked <= rwnd` ### 5.4 `TCP` 连接管理 Q1:**建立连接(三次握手)** ![三次握手](https://cdn.jsdelivr.net/gh/LoveLifeEveryday/FigureBed@master/typora202003/18/160201-36438.gif) 1. 客户端发送 `SYN` 位置 1 的报文段 2. 服务端返回 `SYN` 为 1,`ACK` 为 1 的报文段 3. 客户端发送 `ACK` 为 1,且附带数据的报文段 >形象化地理解: > >`TCP 三次握手`就好比两个人在街上隔着50米看见了对方,但是因为雾霾等原因不能100%确认,所以要通过招手的方式相互确定对方是否认识自己。 > >张三首先向李四招手(`syn`),李四看到张三向自己招手后,向对方点了点头挤出了一个微笑(`ack`)。张三看到李四微笑后确认了李四成功辨认出了自己(进入`estalished`状态) > >但是李四还有点狐疑,向四周看了一看,有没有可能张三是在看别人呢,他也需要确认一下。所以李四也向张三招了招手(`syn`),张三看到李四向自己招手后知道对方是在寻求自己的确认,于是也点了点头挤出了微笑(`ack`),李四看到对方的微笑后确认了张三就是在向自己打招呼(进入`established`状态)。 > >于是两人加快步伐,走到了一起,相互拥抱 ![张三李四](https://s1.ax1x.com/2020/03/18/801Sqs.png) Q2:**断开连接(四次挥手)** ![四次挥手](https://cdn.jsdelivr.net/gh/LoveLifeEveryday/FigureBed@master/typora202003/18/163309-569310.gif) 1. 客户发送 `FIN` 为 1 的报文段 2. 服务端返回 `ACK` 为 1 的报文段 3. 服务端发送 `FIN` 为 1 的报文段 4. 客户端返回 `ACK` 为 1 的报文段 5. 客户端在一段时间后,关闭连接 > 形象化地理解: > > 张三挥手(`fin`)——李四伤感地微笑(`ack`)——李四挥手(`fin`)——张三伤感地微笑 六.拥塞控制 ---- Q1:**拥塞的代价** * 导致**分组过长的排队时延** * 需要**重传**因**缓存溢出丢失**的分组 * 高延时导致重送分组 * 丢包导致运输相关分组的分组交换器所作的工作全部白费 Q2:**`TCP` 的拥塞控制** - `TCP` 采用端到端的拥塞控制 - 三个主要问题: 1. 一个 TCP 的发送方如何限制自己的发送流量的速率? 通过设置一个**拥塞窗口** (`cwnd`), 并且保证:`LastByteSent - LastByteAcked <= min{cwnd, rwnd}` 2. 如何感知其发送路径拥塞了? * `timeout` * 收到一次正常 `ACK` 后连续收到三次正常 `ACK` 的重复 3. 感到拥塞时,采用什么样的算法改变发送速率? * 慢启动 `cwnd` 的值从 1 `MSS` 开始,并且对每一个 `ACK`,`cwnd` 值变为原来的 2 倍,直到超过阈值 (`ssthresh`),转为拥塞避免模式 * 拥塞避免 在每一个 `RRT` 时间,`cwnd` 的值增加一个 `MSS` * 快速恢复 `cwnd` 的值降为一半加上重复收到的重复 `ACK` 的数量,并且每一个 `ACK`,`cwnd` 的值增加一个 `MSS` 在实践中,一旦 `timeout` 就会会到慢启动的状态,多次重复 `ACK` 则会进入快速恢复状态 Q3:`TCP` 公平 `TCP` 的公平性在于保证每个连接的吞吐量是平均的,而不是应用或进程间 ## 七.再谈握手和挥手 ### 7.1 为啥一定要三次握手,两次不行吗? 弄清这个问题,我们需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。 - 第一次握手:客户端发送网络包,服务端收到了。 这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。 - 第二次握手:服务端发包,客户端收到了。 这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。 - 第三次握手:客户端发包,服务端收到了。 这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。 因此,需要三次握手才能确认双方的接收与发送能力是否正常。 试想如果是用两次握手,可能会出现下面这种情况: >如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再**重传**一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在**某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端**,此时服务端**误认为客户端又发出一次新的连接请求**,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源 ### 7.2 为啥挥手要四次? 这是因为服务端在`LISTEN`状态下,收到建立连接请求的`SYN`报文后,把`ACK`和`SYN`放在一个报文里发送给客户端。而关闭连接时,当收到对方的`FIN`报文时,**仅仅表示对方不再发送数据了但是还能接收数据**,所以服务端可以立即`close`,也可以发送一些数据给客户端后,再发送`FIN`报文给客户端来表示同意现在关闭连接,因此,服务端`ACK`和`FIN`一般都会分开发送。 ------ 如果文章对您有一点帮助的话,希望您能点一下赞,您的点赞,是我前进的动力 本文参考链接: - 《计算机网络-自顶向下方法》 - [面试官,不要再问我三次握手和四次挥手](https://juejin.im/post/5d9c284b518825095879e7a5#heading-7) - [TCP的三次握手四次挥手](https://juejin.im/post/5a0444d45188255ea95b66bc#heading-5) - [跟着动画来学习TCP三次握手和四次挥手](https://juejin.im/post/5b29d2c4e51d4558b80b1d8c#heading-2) - [第三章-运输层-阅读笔记](https://juejin.im/post/5cf51edfe51d45598611b920#heading-8) - [第三章:运输层 |《计算机网络:自顶向下方法》](https://zhuanlan.zhihu.com/p/36030269)

加载全部内容

相关教程
猜你喜欢
用户评论