linux TCP协议(2)---重传与流量控制

前言:前面我们说过tcp是一种可靠的协议,可靠性是通过多种方式来保障的,本文要说明的的重传功能和流量控制就是其中的两种措施。因为网络环境复杂性和和不同网络栈实现的细微区别,导致tcp在这些机制上异常复杂,本文主要简单说明一下这两个问题,更加详细的可以参考《tcp/ip详解》。

一. tcp重传机制

数据包在传输过程中异常情况多样,tcp既然要保证通信的可靠性,就自然要考虑到发送失败而重新发送的问题,这就是重传。因为tcp对于发送都需要进行确认,因此,重传也主要是围绕确认这个问题展开的,如怎么判断包失败需要重传等。常见的导致重传的原因如下:

  1. 数据报在传输过程中丢失,可能是由于中间的设备或者链路问题。
  2. 接收方的确认报文丢失。
  3. 接收方未能发送出确认报文。

既然涉及到了这么多种的因素,那么tcp是如何判断需要重传的呢?通常,重传分为2类:

  1. 超时重传
  2. 快速重传

1.1 超时重传

超时重传是最基本的机制,在发送每个包时,都会开启一个超时定时器,同时放入重传队列,如果定时器超时仍然没收到ack消息,那么就会进行重传,重传的次数可以限制;如果在定时器超时前收到了ack消息,就把数据包从重传队列中删除。

那么既然是超时重传,那么定时器设置为多少合适呢?如果设置过小,可能会导致大量不必要的重传出现,反而可能会导致拥塞的发生;如果设置过大,则又会导致不能及时重传数据包。因此,对于定时器的超时时间是需要仔细斟酌的,通常使用测量给定连接往返时间(RTT)的方法来设置定时器。关于测量RTT的值的算法,可以参考《tcp/ip详解》的21.3章节,这里显然没能力说的比那里更好:-)

下面我们简单看一下代码的位置。重传需要定时器,定时器的初始化是在tcp协议初始化时一并完成的,在tcp_v4_init_sock()中,tcp_init_xmit_timers(sk);,进而,在其中

inet_csk_init_xmit_timers(sk, &tcp_write_timer, &tcp_delack_timer,
				  &tcp_keepalive_timer);

在这里,我们可以看到一并初始化保活定时器等三种定时器。这里的主要关心的定时器就是tcp_write_timer,然后到tcp_retransmit_timer()->tcp_retransmit_skb()->tcp_transmit_skb()。就是在超时后进行重传操作。重点在tcp_retransmit_skb()函数中。

1.2 快速重传

快速重传不是非等定时器超时后才进行重传,而是当接收方收到的数据包是不正常的序列号,那么接收方会重复把应该收到的那一条ACK重复发送,这个时候,如果发送方收到连续3条的同一个序列号的ACK,那么就会启动快速重传机制,把这个ACK对应的发送包重新发送一次。

二. 流量控制

话仍旧从tcp是一种可靠的通信协议开始,在说到可靠行时,可能大多数人会想到的是只要发送的接收端能接收就好,那么问题来了,如果发送端发送的特别快,而接收端处理的能力慢,会出现什么情况?大量的数据报积压在接收端,甚至被丢弃,进而也会引发重传等后果,所以,tcp做了一个很复杂却很有效的东西---流量控制。也就是对发送和接收进行控制,目前使用的就是大名鼎鼎的滑动窗口机制。

2.1 滑动窗口

滑动窗口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。不同的滑动窗口协议窗口大小一般不同。发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。下面上几张图:


posted @ 2017-07-31 22:22  AISEED  阅读(1300)  评论(0编辑  收藏  举报