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

TCP 相关机制

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

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

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

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

后发先至

image.png

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

出现原因

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

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

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

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

1. 确认应答

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

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

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

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

超时时间的设定

这里的时间不是固定不动的,而是动态变化的
发送方第一次重传,超时时间是 t1,如果重传之后,仍然没有 ACK,就会继续重传,第二次重传的超时时间是 t2t2>t1

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

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

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

TCP 报头

image.png|449

首部长度

TCP 报头的长度

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

保留(6 位)

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

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

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

选项

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

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

序号

由于会出现“后发先至”的情况,所以需要通过编号,区分出数据的先后顺序
序号:表示的就是 TCP 数据报载荷中的第一个字节的序号,由于序号是连续递增,知道了第一个字节的序号,后续每个字节的序号也就知道了
image.png|565

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

确认序号

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值