| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> TCP协议 可靠传输概念解析 -> 正文阅读 |
|
[网络协议]TCP协议 可靠传输概念解析 |
概述? ? ? ? 可靠传输作为TCP协议的核心机制,是其与UDP协议的最大区别.操作系统为了实现可靠性和在实现可靠性的前提下提高效率,引入了很多机制.本篇博客将对这些机制进行一一解析. 确认应答? ? ? ? 确认应答,顾名思义,是指当接收方收到发送方传输过来的数据时,会返回一个特殊的应答报文.告诉发送方数据成功传输. ? ? ? ? 由于在网络上传输数据存在后发先至的情况,因此我们需要给发送的数据编号,这样接收方在返回应答报文时,就可以用序号表示应答的是哪个报文了.这一点在TCP的报文结构中有所体现,如下图: ? ? ? ? ? ? ? ? ?在一般的TCP报文中,32位序号决定了一个数据报的"起点",报文的长度决定了数据报的"终点",确认序号此时不起作用.而在应答报文中,32位确认序号就会生效,表示应答的是哪个报文.例如: ? ? ? ? ? ? ? ? ? ? ?在上图的数据传输过程中,数据报的32位序号是1,长度是1000个字节.这表示这次传输把序号为1-1000的这些字节都发送过去了.接收方在返回时,就会根据这个32位序号和长度计算出下一个数据报的字节起点序号,将其作为32位确认序号发送回去. ? ? ? ? ? ? ? ? ? ? ? 那么,如何区分一个报文是普通报文还是应答报文呢?这点其实在TCP的报文内容中有定义. ? ? ? ? ? ? ? ? ? ? ? ?在这六位中,第二位"ACK"则表示一个报文是否是应答报文.若ACK位为0,则表示不是应答报文,反之则是应答报文.所以我们也经常将应答报文称为ACK报文.(ACK实际上是"acknowledge"的缩写). 超时重传? ? ? ? 数据的传输环境是比较复杂的,尤其是当网络拥挤的时候,就容易产生丢包.而丢包是无差别的,无论是发送的普通报文还是应答的ACK报文,都有可能在传递的过程中丢失.而确认应答机制依赖于ACK报文的传递.倘若ACK报文传递失败或压根就没法传递该怎么办呢?这就需要我们引入超时重传机制. ? ? ? ? 超时重传机制的原理同样很简单:在发送方发送数据一段时间后,如果没有收到接收方发回的ACK报文.发送方就会触发超时重传机制,再次发送一次数据. ? ? ? ? 有两种情况会触发超时重传机制:发送的数据丢了 和 ACK报文丢了.图示如下: ????????这是发送的数据丢失的情况. ? ? ? ? ?这是ACK丢失的情况. ? ? ? ? ?可以发现的是,报文的丢失情况对发送方是不可见的,发送方只需在没有收到ACK后重传数据即可.而对接收方来说则不然,在ACK丢失的情况下,接收方会收到两份一模一样的数据,这时候就需要接收方对收到的相同的数据进行去重. ? ? ? ? ?此外,在超时重传中还有一个"重传等待时间"的概念.假设有两次重传,第一次重传后等待时间为t1,第二次重传后等待时间为t2.此处t2应大于t1.这是因为在正常的网络数据传输的过程中,丢包的概率并不是特别大.在连续两次重传的情况下,单次丢包的概率已经相当大了,很可能是网络上遇到了严重故障且短时间无法恢复.这种情况下,重传数据多少次也是无意义的.因此重传的时间间隔应随着重传次数的增加而变大. 连接管理? ? ? ? 确认应答和超时重传这两个机制是保证TCP可靠性的最核心机制.此外,操作系统还提供了一些其他的辅助机制,连接管理就是其中之一. ? ? ? ? 连接管理主要分为两个部分:"三次握手" 和 "四次挥手". ? ? ? 三次握手? ? ? ? ? ? ?原理? ? ? ? "三次握手"指代的是服务器和客户端建立连接的过程.具体含义如下: ? ? ? ? 我们用A表示客户端,B表示服务器. ? ? ? ? 第一次:A向B发送请求连接的报文(SYN) ? ? ? ? 第二次:B向A返回应答报文(ACK) ? ? ? ? 第三次:B也给A发送一个尝试建立连接的报文(SYN) ? ? ? ? 第四次:A向B返回应答报文(ACK) ? ? ? ? (SYN与ACK一样,是上面六位中的一位.SYN位为1表示该报文是请求连接的报文,为0则不是) ? ? ? ? 经历上述四次交互,就完成了建立连接的过程.那为什么我们称之为"三次握手"呢?这是因为第二次和第三次交互都是由操作系统内核来完成的,二者可以合并成一个报文.这就变成了三次交互,因此我们称之为"三次握手".图示如下: ? ? ? ? ? ?因为每次数据报文传输,都要经过一系列的封装和分用,这需要消耗一定的时间和资源,因此在允许的情况下,把两个报文合成一个报文无疑的效率更高的. ? ? ? ? ? ? 作用? ? ? ? ? ? ? ? "三次握手"的作用相当于投石问路.在正式的数据传输之前,先确定好服务器和客户端之间的通信链路是否畅通,验证通信双方的接收和发送能力是否都正常.这是其一. ? ? ? ? ? ? ? ? 其二,"三次握手"还能让通信双方在正式传输数据之前协调一些重要的参数.如传输数据的字节序号从几开始等等. ? 四次挥手? ? ? ? ? ? ?原理? ? ? ? ? ? ? ? ?"四次握手"是结束通信时进行的过程.与"三次握手"只能由客户端发起第一次请求不同,"四次挥手"的第一次请求既可以由客户端发起,也可以由服务器发起.具体含义如下: ? ? ? ? ? ? ? ? ?我们同样用A表示客户端,B表示服务器.这里假设由A先发送请求. ? ? ? ? ? ? ? ? ?第一次:A向B发送请求结束连接的报文(FIN) ? ? ? ? ? ? ? ? ?第二次:B返回给A应答报文(ACK) ? ? ? ? ? ? ? ? ?第三次:B向A发送请求结束连接的报文(FIN) ? ? ? ? ? ? ? ? ?第四次:A返回给B应答报文(ACK) ? ? ? ? ? ? ? ? ?(FIN与上述的ACK和SYN一样,属于6位标志位中的一位.FIN位是1表示该报文是请求结束连接的报文,为0则不是) ? ? ? ? ? ? ? ? ?与"三次握手"不同的是,"四次挥手"中的第二次和第三次通常情况下不能合并(能合并的情况在后面会讲到).这是因为B返回ACK给A是操作系统内核的行为,操作系统内核在收到A发来的FIN后,就会立即返回ACK.而B发送FIN给A则是应用层代码部分行为,在代码部分调用socket.close()方法,才会触发FIN的发送. ? ? ? ? ? ? ? ? ? 操作系统层面的操作和代码层面的操作无疑是有时间间隔的,这个时间间隔就决定了B的ACK和FIN在通常情况下不能合并.图例如下: ? ? ? ? ? ? ? ? ?之所以要在代码层面决定FIN报文的发送,是因为在A给B发送FIN报文的时候,B可能还有数据没有读取完,仍需继续工作.这个时候什么时候断开连接就是需要通过代码来手动控制的了. 服务器状态? ? ? ? 我们借助一张图来阐明服务器在"三次握手"和"四次挥手"中的状态,如下: ? ? ? ? ?以下仅介绍一些重要的状态. ? ? ? ? 1.服务器的LISTEN状态.表示服务器已经启动完毕,绑定端口成功,等待接收客户端的请求(对应到代码即是服务器的"new ServerSocket()"). ? ? ? ? 2.ESTABLISHED状态.体现在"三次握手"过程中,当一方在收到另一方对自己发送的SYN报文的应答报文时,即会进入ESTABLISHED状态.表示单向连接建立完成(对应到代码即是客户端的new Socket(ip,port)). ? ? ? ? 3.CLOSE_WAIT状态.体现在"四次挥手"过程中,被动接收FIN的一方在返回ACK报文后即会进入CLOSE_WAIT状态.该状态顾名思义,是等待调用自己的close()方法.如果我们在服务器发现了大量的CLOSE_WAIT状态,那就很有可能是服务器端在接收了客户端FIN报文后未调用自己的close()方法. ? ? ? ? 4.TIME_WAIT状态.同样体现在"四次挥手"过程中,该状态只出现在率先发送FIN报文的一方.当先发送的这一方收到了对方发送过来的FIN报文,并且返回了ACK报文之后.就会进入TIME_WAIT状态.该状态存在的目的是:在最后一个ACK报文丢失的情况下,可以触发对方FIN报文的超时重传,保证连接能正常结束. 滑动窗口? ? ? ?概念介绍? ? ? ? 滑动窗口机制的作用是在保证TCP可靠传输的前提下,尽可能的提升数据传输的速率. ? ? ? ? 我们先来看看,在没有滑动窗口的情况下,TCP协议是怎么传递数据的,如下图: ? ? ? ? ?这是常规的"一问一答"的传输方式.这种传输方式的效率无疑是低下的------一份数据必须要在收到上一份数据对应的ACK报文之后才能发送出去.但是实际在网络数据传输的过程中,丢包的概率并不太大.因此我们可以考虑一次性发送多分数据,将等待ACK报文的时间重叠起来,起到提升效率的作用.具体如下图: ? ? ? ? 在不等待的前提下,我们将一次性发送的数据报个数称之为滑动窗口的长度,记为n.当n趋于无穷大时,TCP协议即简化成了UDP协议------不需要等待任何ACK报文. ? ? ? ? 但是实际上,n并不是越大越好的,因为传输效率不单单由发送速率决定,还由接收速率决定.而接收速率我们是通过服务器上的应用程序读取接收缓冲区的数据的速度来间接衡量的.这个在后面会讲到. ? ? ? ? 下面这张图可以形象的描述滑动窗口的运行流程: 滑动窗口下的超时重传? ? ? ? 滑动窗口下的超时重传同样分为两种情况: ? ? ? ? 情况一? ? ? ? 情况一:数据报抵达了服务器,但是服务器返回的ACK丢了.如图: ? ? ? ? ?由于一个ACK报文中的确认序号代表从起始序号到当前确认序号这一段数据都已经成功接收(比如起始需要为1,一次数据发送长度为1000字节,当服务器返回的ACK中的确认序号为3001时,表示从1-3000这段数据都已经成功接收).所以在这种情况下,ACK报文丢失并不是什么严重的事.丢包的概率本身就不高,除非一个滑动窗口中所有对应的ACK报文都丢失了,否则一旦有一个ACK抵达,就能表示前面所有的数据均成功抵达. ? ? ? ? 因此在上述情况下,操作系统不需要做什么额外操作. ? ? ? ? 情况二? ? ? ? ? ? ? ? 与情况一相对,情况二则是传输的数据丢失了.图示如下: ? ? ? ? ? ? ? ? ?当服务器收到了"序号间断"的数据时,服务器可以很明确的知道是数据包丢失了,这时候服务器会重复发送相同的ACK报文去索要丢失的数据,客户端在多次收到重复的ACK之后,就会再发送一份对应的数据,然后循环这个过程,直到服务器收到缺失的数据为止. ? ? ? ? ? ? ? ? 值得注意的是,在服务器重复发送相同的ACK期间,它仍在不断接收客户端发来的新的数据.这部分数据在客户端补发数据之后,是不需要再重新发送的. 流量控制? ? ? ?概述? ? ? ? 根据上述对滑动窗口的描述我们可以知道,滑动窗口越大,数据的发送速率就越大.而发送速率并不是越大越好的,当发送速率>>接收速率时,不仅不能提高整体的传输速率,反而还增加接收方丢包的概率,触发更多的超时重传,降低整体的传输速率. ? ? ? ? 因此,我们需要一个机制来对滑动窗口的大小来进行控制,而流量控制,正能起到这种作用. ? ? ? ? 要想让发送速率和接收速率相当,我们就需要有对应的指标来衡量他们的大小.发送速率的大小我们可以用滑动窗口的大小来衡量,而接收速率的大小我们无法直接得知,需要间接表示. ? ? ? ? 接收速率的衡量? ? ? ? ? ? ? ? 客户端发送给服务器的数据会先进入服务器的接收缓冲区中,等待应用程序调用read()方法从接收缓冲区中读取数据.具体如下图: ? ? ? ? ?而接收缓冲区我们可以看成一个阻塞队列,阻塞队列的大小是有上限的.因此我们可以用阻塞队列的剩余空间大小来间接反映服务器的接收速率------当阻塞队列剩余空间越大,说明接收速率越快;反之,则说明接收速率越小. ? ? ? ? ?在确定衡量指标之后,我们需要考虑的是如何将这个指标告知发送方.这一点,TCP在设计报文结构中有预留好位置,如下图: ? ? ? ? ?在上述报文结构中,有一个"16位窗口大小".这一项在当前报文是ACK报文的时候会生效,用于表示接收缓冲区的剩余空间大小.根据这个数据,我们就可以合理调整发送速率了. ? ? ? ? 这里需要注意的是,虽然窗口大小只有16位,但是这并不意味着窗口大小最大只有64KB.在上述报文结构中,我们注意到有"选项"这一部分.这部分中有一个特殊字段,叫做"窗口扩大因子".可以以一定比例扩大窗口的大小. ? ? ? ? 流量控制的图示如下: ? ? ? ? 可见在流量控制下,服务器每次在返回ACK报文的时候,都会告知客户端当前自己的接收缓冲区有多大.从而让客户端可以实时调整滑动窗口的大小. ? ? ? ? 值得注意的是,当服务器告知客户端当前接收缓冲区剩余大小为0时,客户端会停发信息.等待服务器给客户端发送窗口大小更新通知.若客户端在一段时间之后(这个时间是有具体值的)仍没收到服务器发回的窗口更新通知,客户端会发一个用于"窗口探测"的报文.这个报文通常只包含一个字节,其作用是询问服务器接收缓冲区的大小是否更新了.如果还没更新,那么客户端会继续等待;如果更新了,那么就继续发送数据. 拥塞控制? ? ? ? 在上述的讨论中,我们把数据传输简化成了客户端--服务器单向传输的模型.但是实际上,客户端传输的信息并不是直接到达服务器的,还需要经过很多中间设备的转发.在这些中间设备上转发的速率同样会影响整体的发送速率,也就是影响发送方滑动窗口的大小. ? ? ? ? 而要想衡量在中间设备上的接收和发送速率,无疑是一件很困难的事情.因为我们既不知道中间设备有几个,也不知道具体有哪些设备,更不知道这些设备的参数是什么.因此我们只能通过做实验的方法来获得一个合适的滑动窗口大小,这就是拥塞控制. ? ? ? ? 我们按照如下步骤进行拥塞控制的实验: ? ? ? ? 1.起初以较小的窗口发送数据(因为网络环境未知). ? ? ? ? 2.如果丢包率低,则说明当前网络环境情况较好,比较畅通.此时我们可以逐渐增大窗口大小. ? ? ? ? 3.当窗口大小增大至出现丢包时,说明窗口大小已经接近阈值了.此时我们应该减小窗口的大小. ? ? ? ? 不断重复第2和第3步,我们就能测试出一个合适的窗口大小. ? ? ? ? 我们可以用一张坐标图来形象的描述这个过程: ? ? ? ? 这张图在教科书上很常见.首先以较小窗口开始(慢开始).然后以指数增长的形式扩大窗口,扩大到一定程度后,改为线性增长直到网络出现拥堵,发生丢包.此时我们重新回到慢开始阶段,将这次指数增长的边界点设为上次出现丢包时窗口大小的一半.重复上述过程.? ? ? ? ? 上述介绍的拥塞控制和流量控制都能影响发送方的滑动窗口大小.因此,最终的滑动窗口大小,就取决于流量控制下的窗口与拥塞控制下的窗口的较小值. 延时应答与捎带应答? ? ? ? 延时应答与流量控制一样,是用于提升发送方发送速率的机制----它可以增大发送方滑动窗口的大小.而捎带应答是在延时应答的基础上,对报文发送的一种简化机制. ? ? ? ? 上面讲到接收方返回的ACK报文会告诉发送方当前接收缓冲区剩余空间的大小.那么,当接收方收到发送方传来的一组数据时,倘若接收方不立即返回ACK报文,而是等待一会,让接收缓冲区的内容消耗一点之后再发送ACK报文.这样就可以得到一个更大的滑动窗口,从而提升发送的速率.(最显著的作用就是可以减少接收缓冲区为满的情况).我们用一张简图来阐述延时应答的效果: ? ? ? ? ? 在服务器收到data1之后,并不立即返回ACK.而是等待一小段时间(图中有标注),在收到data2之前将ACK返回即可.这样可以让客户端获得一个更大的滑动窗口大小.提升发送速率. ? ? ? ? 延时应答则是借助了上述的原理.回顾一下"四次挥手"的原理: ? ? ? ? ? ?我们提到过服务器的ACK和FIN不能合并发送的原因是二者发送的原理不同:前者是由操作系统自动发送的,而后者是在代码中调用close()方法发送的. ? ? ? ? 但是有了延时应答事情就会变的有所不同,我们可以将ACK的发送时间推迟,推迟到和FIN的发送时机一致.这样我们就可以将两个报文合并成一个报文发送,省去一步分装和分用,节约更多资源. 异常处理? ? ? ? 主机关机/程序崩溃? ? ? ? ? ? ? ? 主机关机和程序崩溃对于TCP端来说没有区别.都会杀死所有的用户进程,这意味着会释放TCP进程中的PCB,也意味着释放文件描述符表上所有的文件资源.这其中也包括socket文件,关闭socket文件会调用socket.close()方法.这个时候就会触发"四次挥手"过程. ? ? ? ? ? ? ? ? 如果在"四次挥手"结束后主机才彻底关闭,这种情况无事发生.而如果在"四次挥手"过程中主机就已经彻底关闭了,那此时另一端会不断重传它的FIN,一直收不到ACK,也就放弃了. ? ? ? ? 主机掉电? ? ? ? ? ? ? ? 接收方掉电? ? ? ? ? ? ? ? ? ? ? ? 发送方尝试发送数据,收不到ACK.不断触发超时重传,仍然无ACK.此时发送方会尝试重新建立连接,也就是"三次握手".仍然失败,此时发送方就会放弃. ? ? ? ? ? ? ? ? 发送方掉电? ? ? ? ? ? ? ? ? ? ? ? 这时情况有所不同,由于接收方无法判断没有数据发过来,究竟是发送方还没发,还是发送方挂了.所以每当一段时间后接收方没有收到数据时,接收方就会给发送方发送一个特殊的报文(ping),用于探测发送方是否还在正常工作.如果发送方还在正常工作,它会给接收方返回一个同样特殊的报文(pong).如果发送方挂了,流程类似接收方掉电的部分----接收方重传不得就放弃. ? ? ? ? ? ? ? ? ? ? ? ? 我们把上述确认机制称为"心跳包"机制. ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ????????????????? ? ? ? ???????? |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 21:15:58- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |