当成功发送ACK时,会删除延迟确认定时器,同时清零ACK的发送状态标志icsk->icsk_ack.pending
ACK发送事件主要做了:更新快速确认模式中的ACK额度,删除ACK延迟定时器,清零icsk->icsk_ack.pending。
在快速确认模式中,可以发送的ACK数量是有限制的,具体额度为icsk->icsk_ack.quick。当额度用完时,就进入延迟确认模式。
static int tcp_transmit_skb (struct sock *sk, struct sk_buff *skb, int clone_it, gfp_t gfp_mask)
{
------------------------------
if (likely(tcb->tcp_flags & TCPHDR_ACK))
tcp_event_ack_sent(sk, tcp_skb_pcount(skb)); /* ACK发送事件的处理 */
------------------------------------------------
}
//ACK发送事件主要做了:更新快速确认模式中的ACK额度,删除ACK延迟定时器,清零icsk->icsk_ack.pending。
/* Account for an ACK we sent. */
static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts)
{
tcp_dec_quickack_mode(sk, pkts);// 更新快速确认模式的ACK额度
inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);//删除ACK延迟定时器
}
static inline void tcp_dec_quickack_mode (struct sock *sk, const unsigned int pkts)
{
struct inet_connection_sock *icsk = inet_csk(sk);
if (icsk->icsk\_ack.quick) { /\* 如果额度不为0 \*/
if (pkts >= icsk->icsk\_ack.quick) {
icsk->icsk\_ack.quick = 0;
/\* Leaving quickack mode we deflate ATO. \*/
icsk->icsk\_ack.ato = TCP\_ATO\_MIN;
} else
icsk->icsk\_ack.quick -= pkts;
}
/*
在快速确认模式中,可以发送的ACK数量是有限制的,具体额度为icsk->icsk_ack.quick。
所以进入快速确认模式时,需要设置可以快速发送的ACK数量,一般允许快速确认半个接收窗口的数据量,但最多不能超过16个,最少为2个。
*/
static void tcp_enter_quickack_mode (struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
tcp\_incr\_quickack(sk); /\* 设置在快速确认模式中可以发送的ACK数量 \*/
icsk->icsk\_ack.pingpong = 0; /\* 快速确认模式的标志 \*/
icsk->icsk\_ack.ato = TCP\_ATO\_MIN; /\* ACK超时时间 \*/
}
static void tcp_incr_quickack(struct sock *sk)
{/* Maximal number of ACKs sent quickly to accelerate slow-start. */
#define TCP_MAX_QUICKACKS 16U
struct inet_connection_sock *icsk = inet_csk(sk);
unsigned int quickacks = tcp_sk(sk)->rcv_wnd / (2 * icsk->icsk_ack.rcv_mss);
if (quickacks == 0)
quickacks = 2;
if (quickacks > icsk->icsk\_ack.quick)
icsk->icsk\_ack.quick = min(quickacks, TCP\_MAX\_QUICKACKS);
}
/* Send ACKs quickly, if "quick" count is not exhausted
* and the session is not interactive.
*/
static bool tcp_in_quickack_mode(struct sock *sk)
{
const struct inet_connection_sock *icsk = inet_csk(sk);
const struct dst_entry *dst = __sk_dst_get(sk);
//如果快速确认模式中可以发送的ACK数量不为0,且设置了快速确认标志 且 dst 路由存在
return (dst && dst_metric(dst, RTAX_QUICKACK)) ||
(icsk->icsk_ack.quick && !icsk->icsk_ack.pingpong);
}
tcp_ack_snd_check()会检查是否需要发送ACK,以及是使用快速确认还是延迟确认。
/*
static inline int inet_csk_ack_scheduled(const struct sock *sk)
{
return inet_csk(sk)->icsk_ack.pending & ICSK_ACK_SCHED;
}
*/
static inline void tcp_ack_snd_check(struct sock *sk)
{
if (!inet_csk_ack_scheduled(sk)) {//如果没有ACK需要发送
/* We sent a data segment already. */
return;
}
__tcp_ack_snd_check(sk, 1);
}
对于以下情况可以立即发送ACK,即进行快速确认:
1. 接收缓冲区中有一个以上的全尺寸数据段仍然是NOT ACKed,并且接收窗口变大了。所以一般收到了两个数据包后,会发送ACK,而不是对每个数据包都进行确认。
2. 此时处于快速确认模式中。
3. 乱序队列不为空。
/*
* Check if sending an ack is needed.
*/
static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible)
{
struct tcp_sock *tp = tcp_sk(sk);
/\* More than one full frame received... \*/
if (((tp->rcv\_nxt - tp->rcv\_wup) > inet\_csk(sk)->icsk\_ack.rcv\_mss &&
/\* ... and right edge of window advances far enough.
\* (tcp\_recvmsg() will send ACK otherwise). Or...
\*/
\_\_tcp\_select\_window(sk) >= tp->rcv\_wnd) ||
/\* We ACK each frame or... \*/
tcp\_in\_quickack\_mode(sk) ||
/\* We have out of order data. \*/
(ofo\_possible && !RB\_EMPTY\_ROOT(&tp->out\_of\_order\_queue))) {
/\* Then ack it now ACK的发送函数为tcp\_send\_ack(),如果发送失败会启动ACK延迟定时器。 \*/
tcp\_send\_ack(sk);
} else {
/\* Else, send delayed ack. \*/
tcp\_send\_delayed\_ack(sk);
}
}
TCP_QUICKACK用于让本端立即发送ACK,而不进行延迟确认。需要注意的是,这个选项并不是可持续的,之后还是有可能进入延迟确认模式的。
所以如果需要一直进行快速确认,要在每次调用接收函数后都进行选项设置。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章