【发布时间】:2016-02-13 18:53:54
【问题描述】:
如果H1打开到H2的TCP连接,那么H1发送一个小包(
TCP 如何确保 H1 的 RTO 计时器在收到延迟的 ACK 之前不会过期?
Linux默认最小RTO为200ms是否正确理解,所以如果网络很快(RTO保持在最小200ms)并且数据包在从H1到H2的途中丢失,那么H1会在200ms内重试?如果网络很慢,那么 H1 的等待时间可能会超过 200ms?
【问题讨论】:
如果H1打开到H2的TCP连接,那么H1发送一个小包(
TCP 如何确保 H1 的 RTO 计时器在收到延迟的 ACK 之前不会过期?
Linux默认最小RTO为200ms是否正确理解,所以如果网络很快(RTO保持在最小200ms)并且数据包在从H1到H2的途中丢失,那么H1会在200ms内重试?如果网络很慢,那么 H1 的等待时间可能会超过 200ms?
【问题讨论】:
关于延迟的 ACK 时间,RFC 1122 说:
一个 TCP 应该实现一个延迟的 ACK,但一个 ACK 不应该 被过度延误;特别是,延迟必须是 不到 0.5 秒,并且在全尺寸的流中 段应该至少每秒有一个 ACK 段。
所以它取决于实现,当然也取决于是否会降低应用程序性能。
在 linux 内核中,它们不会按计时器或固定间隔发送延迟的 ACK,正如您在以下代码中所见,它们的行为因条件而异。
正如您在net/ipv4/tcp_input.c 中看到的,他们在评论中说:
在分析数据时必须牢记一些事情 tp->ato 延迟确认超时间隔的行为。当一个 连接启动,我们希望尽快确认。这 问题是“好”的 TCP 在数据开始时启动缓慢 传播。这意味着在我们发送前几个 ACK 之前 发件人将坐在他的一端,只排队他的大部分数据,因为 他只能在任何给定时间发送 snd_cwnd 未确认的数据包。为了 我们发送的每个 ACK,他都会增加 snd_cwnd 并传输更多他的 队列。 -戴夫M
static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
u32 now;
inet_csk_schedule_ack(sk);
tcp_measure_rcv_mss(sk, skb);
tcp_rcv_rtt_measure(tp);
now = tcp_time_stamp;
if (!icsk->icsk_ack.ato) {
/* The _first_ data packet received, initialize
* delayed ACK engine.
*/
tcp_incr_quickack(sk);
icsk->icsk_ack.ato = TCP_ATO_MIN;
} else {
int m = now - icsk->icsk_ack.lrcvtime;
if (m <= TCP_ATO_MIN / 2) {
/* The fastest case is the first. */
icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + TCP_ATO_MIN / 2;
} else if (m < icsk->icsk_ack.ato) {
icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + m;
if (icsk->icsk_ack.ato > icsk->icsk_rto)
icsk->icsk_ack.ato = icsk->icsk_rto;
} else if (m > icsk->icsk_rto) {
/* Too long gap. Apparently sender failed to
* restart window, so that we send ACKs quickly.
*/
tcp_incr_quickack(sk);
sk_mem_reclaim(sk);
}
}
icsk->icsk_ack.lrcvtime = now;
tcp_ecn_check_ce(tp, skb);
if (skb->len >= 128)
tcp_grow_window(sk, skb);
}
【讨论】:
net/ipv4/tcp_output.c,我想你可以在那里找到答案