快捷搜索:   服务器  安全  linux 安全  MYSQL  dedecms

tcp连接在断网后的恢复能力(2)

  1470      *     Minshall version sounds: there are no _small_

  1471      *     segments in flight. (tcp_nagle_check)

  1472      *  c) We have too many packets 'in flight'

  1473      *

  1474      *  Don't use the nagle rule for urgent data (or

  1475      *  for the final FIN -DaveM).

  1476      *

  1477      *  Also, Nagle rule does not apply to frames, which

  1478      *  sit in the middle of queue (they have no chances

  1479      *  to get new data) and if room at tail of skb is

  1480      *  not enough to save something seriously (<32 for now).

  1481      */

  1482

  1483     /* Don't be strict about the congestion window for the

  1484      * final FIN frame.  -DaveM

  1485      */

  1486     return (((nonagle&TCP_NAGLE_PUSH) || tp->urg_mode

  1487          || !tcp_nagle_check(tp, skb, cur_mss, nonagle)) &&

  1488         (((tcp_packets_in_flight(tp) + (pkts-1)) < tp->snd_cwnd) ||

  1489          (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN)) &&

  1490         !after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd));

  1491 }

  这个函数的注释比实现代码还多,return后面复杂的条件判断可以被拆开:其实是三个条件的“and”操作,我们看第二个条件,就是:

  (((tcp_packets_in_flight(tp) + (pkts-1)) < tp->snd_cwnd) || (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN))

  其中,tcp_packets_in_flight是指正在“飞行中”的packet数,也就是正在网络上的包数,它的计算方法是:

  发送过一次的包数 + 重发过的包数 - 队列中存留的包数

  而TCPCB_FLAG_FIN是指一端是否发完了数据,这个在我们的项目中不存在,我们的数据没个完。

  这下清楚了:

  如果设了NODELAY,则关闭了nagle算法,大量的小数据包被发出去(看看上面第一个tcpdump的数据),在突然断网时,in_flight的包很多,多得超过了snd_cwnd即发送窗口的大小,于是tcp_snd_test返回0,真正的发送没有发生。不发送存着的数据,snd_buf中的空间就腾不出来,tcp_sendmsg就一直返回0。恶性循环。

  有人要问了,既然snd_buf没空间了,那ACK又是怎么发出去的呢?答案是:发ACK不需要snd_buf空间,它直接就扔出去了。在socket收消息时,会调用tcp_recvmsg,收完后会清空读缓冲cleanup_rbuf,cleanup_rbug里会发送ACK消息,如图:

  tcp_write_xmit里的操作其实就是从发送队列里循环拿skb,然后调用tcp_transmit_skb发到网络上去,而ACK是直接就调用tcp_transmit_skb,故而不经过发送队列,也就不受snd_buf空间的影响。

  还有人可能问,这岂不是linux tcp协议栈的bug?我觉得有可能,因为在linux的2.6.32的内核里,tcp_snd_test函数已经没有了(实际上从2.6.13开始tcp_snd_test就没了,用rhel5的人可以松口气了),__tcp_push_pending_frame里那个别扭的“||”操作也拿掉了,改为直接调用tcp_write_xmit,再在tcp_write_xmit里对窗口和nagle算法就行判断,决定是否该发送包。逻辑更清晰,bug也避开了。

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论