免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 6439 | 回复: 5

请教Linux对于FIN_WAIT_1连接处理的问题 [复制链接]

论坛徽章:
0
发表于 2012-03-22 20:02 |显示全部楼层
在tcp_rcv_state_process中,对于TCP_FIN_WAIT1状态的处理有这么一段代码:
  1.                 tmo = tcp_fin_time(sk);
  2.                 if (tmo > TCP_TIMEWAIT_LEN) {
  3.                         inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN);
  4.                 } else if (th->fin || sock_owned_by_user(sk)) {
  5.                         /* Bad case. We could lose such FIN otherwise.
  6.                         * It is not a big problem, but it looks confusing
  7.                         * and not so rare event. We still can lose it now,
  8.                         * if it spins in bh_lock_sock(), but it is really
  9.                         * marginal case.
  10.                         */
  11.                         inet_csk_reset_keepalive_timer(sk, tmo);
  12.                 } else {
  13.                         tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
  14.                         goto discard;
  15.                 }
复制代码
当计算出来的tmo大于60s时,会先启动keepalive定时器,超时时间为(tmo-60s),
在keepalive定时器处理函数中是这样处理的:
  1.         if (sk->sk_state == TCP_FIN_WAIT2 && sock_flag(sk, SOCK_DEAD)) {
  2.                 if (tp->linger2 >= 0) {
  3.                         const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN;

  4.                         if (tmo > 0) {
  5.                                 tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
  6.                                 goto out;
  7.                         }
  8.                 }
  9.                 tcp_send_active_reset(sk, GFP_ATOMIC);
  10.                 goto death;
  11.         }
复制代码
不明白这里tcp_time_wait设置定时器为啥又一次设置为tmo-60s。我总觉得应该是设置为60s

举两个例子,首先假设在第一段代码中处理的数据包没有fin标志,
连接1.tcp_fin_time返回的tmo是70s,那么在第一段代码中设置keepalive定时器70-60=10s,10s后在keepalive_timer里面进入time_wait状态,且time_wait定时器又设置为10s,10s后超时删除sock。总存活时间20s
连接2.tcp_fin_time返回的tmo是50s,那么调用tcp_time_wait且设置time_wait定时器50s,50s后删除连接。总存活时间50s

这不是很奇怪吗?连接1计算的tmo大于连接2,并且报文交互顺序也一样,但是却比连接2早结束。是我哪里理解错了?

多谢各位啦

论坛徽章:
2
申猴
日期:2013-12-26 22:11:31天秤座
日期:2014-12-23 10:23:19
发表于 2012-03-22 21:05 |显示全部楼层
本帖最后由 goter 于 2012-03-22 21:40 编辑

10s后在keepalive_timer里面进入time_wait状态

这个是哪段代码看出来的?什么时候变成TCP_FIN_WAIT2状态的?

sock_flag(sk, SOCK_DEAD) 这个SOCK_DEAD标记,只有调用tcp_close或者listen stop时才会设置

论坛徽章:
0
发表于 2012-03-23 00:05 |显示全部楼层
本帖最后由 peimichael 于 2012-03-23 00:06 编辑

回复 2# goter


>>>10s后在keepalive_timer里面进入time_wait状态
>>>这个是哪段代码看出来的?
我贴的第二段代码,
  1.     if (sk->sk_state == TCP_FIN_WAIT2 && sock_flag(sk, SOCK_DEAD)) {
  2.         if (tp->linger2 >= 0) { /*linger2默认是等于0的吧*/
  3.             const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN;

  4.             if (tmo > 0) { /*tcp_fin_time为70s时,这里满足大于0*/
  5.                 /***********这里被挂入time_wait表,tmo后被会被删除,而tmo=70s-60s=10s************/
  6.                 tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
  7.                 goto out;
  8.             }
  9.         }
  10.         tcp_send_active_reset(sk, GFP_ATOMIC);
  11.         goto death;
  12.     }
复制代码
>>>什么时候变成TCP_FIN_WAIT2状态的?
在tcp_rcv_state_process函数处理TCP_FIN_WAIT1的那段代码一开始就将sock设置成了TCP_FIN_WAIT2


>>>sock_flag(sk, SOCK_DEAD) 这个SOCK_DEAD标记,只有调用tcp_close或者listen stop时才会设置
我前面没说清楚,我描述的正是已经调用过tcp_close的情况,sock已经发出了一个fin,进入了FIN_WAIT_1状态,正在等待回来的ack


谢谢

论坛徽章:
0
发表于 2012-03-28 17:35 |显示全部楼层
1.tcp_fin_time返回的tmo是70s,那么在第一段代码中设置keepalive定时器70-60=10s,10s后在keepalive_timer里面进入time_wait状态,且time_wait定时器又设置为10s,10s后超时删除sock。

为何要说10s后就删除sock呢?
time_wait里面有check了RTO时间。
                /* Get the TIME_WAIT timeout firing. */
                if (timeo < rto)
                        timeo = rto;

const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);

tcp_fin_time返回的tmo是70s,说明(icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1)的值就是70s,
net.ipv4.tcp_fin_timeout默认是60s。
所以,在time_wait里需要再等70s,才会删除sock。


peimichael 发表于 2012-03-22 20:02
在tcp_rcv_state_process中,对于TCP_FIN_WAIT1状态的处理有这么一段代码:当计算出来的tmo大于60s时,会先 ...

论坛徽章:
0
发表于 2012-03-28 22:17 |显示全部楼层
纳微芯 发表于 2012-03-28 17:35
1.tcp_fin_time返回的tmo是70s,那么在第一段代码中设置keepalive定时器70-60=10s,10s后在keepalive_timer ...


看了下代码,我确实是有些想当然认为传进去的时间就会被用作timer的时间,楼上解释的对。

但是仔细想想还是有问题,首先假设rto很小,3.5RTO<10s,然后看以下情况:
1.把net.ipv4.tcp_fin_timeout设置为默认值60,那么在第一段代码中tmo > TCP_TIMEWAIT_LEN不成立,会调度time_wait定时器并设置超时为60s
2.把net.ipv4.tcp_fin_timeout设置为70,那么在第一段代码中tmo > TCP_TIMEWAIT_LEN成立,设置keepalive timer 10秒,10秒后在keepalive timer中仍然会把time_wait timer设置为10s(前面已经假设3.5RTO<10s,因此经过判断对比,这里time_wait超时时间仍然是10s)。
     这两种情况有没有问题呢?

多谢!

论坛徽章:
0
发表于 2012-03-29 10:27 |显示全部楼层
默认是60s,总超时是60s。
设置为70s,总超时是20s(10 keepalive + 10 time_wait)
也就是说,手工设置的时间并不是预想中的那样。


看到你在netdev上的提问了。
你可以把rto很少的例子,放到邮件里。

不过你可以看看tcp_close()中对TCP_FIN_WAIT2的处理。这里讲keepalive的定时器从tmo改成了tmo-TCP_TIMEWAIT_LEN
        if (sk->sk_state == TCP_FIN_WAIT2) {
                struct tcp_sock *tp = tcp_sk(sk);
                if (tp->linger2 < 0) {
                        tcp_set_state(sk, TCP_CLOSE);
                        tcp_send_active_reset(sk, GFP_ATOMIC);
                        NET_INC_STATS_BH(sock_net(sk),
                                        LINUX_MIB_TCPABORTONLINGER);
                } else {
                        const int tmo = tcp_fin_time(sk);

                        if (tmo > TCP_TIMEWAIT_LEN) {
                                inet_csk_reset_keepalive_timer(sk,
                                                tmo - TCP_TIMEWAIT_LEN);
                        } else {
                                tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
                                goto out;
                        }
                }
        }

原因是:
commit 52499afe40387524e9f46ef9ce4695efccdd2ed9
Author: David S. Miller <davem@sunset.davemloft.net>
Date:   Mon Jul 31 22:32:09 2006 -0700

    [TCP]: Process linger2 timeout consistently.

    Based upon guidance from Alexey Kuznetsov.

    When linger2 is active, we check to see if the fin_wait2
    timeout is longer than the timewait.  If it is, we schedule
    the keepalive timer for the difference between the timewait
    timeout and the fin_wait2 timeout.

    When this orphan socket is seen by tcp_keepalive_timer()
    it will try to transform this fin_wait2 socket into a
    fin_wait2 mini-socket, again if linger2 is active.

    Not all paths were setting this initial keepalive timer correctly.
    The tcp input path was doing it correctly, but tcp_close() wasn't,
    potentially making the socket linger longer than it really needs to.

    Signed-off-by: David S. Miller <davem@davemloft.net>


peimichael 发表于 2012-03-28 22:17
看了下代码,我确实是有些想当然认为传进去的时间就会被用作timer的时间,楼上解释的对。

但是仔细想 ...
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP