TCP定时器 之 连接建立定时器

当服务器收到新的syn请求,会回复syn+ack给请求端,若某时间内未收到请求端回复的ack,新建连接定时器超时执行回调,重传syn+ack,当超时超过固定次数时,该连接中止;本文主要分析其初始化流程,具体的建立连接和超时重传流程在后续的文章中进行详细讨论;

request_sock结构中的rsk_timer成员为新建连接计时器;

 1 /* struct request_sock - mini sock to represent a connection request
 2  */
 3 struct request_sock {
 4     struct sock_common        __req_common;
 5         /* 省略了一些字段 */
 6     struct timer_list        rsk_timer;
 7     const struct request_sock_ops    *rsk_ops;
 8     struct sock            *sk;
 9     /* 省略了一些字段 */
10 };

 

函数调用关系如下,其中tcp_rcv_state_process中判断标记,如果发生是设置了syn请求标记,则进入新建连接流程,在tcp_conn_request中将会新建连接请求控制块,用于跟踪完成三次握手;

1 /**
2  *tcp_v4_rcv
3  * |-->tcp_v4_do_rcv
4  *      |-->tcp_rcv_state_process /* 这里如果是syn请求,则调用下面的conn_request函数 */
5  *             |-->tcp_v4_conn_request
6  *                  |-->tcp_conn_request /* 这里新建请求控制块 */
7  *                      |-->inet_csk_reqsk_queue_hash_add
8  *                           |-->reqsk_queue_hash_req
9  */

 

启动定时器:

reqsk_queue_hash_req函数进行连接请求定时器的设定,将req->rsk_timer的超时回调设置为reqsk_timer_handler,reqsk_timer_handler调用inet_rtx_syn_ack进行syn+ack的重传;

其中超时时间初始化为timeout=TCP_TIMEOUT_INIT,为1HZ;timeout会随着重传的次数不断变化timeo = min(TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX);

 1 static void reqsk_queue_hash_req(struct request_sock *req,
 2                  unsigned long timeout)
 3 {
 4     req->num_retrans = 0;
 5     req->num_timeout = 0;
 6     req->sk = NULL;
 7 
 8     /* 添加定时器 */
 9     setup_pinned_timer(&req->rsk_timer, reqsk_timer_handler,
10                 (unsigned long)req);
11     mod_timer(&req->rsk_timer, jiffies + timeout);
12 
13     inet_ehash_insert(req_to_sk(req), NULL);
14     /* before letting lookups find us, make sure all req fields
15      * are committed to memory and refcnt initialized.
16      */
17     smp_wmb();
18     atomic_set(&req->rsk_refcnt, 2 + 1);
19 }

 

定时器回调函数:

判断是否超时次数超过阈值,是否需要重传syn+ack,如果超时或者未收到ack情况下重传失败,则取消连接;函数中还包含了对refer_accept选项的处理;

 1 /* 新建连接定时器超时处理 */
 2 static void reqsk_timer_handler(unsigned long data)
 3 {
 4     struct request_sock *req = (struct request_sock *)data;
 5     struct sock *sk_listener = req->rsk_listener;
 6     struct net *net = sock_net(sk_listener);
 7     struct inet_connection_sock *icsk = inet_csk(sk_listener);
 8     struct request_sock_queue *queue = &icsk->icsk_accept_queue;
 9     int qlen, expire = 0, resend = 0;
10     int max_retries, thresh;
11     u8 defer_accept;
12 
13     /* 不是LISTEN状态 */
14     if (sk_state_load(sk_listener) != TCP_LISTEN)
15         goto drop;
16 
17     /* 阈值设置为允许的最大重传数 */
18     max_retries = icsk->icsk_syn_retries ? : net->ipv4.sysctl_tcp_synack_retries;
19     thresh = max_retries;
20     /* Normally all the openreqs are young and become mature
21      * (i.e. converted to established socket) for first timeout.
22      * If synack was not acknowledged for 1 second, it means
23      * one of the following things: synack was lost, ack was lost,
24      * rtt is high or nobody planned to ack (i.e. synflood).
25      * When server is a bit loaded, queue is populated with old
26      * open requests, reducing effective size of queue.
27      * When server is well loaded, queue size reduces to zero
28      * after several minutes of work. It is not synflood,
29      * it is normal operation. The solution is pruning
30      * too old entries overriding normal timeout, when
31      * situation becomes dangerous.
32      *
33      * Essentially, we reserve half of room for young
34      * embrions; and abort old ones without pity, if old
35      * ones are about to clog our table.
36      */
37 
38     /* 获取accept队列长度 */
39     qlen = reqsk_queue_len(queue);
40 
41     /* 请求accept队列数> 最大未完成连接数的一半 */
42     if ((qlen << 1) > max(8U, sk_listener->sk_max_ack_backlog)) {
43 
44         /* 没有重传ack的请求控制块数 */
45         int young = reqsk_queue_len_young(queue) << 1;
46 
47         /* 可以看出年轻的数量越多,则允许重传次数越多 */
48         while (thresh > 2) {
49 
50             // 没重传的数量> 队列长度的(1/2, 1/4,1/8...)
51             if (qlen < young)
52                 break;
53 
54             /* 阈值减1 */
55             thresh--;
56 
57             /* 扩大young */
58             young <<= 1;
59         }
60     }
61     /* 设置了TCP_DEFER_ACCEPT,则使用之 */
62     defer_accept = READ_ONCE(queue->rskq_defer_accept);
63     if (defer_accept)
64         max_retries = defer_accept;
65 
66     /* 超时和是否重传判断 */
67     syn_ack_recalc(req, thresh, max_retries, defer_accept,
68                &expire, &resend);
69     /* 统计超时次数 */
70     req->rsk_ops->syn_ack_timeout(req);
71     if (!expire && /* 未超时 */
72         (!resend || /* 不需要重传 */
73          !inet_rtx_syn_ack(sk_listener, req) || /* 重传成功 */
74          inet_rsk(req)->acked)) { /* 收到ack了,但是未建立连接(defer_accept,其他情况?) */
75         unsigned long timeo;
76 
77         /* 该请求尚未重传过,则将未重传块-1 */
78         /* 超时次数+ 1 */
79         if (req->num_timeout++ == 0)
80             atomic_dec(&queue->young);
81 
82         /* 重新设定超时时间为上次时间* 2,与pto_max取最小值 */
83         timeo = min(TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX);
84         mod_timer(&req->rsk_timer, jiffies + timeo);
85         return;
86     }
87     
88     /* 取消连接,从链表移除请求控制块 */
89 drop:
90     inet_csk_reqsk_queue_drop_and_put(sk_listener, req);
91 }

 

判断是否已经超时,或者是否需要重传syn+ack;

 1 /* Decide when to expire the request and when to resend SYN-ACK */
 2 /* 判断是否超时,是否重传syn+ack */
 3 static inline void syn_ack_recalc(struct request_sock *req, const int thresh,
 4                   const int max_retries,
 5                   const u8 rskq_defer_accept,
 6                   int *expire, int *resend)
 7 {
 8     /* 无defer_accept选项 */
 9     if (!rskq_defer_accept) {
10         /* 超时次数> 重传阈值则超时 */
11         *expire = req->num_timeout >= thresh;
12 
13         /* 需要重传 */
14         *resend = 1;
15         return;
16     }
17 
18     /* 以下设置了defer_accept */
19 
20     /* 超时次数> 重传阈值&&  (未收到ack || 超时次数>= 最大重传数),则超时 */
21     /* 已经收到ack的情况下,超时次数达到defer_accept限制 */
22     *expire = req->num_timeout >= thresh &&
23           (!inet_rsk(req)->acked || req->num_timeout >= max_retries);
24     /*
25      * Do not resend while waiting for data after ACK,
26      * start to resend on end of deferring period to give
27      * last chance for data or ACK to create established socket.
28      */
29 
30     /* 未收到ack || 收到ack,但是超时次数达到了defer_accept限制-1,需要重传*/
31     /* 给了设置defer_accept选项情况下一次机会 */
32     *resend = !inet_rsk(req)->acked ||
33           req->num_timeout >= rskq_defer_accept - 1;
34 }

 

posted @ 2019-10-27 22:12  AlexAlex  阅读(757)  评论(0编辑  收藏  举报