当服务器收到新的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 = ;
  5. req->num_timeout = ;
  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, + );
  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 = , resend = ;
  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 << ) > max(8U, sk_listener->sk_max_ack_backlog)) {
  43.  
  44. /* 没有重传ack的请求控制块数 */
  45. int young = reqsk_queue_len_young(queue) << ;
  46.  
  47. /* 可以看出年轻的数量越多,则允许重传次数越多 */
  48. while (thresh > ) {
  49.  
  50. // 没重传的数量> 队列长度的(1/2, 1/4,1/8...)
  51. if (qlen < young)
  52. break;
  53.  
  54. /* 阈值减1 */
  55. thresh--;
  56.  
  57. /* 扩大young */
  58. young <<= ;
  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++ == )
  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 = ;
  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 - ;
  34. }

TCP定时器 之 连接建立定时器的更多相关文章

  1. 14.TCP的坚持定时器和保活定时器

    一.坚持定时器   1.坚持定时器的由来         TCP通过让接收方指明希望从发送方接受的窗口大小来进行流量控制.设置窗口大小为0可以组织发送方传送数据,直至窗口变为非0为止.         ...

  2. TCP的定时器系列 — 保活定时器

    主要内容:保活定时器的实现,TCP_USER_TIMEOUT选项的实现. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 原理 HTTP有Keepaliv ...

  3. 动手学习TCP:4种定时器

    上一篇中介绍了TCP数据传输中涉及的一些基本知识点.本文让我们看看TCP中的4种定时器. TCP定时器 对于每个TCP连接,TCP管理4个不同的定时器,下面看看对4种定时器的简单介绍. 重传定时器使用 ...

  4. 【网络协议】TCP中的四大定时器

    前言 对于每个TCP连接,TCP一般要管理4个不同的定时器:重传定时器.坚持定时器.保活定时器.2MSL定时器. 重传定时器 非常明显重传定时器是用来计算TCP报文段的超时重传时间的(至于超时重传时间 ...

  5. TCP的四种定时器简单记录

    TCP管理的4个不同的定时器: 1.重传定时器:用于当希望收到另一端的确认. 2.坚持定时器:使窗口大小信息保持不断流动. 3.保活定时器:检测TCP空闲连接的另一端何时崩溃或重启. 4.2MSL定时 ...

  6. TCP定时器 之 FIN_WAIT_2定时器

    当TCP主动关闭一端调用了close()来执行连接的完全关闭时会执行以下流程,本端发送FIN给对端,对端回复ACK,本端进入FIN_WAIT_2状态,此时只有对端发送了FIN,本端才会进入TIME_W ...

  7. TCP定时器 之 保活定时器

    在用户进程启用了保活定时器的情况下,如果连接超过空闲时间没有数据交互,则保活定时器超时,向对端发送保活探测包,若(1)收到回复则说明对端工作正常,重置定时器等下下次达到空闲时间:(2) 收到其他回复, ...

  8. TCP定时器 之 坚持定时器

    坚持定时器在接收方通告接收窗口为0,阻止发送端继续发送数据时设定. 由于连接接收端的发送窗口通告不可靠(只有数据才会确认,ACK不会确认),如果一个确认丢失了,双方就有可能因为等待对方而使连接终止:接 ...

  9. tcp中的常见定时器

    (1)超时重传定时器tcp的靠谱特性,通过确认机制,保证每一个包都被对方收到,那么什么时候需要重传呢?就是靠这个超时重传定时器,每次发送报文前都启动这个定时器,如果定时器超时之前收到了应答则关闭定时器 ...

随机推荐

  1. Java定义队结构,实现入队、出队操作

    package com.example.demo; import java.util.ArrayList; public class Queue { ArrayList<Object> l ...

  2. javaee 自定义标签实战

    用过javaee标准标签库的里的标签应该都知道,标签的存在使得页面上的jsp脚本大大减少,甚至说没有了,大大提高了工作效率,使得页面的整洁性也有了很大的提高.下面我们就 模仿核心标签库中choose标 ...

  3. python-迭代器与生成器2

    python-迭代器与生成器2 def fib(max): n,a,b=0,0,1 while n<max: #print(b) yield b a,b=b,a+b #t=(b,a+b) 是一个 ...

  4. Elasticsearch改动

    随着Elasticsearch的版本升级,Elasticsearch的一些特性也在改变,下面是一些需要注意的地方 v6.x 版本之前 : 一个index下面是可以创建多个type v6.x 版本 : ...

  5. Java HashMap的工作原理(转载)

    原文地址:http://www.importnew.com/10620.html 面试的时候经常会遇见诸如:"java中的HashMap是怎么工作的","HashMap的 ...

  6. qt5---滑动条QSlider

    需要    #include <QSlider> #include "win.h" #include <QDebug> #include <QPush ...

  7. 【51nod1220】约数之和

    题目 d(k)表示k的所有约数的和.d(6) = 1 + 2 + 3 + 6 = 12. 定义S(N) = ∑1<=i<=N ∑1<=j<=N d(i*j). 例如:S(3) ...

  8. SAP Diagnostics Agent无法启动

    [问题]SAP Diagnostics Agent无法启动. [现象]Diagnostics Agent安装并没有发生错误,但是打开SAPMMC,Diagnostics Agent(DAA)的Inst ...

  9. testNG之测试报告

    原文:https://www.cnblogs.com/yuan-yuan/p/4503524.html 测试报告 执行完测试用例之后,会在项目的test-output(默认目录)下生成测试报告 打开i ...

  10. Spring、SpringMVC和Springboot的区别

    spring boot就是一个大框架里面包含了许许多多的东西,其中spring就是最核心的内容之一,当然就包含spring mvc. spring mvc 是只是spring 处理web层请求的一个模 ...