【TCP】确认应答、超时重传机制和TCP报头

简介: 【TCP】确认应答、超时重传机制和TCP报头

TCP 相关机制

TCP 基本特点:有连接、可靠传输、面向字节流、全双工

  • 有连接、面向字节流和全双工都能在前面的代码中体现
  • 有连接:必须要先调用 accept 建立联系才能处理
  • 面向字节流:会拿到 clientSocket 对象的 InputStreamOutputStream,再来去读写数据
  • 全双工:一个 Socket 对象,既可以读,也可以写
    而此处的可靠传输在代码层面感知不到,它是在系统内核完成了这里的工作

TCP 最核心的资质就是“可靠传输”,不能做到 100% 送达,只能尽可能的是数据能到达对方方

  1. 能感知到对方是否收到
  2. 如果发现对方没有收到,就要进行重试

后发先至

  • 如果数据按照左边的传输次序来的,那就不会有歧义
  • 虽然女神先发的“好啊好啊”,后发的“滚”,但是在网络传输中,可能存在“后发先至”,对于我们接收方来说,可能会先收到“滚”,后收到“好啊好啊”
  • 此时,歧义就产生了,我将会误以为女神答应我了

出现原因

互联网最初是用来防御核弹打击的,即使是遭受到了核弹打击,但是 A~B 之间的通信路径有很多很多条,不会全军覆没,所以仍然可以确保数据能正常传输

  • 在正常传输数据的时候,传输数据包不一定走同一条路线
  • 因为是不同的路线,每个数据包传输过程中,遇到的状况也都有差别,最终达到目标的时序就可能存在差异了

举个例子:一个婚车队伍,在触发的时候都是按照顺序,一辆一辆跟着走的,但一到大路上,车队就可能会走散

  • 本来跟着头车走,结果遇到了红灯,一旦跟不上,就各凭本事了
  • 每辆车,各个导航,可能走的路线都不一样,可能有的车还会堵车
  • 最后到达目的地的先后顺序改变的情况就是很常见的

1. 确认应答

后发先至的解决方法,TCP 核心机制,感知对方是否收到,就是要靠对方告诉你一声“收到了”

  • 后发先至是客观存在的情况,无法改变
    若要解决,可以给传输的数据添加“编号”,通过编号,可以区分出数据的先后顺序
  • 我收到的应答报文,即使顺序出现错乱,也能识别出来原意
  • 由于 TCP 是面向字节流的,是加上这里的编号并非是按照“第一条,第二条”这样的方式来编排的,而是按照“字节“,“第一个字节,第一百个字节”,这样来编排的
  • 每个字节都有一个独立的编号,字节和字节之间,编号是连续的、递增的
  • 这种按照字节编号的机制,就成为“TCP 的序号”,在应答报文中,针对之前收到的数据进行对应的编号,称为“TCP 的确认序号

之后,TCP 就可以针对接收方收到的信息,进行重新排序,确保应用程序 read 到的数据一定是和发送方的数据顺序是一致的

接收方这边调用 read 的时候如果没有数据,就会阻塞等待(前面回显服务器文章中写的是 scanner 读取,本质上就是调用 InputStream.read

  • 接收方收到的数据信息顺序可能和发送发传输时的顺序不一样
  • 此时接收方收到 1001-2000 这个数据到了,但是接收方不会让 read 接触阻塞,因为这个数据的起始部分还没到,2001-3000 到了之后也进行阻塞
  • 直到1-1000这个数据到达之后,read才会接触阻塞,才会读取到1-10001001-20002001-3000
  • 接收方这边,操作系统内核里面,有一段空间,作为“接收缓冲区”,收到的数据就会先在缓冲区中排队等待,直到开头的数据到了,应用程序才能真正读取到里面的数据

前面接亲的例子:等婚车到了女方门口,因为头车还没来,所以不能直接开到新娘门口去接人

  • 因此这样的车就得在外等待,必须等头车到
  • 等车到了之后,再重新排好队,再一起开到新娘家门口

丢包

丢包的原因有很多种:

  1. 数据传输的过程中,发生了 bit 翻转,收到这个数据的接收方/中间的路由器什么的,计算校验和,但是发现校验和对不上
    发现错误,要及时止损,不能将错就错
  • 所以就会把这个数据丢弃掉,不继续往后转发/不交给应用层使用

  1. 数据传输到某个节点(路由器/交换机),但这个节点的负载太高了,后续传输过来的数据就可能被这个路由器直接丢弃
  • 负载太高:某个路由器,单位时间只能转发 N 个包,但现在是网络高峰期,这个路由器单位时间需要转发的包超过 N 个了,发不过来了

2. 超时重传

发生丢包是完全随机,不可预测的,TCP 再怎么厉害,也不可能避免数据发生丢包。TCP 能做的是:感知到数据是否丢失,如果丢包,就重新再发一次

此时需要通过应答报文来区分

  • 收到应答报文,说明数据没丢包
  • 没收到应答报文,就说明数据丢包了
  • 网络传输是需要消耗时间的,这里的“没收到”是暂时没收到,还是永远都收不到?
  • 发送方发送数据之后,会给出一个“超时时间”,如果在这个时间限制之内,没有收到反馈的 ACK(应答报文 ACK 由 0 变为 1),就视为数据丢包了

    不管是因为数据丢了导致的丢包还是因为 ACK 丢了导致的,都会重发
  • 但如果是因为 ACK 丢了导致的重发,接收方就会收到两份一样的数据,这样是很不好的(一次扣款请求扣两次)
  • 所以为了确保应用程序调用read读出来的数据是唯一、不重复的,TCP 就会对这种情况进行处理——去重
  • 接收方有一个“接收缓冲区”,收到的数据会先进入到缓冲区里,后续再督导数据,就会根据序号,在缓冲区中找到对应的位置(排序)。如果发现当前序号 1-1000 这个数据已经在缓冲区中存在了,就会直接把新收到的这个数据丢弃掉

超时时间的设定

这里的时间不是固定不动的,而是动态变化的

发送方第一次重传,超时时间是 t1,如果重传之后,仍然没有 ACK,就会继续重传,第二次重传的超时时间是 t2t2>t1

  • 每多重传一次,超时时间的间隔就会变大,重传的频次会降低
  • 经过一次重传之后,就能让数据到达的概率提升很多
  • 反之,如果重传了几次,都没有顺利到达,说明网络的丢包率已经达到了一个非常高的程度——>网络发生了严重故障,大概率没法继续使用了
    重传也不会无休止的进行,当重传达到一定次数之后,TCP 不会再重传,就认为这个连接已经挂了
  • 先尝试进行“重置/复位连接”,发送一个特殊的数据包“复位报文”,尝试和对方重新进行连接
  • 如果网络这会恢复了,复位报文就会重置连接,使通信可以继续进行
  • 如果网络还有严重问题,复位报文也没有得到回应,此时 TCP 就会单方面放弃连接(发送方释放掉之前接收方的相关信息,这个连接诶也就没了)

确认应答和超时重传相互补充,共同构建了 TCP 的“可靠传输机制

  • 可靠传输机制不是靠“三次握手和四次挥手保证的”

TCP 报头

首部长度

TCP 报头的长度

  • UDP 协议报头固定就是 8 个字节
  • 对于 TCP 来说,报头长度是可变的
    4 个比特位可表示的范围:
  • 0000~1111——>0x0~0xF——>0~15
  • 此处的长度单位是 4 字节,不是字节(所以范围是 0~60 字节)

保留(6 位)

虽然现在不用,但是先把这个东西申请下来,以备不时之需。用于考虑未来的可扩展性

  • 充分吸取了 UDP 的教训,UDP 的报文长度字段,是没法扩展的
  • 如果未来某一天,TCP 需要新增属性或者谋和属性的长度不够用,就可以把保留位拿出来,进行使用
  • TCP 的结构不需要发生太大的改变,这样的升级就会容易很多

关于“可扩展性”也是属于编程的时候需要考虑到的一点,毕竟写的代码不可能写一份就能持续地使用。对代码做出调整,做出修改,是非常普遍、常见的情况

但是,

选项

TCP 报头边长的主要原因。四个字节为一个单位

  • 可以有, 也可以没有
  • 可有一个,也可有多个
    通过“首部长度”确定报头有多长,如果是两个四个字节长度就是两个选项,三个四个字节长度就是三个选项,以此类推

序号

由于会出现“后发先至”的情况,所以需要通过编号,区分出数据的先后顺序

序号:表示的就是 TCP 数据报载荷中的第一个字节的序号,由于序号是连续递增,知道了第一个字节的序号,后续每个字节的序号也就知道了

  • 32 位/四字节,表示的范围是 0~42亿9千万0~4G
  • 因为 TCP 是面向字节流的,所以一个 TCP 数据报和下一个 TCP 数据报携带的数据,是可以直接进行拼装的
  • 比如要传输一个特别大的数据,传输过程中,本身就会通过多个TCP数据报来进行携带,这些TCP数据报彼此之间携带的载荷都是可以在接受方自动拼起来的
  • 这样就不像 UDP 存在传输的上限,使用 UDP 传输大数据,就需要考虑调用这一次 send 操作,参数是否超过了 64KB,超过了就不行
  • 使用 TCP 的话就没关系,可以调用一次 write,也可以调用多次 write。无论怎么进行 write,在网络传输和对端接收的角度来看是没有任何差别的
  • 如果多次 write,传输的总数据量超过上述的 4G 也没关系,这里的数据序号是可以再从 0 开始重新设置的

确认序号

确认序号的设定方式,和后发先至中发短信的例子,略有差别

  • TCP 序号不是按照“一条两条”来编排的,而是按照“字节”来编排的

TCP 的确认序号这里,填写的是 1001,接收方收到的数据的最后一个字节序号的下一个序号

  • 表示的含义是 <1001 的序号的数据都收到了(TCP 序号是连续增长的)
  • 对于应答报文来说,“确认序号”就会按照收到的数据的最后一个字节序号+1 的方式来填写
  • 并且六个标志位中,第二个标志位(ACK)会设为1
  • 普通报文的 ACK0,应答报文的 ACK1
  • 如果是普通报文,序号是有效的,确认序号是无效的;如果是应答报文,序号和确认序号都是有效的
  • 应答报文的序号是另一套编号体系,和传输数据的序号是不一样的
  • 应答报文默认情况下是不携带数据的


相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
安全 Unix 开发工具
【git 实用指南】git 如何忽略文件权限
【git 实用指南】git 如何忽略文件权限
956 2
|
缓存 网络协议
TCP累计确认和延迟确认傻傻分不清?
TCP累计确认和延迟确认傻傻分不清?
1328 1
|
前端开发 JavaScript 定位技术
HTML中target的使用注意点
`target` 属性在 HTML 中主要用于 `&lt;a&gt;` 和 `&lt;form&gt;` 元素,指定链接打开或表单提交的位置。常见值包括 `_self`(当前窗口)、`_blank`(新窗口)、`_parent`(父框架)和 `_top`(整个窗口)。使用 `target=&quot;_blank&quot;` 时应加 `rel=&quot;noopener noreferrer&quot;` 以提高安全性。JavaScript 可动态设置 `target`,CSS 可增强样式。尽管 `target` 主要用于链接和表单,但在特定场景下也可应用于其他元素。
|
调度 数据安全/隐私保护
用户态和内核态 中断处理机制
用户态和内核态 中断处理机制
999 0
|
Java 数据库
request.setAttribute()详解
request.setAttribute()详解
1452 1
【实验篇】如何利用BFD与浮动路由联动实现链路自动切换?
【实验篇】如何利用BFD与浮动路由联动实现链路自动切换?
471 0
|
Linux 芯片 Ubuntu
Linux驱动入门 —— 利用引脚号操作GPIO进行LED点灯
Linux驱动入门 —— 利用引脚号操作GPIO进行LED点灯
|
索引
tar 与 cpio 的简单对比
本文对比了 tar 和 cpio 在打包、解包、文件构成及特性上的区别。
480 0