当前位置: 首页>编程语言>正文

swift创建定时器 lwip 定时器

                                                               lwip协议栈中超时定时器实现原理

1,超时定时器存在的目的:

      tcpip协议中存在很多需要定时处理的任务,包括一次性超时处理和周期性超时处理。

      以tcp传输为例,每条连接总共需要建立七个定时器,依次为:

      1)“连接建立”定时器。

              如果建立连接启动后75秒内没收到响应,则中止建立。

      2)“重传”定时器

              在tcp发送某个数据段时设定,如果定时器超时了还没接收到对端的确认,则重传数据段。重传定时器值是动态计算的,与          RTT的估算密切相关,且还取决于该报文已被重传的次数。

     3)“延迟ACK”定时器

             tcp收到必须确认但无需马上发出确认的数据时设定, 如果在200ms内,有数据要在该连接上发送,延迟的ACK随数据一并发          送会对端,若200ms后仍未发出,则定时器超时,此时需要发送一个立即确认。

     4)“持续”定时器

器,超时后向对端发送1字节的数据,判断对端接收窗是否已打开。

     5)“保活”定时器

             定时器在tcp控制块的so_options字段设置了SOF_KEEPALIVE选项时生效。如果连接的空闲时间超过2小时,保活定时器超             时,此时向对端发送连接探测报文,强迫对端响应。如果收到响应,tcp可确认对端主机工作正常,如果收到RST复位响应,可确                 认对端主机已重启,如果连续若干次保活测试都未收到响应,则tcp假定对端主机已崩溃,但无法区分是主机故障还是线路故障。

     6)“FIN_WAIT_2”定时器

     7)“TIME_WAIT”定时器(也称2MSL定时器)

2,lwip中超时定时器设计架构:

      这里不讨论各个定时器与tcp协议有关的超时如何处理,只讲超时定时器本身的设计。

      在lwip中,超时定时器代码实现在 src/core/timers.c中

      超时定时器是按链表的形式进行组织的,并且按时间长短进行排序,时间最短的永远在最前面。定时器使用结构体struct sys_timeo进行定义,结构体定义如下:

struct sys_timeo {
               struct sys_timeo *next;      //指向下一个定时器
),在加入链表时这个值会进行调整
               sys_timeout_handler h;    //定时器回调函数
               void *arg;                          //回调函数传入参数
         #if LWIP_DEBUG_TIMERNAMES
               const char* handler_name;            //回调函数名称,调试用
         #endif /* LWIP_DEBUG_TIMERNAMES */
        };
 
 添加超时定时器,函数如下:
void  sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)

 {
   struct sys_timeo *timeout, *t;
   /* 每个定时器都从对应的内存池中分配数据结构 */
   timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
   if (timeout == NULL) {
     LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
     return;
   }
   timeout->next = NULL;
   timeout->h = handler;
   timeout->arg = arg;
   timeout->time = msecs;
 #if LWIP_DEBUG_TIMERNAMES
   timeout->handler_name = handler_name;
   LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n",
     (void *)timeout, msecs, handler_name, (void *)arg));
 #endif /* LWIP_DEBUG_TIMERNAMES */
   /* 如果创建的是第一个定时器,则不用特殊处理,next_timeout是一个全局指针,指向定时器链表中第一个定时器 */
   if (next_timeout == NULL) {
     next_timeout = timeout;
     return;
   }
   /* 从第二个定时器开始就要添加到链表中,添加原则是定时最短的定时器始终在前面,如果新添加的定时器时长小于当前链首
      定时器,则新添加的定时器成为链首,旧的链首定时器的定时值要减去新链首定时器定时值,这样旧定时器时长不变,如果新
      添加的定时器大于等于当前链首定时器的时长,则要在整个链表里逐个比较,最终将其插入到比其短的定时器之后,比其长的
      定时器之前,当然其后定时器的定时值也要进行调整,其前的定时器无需调整 */
   if (next_timeout->time > msecs) {
     next_timeout->time -= msecs;
     timeout->next = next_timeout;
     next_timeout = timeout;
   } else {
     for(t = next_timeout; t != NULL; t = t->next) {
       timeout->time -= t->time;
       if (t->next == NULL || t->next->time > timeout->time) {
         if (t->next != NULL) {
           t->next->time -= timeout->time;
         }
         timeout->next = t->next;
         t->next = timeout;
         break;
       }
     }
   }
 }
     链表中定时器总是按时长升序进行排列,其定时值调整算法为:
Timer(x-2).time + ......
              Timer1.time = 10
              Timer2.time = 5
              Timer3.time = 20
              Timer4time = 10
      按1-4顺序都添加到链表中后,定时器值如下:  
              Timer1.time = 5
              Timer2.time = 5
              Timer3.time = 10
              Timer4.time = 0
      链接关系变为:
              Timer2.next = Timer1
              Timer1.next = Timer4
              Timer4.next = Timer3
              Timer3.next = NULL
      此时,全局指针变量next_timeout指向Timer2。
3,lwip中超时定时器应用:
      不管是否有os支持,超时定时器都可以使用。lwip中如下两个函数可以实现对超时的处理:
void sys_check_timeouts(void)

直接退出该函数,否则,执行该定时器回调函数,并从链表上删除该定时器,然后继续检查下一个定时器,直到没有一个定时器到期退出

sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)

      这个函数可在os线程中循环调用,主要是等待mbox消息,并可阻塞,如果等待mbox时超时,则会同时执行超时事件处理,即调用定时器回调函数,如果一直没有mbox消息,则会永久性地循环将所有超时定时器检查一遍,一举两得。

      lwip中tcpip线程就是靠这种方法,即处理了上层及底层的mbox消息,同时处理了所有需要定时处理的事件。


https://www.xamrdz.com/lan/58c1959689.html

相关文章: