/* This routine deals with incoming acks, but not outgoing ones. */
/*
接收到一个ACK的时候,如果之前网络中没有发送且未确认的数据段,
本端又有待发送的数据段,说明可能遇到对端接收窗口为0的情况。
这个时候会根据此ACK是否打开了接收窗口来进行零窗口探测定时器的处理:
1. 如果此ACK打开接收窗口。此时对端的接收窗口不为0了,可以继续发送数据包。??? 那么清除超时时间的退避指数,删除零窗口探测定时器。
2. 如果此ACK是接收方对零窗口探测报文的响应,且它的接收窗口依然为0。那么根据指数退避算法,??? 重新设置零窗口探测定时器的下次超时时间,超时时间的设置和超时重传定时器的一样。
*/
//tcp_ack()用于处理接收到的带有ACK标志的段,会检查是否要删除或重置零窗口探测定时器。
static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
{
s/* We very likely will need to access write queue head. *//* We passed data and got it acked, remove any soft error
* log. Something worked...
*/
///* 清零探测次数,所以如果对端有响应ACK,实际上是没有次数限制的 */
sk->sk_err_soft = 0;
icsk->icsk_probes_out = 0;
tp->rcv_tstamp = tcp_time_stamp;
if (!prior_packets) /* 如果之前网络中没有发送且未确认的数据段 */
goto no_queue;
no_queue:
/* If data was DSACKed, see if we can undo a cwnd reduction. */
if (flag & FLAG_DSACKING_ACK)
tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit);
/* If this ack opens up a zero window, clear backoff. It was
* being used to time the probes, and is probably far higher than
* it needs to be for normal retransmission.
*/ /* 如果还有待发送的数据段,而之前网络中却没有发送且未确认的数据段,
* 很可能是因为对端的接收窗口为0导致的,这时候便进行零窗口探测定时器的处理。
*/ /* 如果ACK打开了接收窗口,则删除零窗口探测定时器。否则根据退避指数,给予重置 */
if (tcp_send_head(sk))
tcp_ack_probe(sk);
if (tp->tlp_high_seq)
tcp_process_tlp_ack(sk, ack, flag);
return 1;
invalid_ack:
SOCK_DEBUG(sk, "Ack %u after %u:%u\n", ack, tp->snd_una, tp->snd_nxt);
return -1;
old_ack:
/* If data was SACKed, tag it and see if we should send more data.
* If data was DSACKed, see if we can undo a cwnd reduction.
*/
if (TCP_SKB_CB(skb)->sacked) {
flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una,
&sack_state);
tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit);
tcp_xmit_recovery(sk, rexmit);
}
SOCK_DEBUG(sk, "Ack %u before %u:%u\n", ack, tp->snd_una, tp->snd_nxt);
return 0;
}