在上文数据通过网络发送的过程中,当程序调用tcp_sendmsg发送数据的时候有可能当前的数据不会马上发送出去,当前在哪些场景下会触发数据的发送呢。
创建 一个新的路径MTU 发现段
未启用路径MTU
当前路径MTU探测段的长度不为0,表示路径MTU发现段已经发出尚未得到确认
拥塞控制状态不处于Open 状态
拥塞窗又大小不足使用时
下 一个发送的段中存在SACK选 项
在tcp_write_xmit函数执行的场景下有可能执行tcp_mtu_probe
tcp_write_xmit->tcp_transmit_skb
tcp_mtu_probe->tcp_transmit_skb
void tcp_write_timer_handler(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
int event;
if (sk->sk_state == TCP_CLOSE || !icsk->icsk_pending)
goto out;
if (time_after(icsk->icsk_timeout, jiffies)) {
sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout);
goto out;
}
event = icsk->icsk_pending;
switch (event) {
case ICSK_TIME_EARLY_RETRANS:
tcp_resume_early_retransmit(sk);
break;
case ICSK_TIME_LOSS_PROBE:
tcp_send_loss_probe(sk); // tlp超时
break;
case ICSK_TIME_RETRANS:
icsk->icsk_pending = 0;
tcp_retransmit_timer(sk); // 重传的场景
break;
case ICSK_TIME_PROBE0:
icsk->icsk_pending = 0;
tcp_probe_timer(sk); // 探测场景
break;
}
out:
sk_mem_reclaim(sk);
}
tcp_probe_timer的调用路径为tcp_send_probe0->tcp_write_wakeup->tcp_transmit_skb,
如果 ACK 到达指向一个记住的 SACK,这意味着我们的记住的 SACK 不反映接收者的真实状态,即接收器 host严重拥塞(或有故障)
Fast Open 启用的情况下,SYN 包也会带有数据。这里
调用 tcp_rcv_fastopen_synack函数处理 SYN 包附带的数据。
tcp_retransmit_skb->__tcp_retransmit_skb->tcp_transmit_skb
这在重传超时后被调用,重传的数据被确认。 它试图重新发送重传队列的其余部分,直到我们已经全部发送或达到拥塞窗口限制。如果做 SACK,第一个超时返回的 ACK基于重传数据包可能会再次向我们提供 FACK 信息。如果是这样,我们使用它来避免不必要的重传。
tcp_keepalive_timer相关内容
tcp_send_active_reset->tcp_transmit_skb
如果是接收到了syn之后建立了链接之后,就会检查状态并且查看能否立即发送数据
tcp_rcv_synsent_state_process在接受到数据之后发送。
tcp_send_synack->tcp_transmit_skb
tcp_connect当建立链接之后尝试发送数据看是否是通过fast open来发送syn带data的数据或者只发送syn报文。
tcp_connect->tcp_transmit_skb
tcp_send_syn_data->tcp_transmit_skb
发送ack的场景下调用数据发送
tcp_send_ack->tcp_transmit_skb
do_tcp_setsockopt在设置TCP_REPAIR参数场景下
tcp_send_window_probe->tcp_xmit_probe_skb->tcp_transmit_skb
在梳理了调用流程上来说,当前会触发调用的场景里面,在tcp_ack或者超时重传的场景下,会继续发送未经ack的数据,在其他的场景下更多的是发送一些窗口探测等相关的协议数据来完成tcp设计的保活,mtu等探测的功能,由于对tcp/ip协议的理解还不够还未能详细的将每个调用函数的场景细节进行学习。
https://void-star.icu/archives/1005