zoukankan      html  css  js  c++  java
  • TCP层close系统调用的实现分析

    在调用close系统调用关闭套接字时,如果套接字引用计数已经归零,则需继续向上层调用其close实现,tcp为tcp_close;本文仅介绍tcp部分,前置部分请参考本博关于close系统调用的文章;

      1 void tcp_close(struct sock *sk, long timeout)
      2 {
      3     struct sk_buff *skb;
      4     int data_was_unread = 0;
      5     int state;
      6 
      7     lock_sock(sk);
      8     sk->sk_shutdown = SHUTDOWN_MASK;
      9 
     10     /* LISTEN状态处理 */
     11     if (sk->sk_state == TCP_LISTEN) {
     12         /* 设置close状态 */
     13         tcp_set_state(sk, TCP_CLOSE);
     14 
     15         /* Special case. */
     16         /* 清理完成连接队列 */
     17         inet_csk_listen_stop(sk);
     18 
     19         goto adjudge_to_death;
     20     }
     21 
     22     /*  We need to flush the recv. buffs.  We do this only on the
     23      *  descriptor close, not protocol-sourced closes, because the
     24      *  reader process may not have drained the data yet!
     25      */
     26     /* 删除接收队列中用户进程未读取的skb */
     27     while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
     28         u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq;
     29 
     30         /* 减去fin的一个序号长度 */
     31         if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
     32             len--;
     33         data_was_unread += len;
     34         __kfree_skb(skb);
     35     }
     36 
     37     sk_mem_reclaim(sk);
     38 
     39     /* If socket has been already reset (e.g. in tcp_reset()) - kill it. */
     40     /* CLOSE状态 */
     41     if (sk->sk_state == TCP_CLOSE)
     42         goto adjudge_to_death;
     43 
     44     /* As outlined in RFC 2525, section 2.17, we send a RST here because
     45      * data was lost. To witness the awful effects of the old behavior of
     46      * always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
     47      * GET in an FTP client, suspend the process, wait for the client to
     48      * advertise a zero window, then kill -9 the FTP client, wheee...
     49      * Note: timeout is always zero in such a case.
     50      */
     51     /* 修复状态,断开连接 */
     52     if (unlikely(tcp_sk(sk)->repair)) {
     53         sk->sk_prot->disconnect(sk, 0);
     54     } 
     55     /* 用户进程有数据未读 */
     56     else if (data_was_unread) {
     57         /* Unread data was tossed, zap the connection. */
     58         NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
     59 
     60         /* 设置为close */
     61         tcp_set_state(sk, TCP_CLOSE);
     62 
     63         /* 发送rst */
     64         tcp_send_active_reset(sk, sk->sk_allocation);
     65     } 
     66     /* lingertime==0,断开连接 */
     67     else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
     68         /* Check zero linger _after_ checking for unread data. */
     69         sk->sk_prot->disconnect(sk, 0);
     70         NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
     71     } 
     72     /* 关闭状态转移 */
     73     else if (tcp_close_state(sk)) {
     74         /* We FIN if the application ate all the data before
     75          * zapping the connection.
     76          */
     77 
     78         /* RED-PEN. Formally speaking, we have broken TCP state
     79          * machine. State transitions:
     80          *
     81          * TCP_ESTABLISHED -> TCP_FIN_WAIT1
     82          * TCP_SYN_RECV    -> TCP_FIN_WAIT1 (forget it, it's impossible)
     83          * TCP_CLOSE_WAIT -> TCP_LAST_ACK
     84          *
     85          * are legal only when FIN has been sent (i.e. in window),
     86          * rather than queued out of window. Purists blame.
     87          *
     88          * F.e. "RFC state" is ESTABLISHED,
     89          * if Linux state is FIN-WAIT-1, but FIN is still not sent.
     90          *
     91          * The visible declinations are that sometimes
     92          * we enter time-wait state, when it is not required really
     93          * (harmless), do not send active resets, when they are
     94          * required by specs (TCP_ESTABLISHED, TCP_CLOSE_WAIT, when
     95          * they look as CLOSING or LAST_ACK for Linux)
     96          * Probably, I missed some more holelets.
     97          *                         --ANK
     98          * XXX (TFO) - To start off we don't support SYN+ACK+FIN
     99          * in a single packet! (May consider it later but will
    100          * probably need API support or TCP_CORK SYN-ACK until
    101          * data is written and socket is closed.)
    102          */
    103         /* 发送fin */
    104         tcp_send_fin(sk);
    105     }
    106 
    107     /* 等待关闭,状态为FIN_WAIT_1 CLOSING LAST_ACK或sk_lingertime超时 */
    108     sk_stream_wait_close(sk, timeout);
    109 
    110 adjudge_to_death:
    111     state = sk->sk_state;
    112     sock_hold(sk);
    113 
    114     /* 设置为DEAD状态 */
    115     sock_orphan(sk);
    116 
    117     /* It is the last release_sock in its life. It will remove backlog. */
    118     /* 删除控制块的backlog、cb */
    119     release_sock(sk);
    120 
    121 
    122     /* Now socket is owned by kernel and we acquire BH lock
    123        to finish close. No need to check for user refs.
    124      */
    125     local_bh_disable();
    126     bh_lock_sock(sk);
    127     WARN_ON(sock_owned_by_user(sk));
    128 
    129     /* 增加孤儿计数 */
    130     percpu_counter_inc(sk->sk_prot->orphan_count);
    131 
    132     /* Have we already been destroyed by a softirq or backlog? */
    133     /* 被软中断或者backlog销毁了????? */
    134     if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
    135         goto out;
    136 
    137     /*    This is a (useful) BSD violating of the RFC. There is a
    138      *    problem with TCP as specified in that the other end could
    139      *    keep a socket open forever with no application left this end.
    140      *    We use a 1 minute timeout (about the same as BSD) then kill
    141      *    our end. If they send after that then tough - BUT: long enough
    142      *    that we won't make the old 4*rto = almost no time - whoops
    143      *    reset mistake.
    144      *
    145      *    Nope, it was not mistake. It is really desired behaviour
    146      *    f.e. on http servers, when such sockets are useless, but
    147      *    consume significant resources. Let's do it with special
    148      *    linger2    option.                    --ANK
    149      */
    150 
    151     if (sk->sk_state == TCP_FIN_WAIT2) {
    152         struct tcp_sock *tp = tcp_sk(sk);
    153         /* linger2小于0,无需等待 */
    154         if (tp->linger2 < 0) {
    155 
    156             /* 转到CLOSE */
    157             tcp_set_state(sk, TCP_CLOSE);
    158             /* 发送rst */
    159             tcp_send_active_reset(sk, GFP_ATOMIC);
    160             __NET_INC_STATS(sock_net(sk),
    161                     LINUX_MIB_TCPABORTONLINGER);
    162         } else {
    163 
    164             /* 获取FIN_WAIT_2超时时间 */
    165             const int tmo = tcp_fin_time(sk);
    166 
    167             /* FIN_WAIT_2超时时间> timewait时间,加FIN_WAIT_2定时器 */
    168             if (tmo > TCP_TIMEWAIT_LEN) {
    169                 inet_csk_reset_keepalive_timer(sk,
    170                         tmo - TCP_TIMEWAIT_LEN);
    171             } 
    172             /* 小于TIME_WAIT时间,则进入TIME_WAIT */
    173             else {
    174                 tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
    175                 goto out;
    176             }
    177         }
    178     }
    179 
    180     /* 未处于CLOSE */
    181     if (sk->sk_state != TCP_CLOSE) {
    182         sk_mem_reclaim(sk);
    183 
    184         /* 孤儿数量过多,或者socket内存过多 */
    185         if (tcp_check_oom(sk, 0)) {
    186             /* 进入CLOSE */
    187             tcp_set_state(sk, TCP_CLOSE);
    188             /* 发送rst */
    189             tcp_send_active_reset(sk, GFP_ATOMIC);
    190             __NET_INC_STATS(sock_net(sk),
    191                     LINUX_MIB_TCPABORTONMEMORY);
    192         }
    193     }
    194 
    195     /* 处于CLOSE */
    196     if (sk->sk_state == TCP_CLOSE) {
    197         struct request_sock *req = tcp_sk(sk)->fastopen_rsk;
    198         /* We could get here with a non-NULL req if the socket is
    199          * aborted (e.g., closed with unread data) before 3WHS
    200          * finishes.
    201          */
    202         if (req)
    203             reqsk_fastopen_remove(sk, req, false);
    204 
    205         /* 销毁控制块 */
    206         inet_csk_destroy_sock(sk);
    207     }
    208     /* Otherwise, socket is reprieved until protocol close. */
    209 
    210 out:
    211     bh_unlock_sock(sk);
    212     local_bh_enable();
    213     sock_put(sk);
    214 }
  • 相关阅读:
    三、git管理修改
    二、git版本回退
    一、git创建版本库及提交
    24格栅格系统
    vue项目报错webpackJsonp is not defined
    vue登录注册及token验证
    react native踩坑之旅
    js判断数组是否有重复值
    react native环境搭建(含错误处理)
    python Token加密解密方式
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11751647.html
Copyright © 2011-2022 走看看