常见面试题:TCP的四次挥手和TCP的滑动窗口
说一说 TCP 的四次挥手。
挥手即终止 TCP 连接,所谓的四次挥手就是指断开一个 TCP 连接时。需要客户端和服务端总共发出四个包,已确认连接的断开在 socket 编程中,这一过程由客户端或服务端任意一方执行 close 来触发。这里我们假设由客户端主动触发 close。四次挥手的流程如图:
数据传送完毕之后呢,双方都可释放连接。
最开始的时候,客户端和服务端都处于 establish 的状态。然后客户端主动关闭,服务器被动关闭,首先客户端进程发出连接释放报文,并且停止发送数据。在该数据报的报头中呢?TCP flags 中的 finish 就等于1,我们这里假设此时的客户端定义的序列号。为 seq=u, 该值等于前面 establish 状态下数据最后一次发送的时候。已经传送过来的数据的最后一个字节的序号,加上一。此时客户端就进入了 finish wait 1 这么一个终止等待一的状态。TCP 规定。即使并报文段不携带任何数据,也要消耗掉一个序号。回执的时候,它的义务会加一就像这里。那当我们的服务器收到连接释放报文了之后,也要发出确认报文及 ack=1。这里作为回应,我们的小写的 ack,它的 sequence number 也就成为了 u+1。并且也携带上了自己的序列号,就是我们的这个 seq=v。此时服务端就进入了 close wait 这么一个关闭等待的状态。这个状态比较重要,大家呢在这里呢,先留下一个印象 TCP 服务器通知高层的应用进程,客户端要释放跟服务器通信的连接。这时候会处于半关闭的状态,即客户端已经没有数据要发送了,
但是服务器若要发送数据,客户端还是能够接受的。这个状态还要持续一段时间,该时间等于整个 close bay 状态所持续的时间,那客户端收到服务器的确认请求后,也就是第二次挥手的时候呢。此时客户端就进入了 finish wait 2 及终止等待二这个状态。等待服务器发送,释放连接报文,就是等待它发送第三次回收的请求。因此在这段时间内呢,还有可能还需要接受服务器发送的最后的数据,服务器将最后的数据发送完毕后呢。就会向客户端呢发送连接释放报文,这里就是 finish=1, ack 还是等于 u+1。由于在半关闭的状态,服务器很可能又发送了一些数据,此时的序号呢,我们就已经变为了 w。此时服务器就进入了 last ack 的这么一个状态了啊,在发送这个 finish 报文的时候,它就进入了 last ack 及最后确认的状态。等待客户端的最终确认,客户端在收到服务器的连接,释放报文之后必须发出确认。即 ack=1。然后再将服务器发过来的这个 w 变成 w+1 回发回去,通过这个小 ack 回发回去,而自己的序号呢?此时我们假定为 u。那么,它就是 u+1,而自己的序号呢?也就是按照之前的报文的序号加一就是u+1。此时客户端就进入了 time wait。即时间等待的状态,注意此时客户端的 TCP 连接还没有释放,必须经过二乘上 msl 的时间后呢。我们的这个连接才真正的释放,才进入到 close 的状态。MSL 及最长报文段寿命。rfc 793 定义了 msl 的值为两分钟,而 linux 设置成了 30 秒,而咱们的服务器只要收到了客户端发出的确认。立即就进入 close 的状态了,可以看到服务器结束 TCP 的连接时间要比客户端稍早一些。以上便是四次挥手的主要流程。
总结
咱们来总结一下 TCP 采用四次挥手来释放连接,在第一次挥手的过程中呢,client 向 server 呢发送了一个报文。用来关闭 client 到 server 的数据传送。 client 呢,就进入到了 finish wait 1 这么一个状态当中。在第二次挥手的过程中,我们的这个 server 在收到了 client 发来的包文之后。会发送一个 ack 给 client,并且我们的小写的 ack 及确认序号,就是我们之前收到的这个 client 发来的。序号加一。那 server 就会进入到了 close wait 的状态。在第三次挥手的过程中,server 又继续向 client 发送了一个 finish 的数据包。用来关闭 server 到 client 的数据传送。server 就进入了到了 last ack 的状态,那在第四次挥手的过程中,client 在收到 server 发来的 fin 包之后呢?client 就会进入到了摊位状态,接着发送一个 ack 给 server 确认序号。之前收到的这个 server 序号加上一。这我就进入到了 close 状态,那 client 在等待 2 msl 的时间之后也会进入到 close 的状态。从而完成四次挥手。
我们注意到,在 TCP 的四次挥手的状态图中,从 time wait 到 close 状态有一个超时设置。这个超时设置是二乘上 msl。那为什么需要等待这一段时间?为什么不直接给转换成 close 状态?主要有两个原因,第一个原因呢就是time状态呢,它是用来保证有足够的时间让对端收到 ack。如果被动关闭的那方没有收到 ack 呢,就会触发被动端重发 finish 包,一来一去正好是两个msl。第二点就是有足够的时间让这个连接不会跟后面的连接混在一起,因为有些路由器会缓存 IP 数据包。如果连接被重用了,那么这些延迟收到的包呢?就有可能会跟新连接混在一起,
那咱们为什么需要四次回收来中断连接呢?前面我们说过全双工的意思是允许数据在两个方向上同时传输及待同一时间服务器可以发送数据给客户端,客户端也可以发送数据给服务器。而因为 TCP 是全双工的,所以发送方和接收方都需要 finish 报文和 ack 报文,也就是说。发送方和接收方各只需两次挥手即可,只不过有一方是被动的,所以看上去就成了所谓的四次挥手。
TCP滑动窗口
面试过程中经常会被问到TCP 的滑动窗口,想要回答这个问题,咱们先来弄清楚两个 TCP 概念。那就分别是RTT和RTO。
RTT和RTO
- RTT:发送一个数据包到收到对应的ACK,所花费的时间
- RTO:重传时间间隔
再通俗的讲,就是我一开始预先算一个定时器时间,如果你回复了 ack,那重传定时器就自动失效,也就是说不用重传了。如果没有回复给我 ack,然后 RTO 定时器的时间又到了,我就重传,由于 RTO 是本次发送当前数据包所预估的超时时间。那么 RTO 就需要一个很好的算法来统计,来更好的预测这次的超时时间。RTO 不是固定写死的配置,而是经过 RTT 计算出来的。有了 RTT 才能计算出 RTO。基于 RTO,我们便有了重传机制,才能支撑起咱们接下来要介绍的滑动窗口。
TCP使用滑动窗口做流量控制和乱序重排
- 保证TCP的可靠性
- 保证TCP的流控特性
前面我们了解到 TCP 会将数据拆分成段进行发送,出于效率和传输速度的考虑,我们不可能等一段一段数据去发送,等到上一段数据被确认之后再发送下一段数据。这个效率呢是非常低的。
我们是要实现对数据的批量发送。TCP 必须要解决可靠传输以及包乱序的问题。所以 TCP 需要知道网络实际的数据处理带宽或是数据处理速度,这样才不会引起网络拥塞导致丢包。TCP 使用滑动窗口及 sliding window 呢,做流量控制与乱序重排 TCP 的滑动窗口主要有两个作用。
第一个作用是提供 TCP 的可靠性,第二个作用是提供 TCP 的流控特性。前面咱们学习的 TCP 报文头里面呢,有一个字段叫做 window ,我们也可以叫做 advertise window,用于接收方通知发送方自己还有多少缓冲区可以接收数据发送方,根据接收方的处理能力来发送数据不会导致接收方处理不过来。这便是流量控制,同时滑动窗口机制还体现了 TCP 面向自节流的设计思路。咱们来大致了解一下窗口有关数据的计算过程。
窗口数据的计算过程
如图所示,左图是 TCP 协议的发送端缓冲区,而右图是接收端缓冲区。左边往右边发数据,两个图中下面的长方形表示要发送的数据流里面假设装满了数据,并且需要按照顺序从左向右发送或者接收。咱们假设对应的数据段位置序号也是从左到右去增长的,对于发送方来讲呢?lastbyetacked指向收到的连续最大的 ack 的位置,也就是从左端算起,连续已经被接收端的程序呢,发送 ack 回执,确认已收到的这个 sequence number。而 lastbytesend 呢指向已发送的最后一个字节的位置,该位置呢,只是发出去了,但是还没有收到 ack 的回应,而 last byWritten指向上层应用,已写完的最后一个字节的位置及当前程序已经准备好的,需要发送到最新的一个数据段,这段是发送出去,但是还没有收到确认的这段呢,是已经发送出去,并且已经收到接收端的确认了的,我们可以看到从 lastbyeack到 lastbywritten都是没有出现间隔的,都是连续的,对于接收方来讲 last by read 指向上层应用已经读完的最后一个字节的位置,也就说我收到了发送方的数据。并且已经处理,并且给他回执了的数据的最后一个位置,
而 Next by expected 指向收到的连续最大的 sequence 的位置。比如说这段呢,我是已经收到了,但是还没有给你发送回执,而 last by receive 呢,是指向已收到的最后一个字节的位置。可以看到 next by expected 和 last by receive 中间呢,有一些 sequence 还没有到达,对应的是空白的区域。此时,咱们可以根据上面的数值计算出接收方的 advertise window 的大小,
之后呢,回发给发送方,让其计算出。发送方的剩余可发送的数据大小及 effective window 的大小,一个是 advertise window,就我能够接受的窗口,一个是你还可以发送的数据及effective window。此时我们的 advertise window 及接收方还能处理的数据的量是可以通过这个公式来计算的,其中 max receive buffer是指接收方能接收的最大数据量,也可以理解为接收端缓存池的大小。而 last by receive-last by read 就表示的是我们当前接收方已未接收到的数据,或者还没有接收到的.
TCP会话的发送方
滑动原理如图所示了,我们先从原滑动窗口来说起,它是虚线部分组成。前面我们已经知道滑动窗口里面包含了已经发送但是还没有收到接收端的确认的数据,以及还没有发送但是,允许向接收方发送的数据,咱们假设原先的滑动窗口的边界呢?是从 32 到 51。我们假设已发送但未被确认的序号呢是 32 到 40。此时,如果 32 和 33 都没有被确认,而 34 被确认的。咱们的这个窗口也不会向右滑动,只有等到 32 到 34 都被确认之后及连续被确认之后,滑动窗口才会被移动。那在此时,没被移动之前咱们需要大于或者等于 52 的数据及窗口外的数据是不能被发送的。此时,我们假设从 32 到 35 都被确认了,只要滑动窗口会向右移动四位到 36 这个位置的地方。进而我们的程序就能发送后面的 52 到 55 的数据了。对于 TCP 的接收方来讲,在某一时刻,在它的接收缓存内呢,会存在三种状态。
第一种状态是已接收并且已发送回执的状态。第二种是未接收但是可以接收,也就是准备接收的这种状态。第三个是未接收,并且不能接收的状态,因为达到了窗口的预值了,是不能接收的。那由于 ack 直接由 TCP 站回复默认,没有应用延迟,不存在已接收,但是未回复 ack 的这种状态。其中未接收并且准备接收的这一段空间呢,就称为接收窗口了。由于接收窗口的滑动机制和前面发送方的一致,这里我们就不做重复讲解了。
TCP会话的接收方
经过上面的讲解,我们得知 TCP 最基本的传输可靠性来源于确认重传机制。 TCP 的滑动窗口的可靠性也是建立在卷重传基础上的。发送窗口只有收到接收端,对于本段发送窗口内字节的 ack 确认才会移动发送窗口的左边界。接收窗口只有在前面所有的段都确认的情况下才会移动左边界,当在前面还有字节未接收。但收到后面的字节的情况下呢,窗口是不会移动的,并不对后续字节确认,以此确保对端。会对这些数据呢进行重传。以上便是滑动窗口的基本原理,滑动窗口的大小可以依据一定策略动态调整。应用会根据自身的处理能力的变化,通过本端 TCP 接收窗口大小的控制来实现对端的发送窗口进行流量限制。
如果本文对你有帮助,可以关注我,第一时间获取更多精彩文章。读者小伙伴也可以在我的个人网站:程序员波特,获取更多Java相关技术系列教程,共享电子书、Java学习路线、视频教程、简历模板和面试题等学习资源。
相关文章:

常见面试题:TCP的四次挥手和TCP的滑动窗口
说一说 TCP 的四次挥手。 挥手即终止 TCP 连接,所谓的四次挥手就是指断开一个 TCP 连接时。需要客户端和服务端总共发出四个包,已确认连接的断开在 socket 编程中,这一过程由客户端或服务端任意一方执行 close 来触发。这里我们假设由客户端…...

力扣随笔之两数之和 Ⅱ -输入有序数组(中等167)
思路:在递增数组中找出满足相加之和等于目标数 定义左右两个指针(下标)从数组两边开始遍历,若左右指针所指数字之和大于目标数,则将右指针自减,若左右指针所指数字之和小于目标数,则左指针自加&…...
最优传输(Optimal Transport)
最优传输(Optimal Transport)是一种数学理论和计算方法,用于描述两个概率分布之间的距离或者对应关系。它的核心概念是如何以最佳方式将一组资源(如质量、能量等)从一个位置传输到另一个位置。 基本概念: …...

MIT-6.824-Lab2,Raft部分笔记|Use Go
文章目录 前记Paper6:RaftLEC5、6:RaftLAB22AtaskHintlockingstructureguide设计与编码 2BtaskHint设计与编码 2CtaskHint question后记 LEC5:GO, Threads, and Raftgo threads技巧raft实验易错点debug技巧 前记 趁着研一考完期末有点点空余…...

使用openeuler 22.03替代CentOS 7.9,建立虚拟机详细步骤
进入浏览器搜索网址下载openeuler 22.03镜像文件 https://mirrors.huaweicloud.com/openeuler/openEuler-22.03-LTS-SP3/ISO/x86_64/openEuler-22.03-LTS-SP3-x86_64-dvd.iso 打开VMware Workstation新建一个虚拟机: 自定义虚拟机位置 加入下载好的openeuler镜像文件…...

代理技术引领出海征程
在数字娱乐的繁荣时代,游戏开发者和发行商们意识到,要在全球市场立足,必须迈向国际化的出海之路。然而,这一旅程面临着跨越网络壁垒、适应多元文化和提升全球连接性的巨大挑战。本文将深入探讨代理技术在游戏行业出海过程中的创新…...

谷粒商城篇章9 ---- P248-P261/P292-P294 ---- 消息队列【分布式高级篇六】
目录 1 消息队列(Message Queue)简介 1.1 概述 1.2 消息服务中两个重要概念 1.3 消息队列主要有两种形式的目的地 1.4 JMS和AMQP对比 1.5 应用场景 1.6 Spring支持 1.7 SpringBoot自动配置 1.7 市面上的MQ产品 2 RabbitMQ 2.1 RabbitMQ简介 2.1.1 RabbitMQ简介 2…...

【Spring连载】使用Spring Data访问 MongoDB(五)----生命周期事件
【Spring连载】使用Spring Data访问 MongoDB(五)----生命周期事件Lifecycle Events 一、实体回调Entity Callbacks1.1 实现实体回调1.2 注册实体回调 二、特定存储的实体回调 一、实体回调Entity Callbacks 1.1 实现实体回调 1.2 注册实体回调 二、特…...

JavaSec 之 SQL 注入简单了解
文章目录 JDBC 注入语句拼接(Statement)修复方案 语句拼接(PrepareStatement)修复方案 预编译 JdbcTemplate修复方案 MyBatisLike 注入Order By 注入In 注入 寒假学了一个月 pwn,真心感觉这玩意太底层学的我生理不适应了,接下来学一段时间 java 安全缓一…...

第十一章——期约与异步函数
ECMAScript 6及之后的几个版本逐步加大了对异步编程机制的支持,提供了令人眼前一亮的新特性。ECMAScript 6新增了正式的Promise(期约)引用类型,支持优雅地定义和组织异步逻辑。接下来几个版本增加了使用async和await关键字定义异步…...

工具方法合集-utils.js
通用 import get from lodash.get import cloneDeep from lodash.clonedeep // 深度clone export function deepClone(obj) {return obj ? cloneDeep(obj) : obj } export function lodashGet(obj, key, defaultValue = ) {//这个 defaultValue 不能给默认 值 会报错;retur…...

安卓11-设置HDMI分辨率流程
安卓11中从设置-显示设置hdmi分辨率流程:framework层通过jni控制底层驱动实现,标准驱动模型 packages\apps\Settings\src\com\android\settings\display\HdmiSettings.javaprivate void updateResolution(final ITEM_CONTROL control, final int index) {showWaitin…...

Vue3+vite搭建基础架构(11)--- 菜单栏功能和Tab页功能实现
Vue3vite搭建基础架构(11)--- 菜单栏功能和Tab页功能实现 说明删除项目中不需要的文件userStore全局属性代码菜单栏代码Tab页代码解决浏览器输入地址时不会打开tab页问题和切换tab页时参数丢失问题 说明 这里记录下自己在Vue3vite的项目实现菜单栏功能和…...

餐饮神秘顾客公司:关于餐饮行业神秘顾客调查注意事项
在餐饮业,顾客体验往往决定品牌的成败。为深入了解顾客需求和感受,许多餐饮企业引入“神秘顾客”调查。然而,此调查并非简单走过场,其中细节和注意事项颇多。餐饮行业神秘顾客调查需注意以下几点: 1. 专业培训&#x…...

概率密度函数(PDF)与神经网络中的激活函数
原创:项道德(daode3056,daode1212) 在量子力学中,许多现象都是统计的结果,基本上用的是正态分布,然而,从本质上思考,应该还存在低阶的分布,标准的正态分布是它的极限,这样一来,或许在…...

.netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
1、SqlSugarCore 相关 1.1 主项目添加数据,否则会报数据库连接错误: <InvariantGlobalization>false</InvariantGlobalization> <PropertyGroup><TargetFramework>net8.0</TargetFramework><Nullable>enable</…...

算法打卡day2|数组篇|Leetcode 977.有序数组的平方、 209.长度最小的子数组、59.螺旋矩阵II
算法题 Leetcode 977.有序数组的平方 题目链接: 977.有序数组的平方 大佬视频讲解:977.有序数组的平方 个人思路 第一时间就只想到暴力解法,双重循环一个循环比较一个循环赋值;但这样可能会超时,所以还能用双指针࿰…...

Hive【内部表、外部表、临时表、分区表、分桶表】【总结】
目录 Hive的物种表结构特性 一、内部表 建表 使用场景 二、外部表 建表:关键词【EXTERNAL】 场景: 外部表与内部表可互相转换 三、临时表 建表 临时表横向对比编辑 四、分区表 建表:关键字【PARTITIONED BY】 场景: 五、分桶表 …...

随手写的小程序2 一个nc能控制的程序
小程序源代码 下载: https://download.csdn.net/download/nn_84/88846445?spm1001.2014.3001.5501 请下载 Qt 5.12.12 server.pro : QT gui networkCONFIG c11 console CONFIG - app_bundle# You can make your code fail to compile if it uses deprecated APIs. # In o…...

Android中通过属性动画实现文字轮播效果
前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。 👉点击跳转到教程 一、创建一个自定义ProvinceView类,具体代码如下 /*** Author: ly* Date: 2024/2/22* D…...

最长的回文串
开始想的简单了,确实没想到奇数字母删去一个后也能用 解法: 桶排序 #include<iostream> #include<vector> #include<algorithm> using namespace std; #define endl \n #define int long long signed main() {int t;cin >> t…...

2023 H1 中国边缘公有云服务市场 Top2,百度智能云加速推动分布式云智能化升级
近期,IDC 发布了《中国边缘云市场跟踪研究 2023 H1》。报告显示,2023 上半年,中国边缘公有云服务市场规模 24.3 亿元,同比增速达到 41.8%。 其中,百度智能云以 15.7% 的市场份额位列中国边缘公有云服务市场第二&#…...

Emlog博客网站快速搭建并结合内网穿透实现远程访问本地站点
文章目录 前言1. 网站搭建1.1 Emolog网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2.Cpolar稳定隧道(云端设置)2.3.Cpolar稳定隧道(本地设置) 3. 公网访问测试总结 前言 博客作为使…...

力扣经典题目解析--旋转图像(字节二面)
题目 原题地址: . - 力扣(LeetCode) 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1࿱…...

【ARMv8M Cortex-M33 系列 8.1 -- RT-Thread 堆内存 检查命令 free 实现及介绍】
文章目录 RT-Thread 堆内存 检查命令 free 实现及介绍rt_memory_info 函数验证 RT-Thread 堆内存 检查命令 free 实现及介绍 在RT-Thread系统中,通常可以通过rt_memory_info函数获取当前的堆内存使用信息,然后你可以包装这个函数来显示剩余的堆空间。rt…...

milvus Delete API流程源码分析
Delete API执行流程源码解析 milvus版本:v2.3.2 整体架构: Delete 的数据流向: 1.客户端sdk发出Delete API请求。 from pymilvus import (connections,Collection, )print("start connecting to Milvus") connections.connect("default", host"192…...

CentOS使用Docker搭建Halo网站并实现无公网ip远程访问
🔥博客主页: 小羊失眠啦. 🎥系列专栏:《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞👍收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默&…...

【JVM】垃圾回收算法
📝个人主页:五敷有你 🔥系列专栏:JVM ⛺️稳中求进,晒太阳 垃圾回收算法 Java是如何实现垃圾回收的呢?简单来说,垃圾回收就做两件事 找到内存中存活的对象释放不在存活对象的内存&…...

如何和将原始request的Header中的值传递给openfeign请求的Header? 以及又如何获取openfeign请求中Header中的值
如何和将原始request的Header中的值传递给openfeign请求的Header? 以及又如何获取openfeign请求中Header中的值 如何和将原始request的Header中的值传递给openfeign请求的Header参考 [https://www.jb51.net/article/282522.htm](https://www.jb51.net/article/28252…...

Flink 侧输出流(SideOutput)
🌸在平时大部分的 DataStream API 的算子的输出是单一输出,也就是某一种或者说某一类数据流,流向相同的地方。 🌸在处理不同的流中,除了 split 算子,可以将一条流分成多条流,这些流的数据类型也…...