作者:爱碩爱你_静莫失心 | 来源:互联网 | 2023-10-14 11:05
文章目录 一,定时器时钟 二,快速定时任务 三,低速定时任务 1,超时重传 2,保活keepalive 3,删除超时PCB 四,小结
TCP协议中许多地方是需要使用到定时功能的,如定时重传功能,保活keepalive功能,坚持定时器功能,这些定时功能会在lwip中的两个定时器函数中实现。
一,定时器时钟 二,快速定时任务 void tcp_fasttmr(void)
比较简单,它的功能主要是每250ms处理延时发送的ack报文和fin报文,同时通知上层应用处理数据。
void tcp_fasttmr ( void ) { struct tcp_pcb * pcb; ++ tcp_timer_ctr; tcp_fasttmr_start: pcb = tcp_active_pcbs; while ( pcb != NULL ) { if ( pcb-> last_timer != tcp_timer_ctr) { struct tcp_pcb * next; pcb-> last_timer = tcp_timer_ctr; if ( pcb-> flags & TF_ACK_DELAY) { LWIP_DEBUGF ( TCP_DEBUG, ( "tcp_fasttmr: delayed ACK\n" ) ) ; tcp_ack_now ( pcb) ; tcp_output ( pcb) ; pcb-> flags & = ~ ( TF_ACK_DELAY | TF_ACK_NOW) ; } if ( pcb-> flags & TF_CLOSEPEND) { LWIP_DEBUGF ( TCP_DEBUG, ( "tcp_fasttmr: pending FIN\n" ) ) ; pcb-> flags & = ~ ( TF_CLOSEPEND) ; tcp_close_shutdown_fin ( pcb) ; } next = pcb-> next; if ( pcb-> refused_data != NULL ) { tcp_active_pcbs_changed = 0 ; tcp_process_refused_data ( pcb) ; if ( tcp_active_pcbs_changed) { goto tcp_fasttmr_start; } } pcb = next; } else { pcb = pcb-> next; } } }
三,低速定时任务 void tcp_slowtmr(void)
每500ms调用,该函数完成了超时重传,tcp保活功能,并会遍历active
和timewait
链表的PCB,删除那些超时或者出错的PCB,同时将PCB中unsent队列中的数据发送出去。一般使用tcp_write();写入数据后,数据不会马上发送,而是在定时任务中发送。
1,超时重传 重点的代码注释如下:
if ( pcb-> state == SYN_SENT && pcb-> nrtx >= TCP_SYNMAXRTX) { ++ pcb_remove; LWIP_DEBUGF ( TCP_DEBUG, ( "tcp_slowtmr: max SYN retries reached\n" ) ) ; } else if ( pcb-> nrtx >= TCP_MAXRTX) { ++ pcb_remove; LWIP_DEBUGF ( TCP_DEBUG, ( "tcp_slowtmr: max DATA retries reached\n" ) ) ; } else { if ( pcb-> persist_backoff > 0 ) { u8_t backoff_cnt = tcp_persist_backoff[ pcb-> persist_backoff- 1 ] ; if ( pcb-> persist_cnt < backoff_cnt) { pcb-> persist_cnt++ ; } if ( pcb-> persist_cnt >= backoff_cnt) { if ( tcp_zero_window_probe ( pcb) == ERR_OK) { pcb-> persist_cnt = 0 ; if ( pcb-> persist_backoff < sizeof ( tcp_persist_backoff) ) { pcb-> persist_backoff++ ; } } } } else { if ( pcb-> rtime >= 0 ) { ++ pcb-> rtime; } if ( pcb-> unacked != NULL && pcb-> rtime >= pcb-> rto) { LWIP_DEBUGF ( TCP_RTO_DEBUG, ( "tcp_slowtmr: rtime %" S16_F" pcb->rto %" S16_F"\n" , pcb-> rtime, pcb-> rto) ) ; ESP_STATS_TCP_PCB ( pcb) ; if ( pcb-> state != SYN_SENT) { u8_t backoff_idx = LWIP_MIN ( pcb-> nrtx, sizeof ( tcp_backoff) - 1 ) ; pcb-> rto = ( ( pcb-> sa >> 3 ) + pcb-> sv) << tcp_backoff[ backoff_idx] ; } pcb-> rtime = 0 ; eff_wnd = LWIP_MIN ( pcb-> cwnd, pcb-> snd_wnd) ; pcb-> ssthresh = eff_wnd >> 1 ; if ( pcb-> ssthresh < ( tcpwnd_size_t) ( pcb-> mss << 1 ) ) { pcb-> ssthresh = ( pcb-> mss << 1 ) ; } pcb-> cwnd = pcb-> mss; LWIP_DEBUGF ( TCP_CWND_DEBUG, ( "tcp_slowtmr: cwnd %" TCPWNDSIZE_F" ssthresh %" TCPWNDSIZE_F"\n" , pcb-> cwnd, pcb-> ssthresh) ) ; tcp_rexmit_rto ( pcb) ; } } } if ( pcb-> state == FIN_WAIT_2) { if ( pcb-> flags & TF_RXCLOSED) { if ( ( u32_t) ( tcp_ticks - pcb-> tmr) > TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { ++ pcb_remove; LWIP_DEBUGF ( TCP_DEBUG, ( "tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n" ) ) ; } } }
2,保活keepalive 服务端需要检查客户端是否还能通信,若两小时内无通信,客户端发送探查报文,若客户端ack,则更新保活计时器,否则,每隔75s发送一个探查报文,若发送超过9个报文,则认为客户端已挂掉
if ( ip_get_option ( pcb, SOF_KEEPALIVE) && ( ( pcb-> state == ESTABLISHED) || ( pcb-> state == CLOSE_WAIT) ) ) { if ( ( u32_t) ( tcp_ticks - pcb-> tmr) > ( pcb-> keep_idle + TCP_KEEP_DUR ( pcb) ) / TCP_SLOW_INTERVAL) { LWIP_DEBUGF ( TCP_DEBUG, ( "tcp_slowtmr: KEEPALIVE timeout. Aborting connection to " ) ) ; ip_addr_debug_print ( TCP_DEBUG, & pcb-> remote_ip) ; LWIP_DEBUGF ( TCP_DEBUG, ( "\n" ) ) ; ++ pcb_remove; ++ pcb_reset; } else if ( ( u32_t) ( tcp_ticks - pcb-> tmr) > ( pcb-> keep_idle + pcb-> keep_cnt_sent * TCP_KEEP_INTVL ( pcb) ) / TCP_SLOW_INTERVAL) { err = tcp_keepalive ( pcb) ; if ( err == ERR_OK) { pcb-> keep_cnt_sent++ ; } } }
3,删除超时PCB if ( pcb-> ooseq != NULL && ( u32_t) tcp_ticks - pcb-> tmr >= pcb-> rto * TCP_OOSEQ_TIMEOUT) { tcp_segs_free ( pcb-> ooseq) ; pcb-> ooseq = NULL ; LWIP_DEBUGF ( TCP_CWND_DEBUG, ( "tcp_slowtmr: dropping OOSEQ queued data\n" ) ) ; } if ( pcb-> state == SYN_RCVD) { if ( ( u32_t) ( tcp_ticks - pcb-> tmr) > TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { ++ pcb_remove; LWIP_DEBUGF ( TCP_DEBUG, ( "tcp_slowtmr: removing pcb stuck in SYN-RCVD\n" ) ) ; } } if ( pcb-> state == LAST_ACK) { if ( ( u32_t) ( tcp_ticks - pcb-> tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { ++ pcb_remove; LWIP_DEBUGF ( TCP_DEBUG, ( "tcp_slowtmr: removing pcb stuck in LAST-ACK\n" ) ) ; } }
以上的代码中若当前PCB需要被删除,则pcb_remove不为0,具体的删除代码如下:
if ( pcb_remove) { struct tcp_pcb * pcb2; tcp_err_fn err_fn = pcb-> errf; void * err_arg; enum tcp_state last_state; tcp_pcb_purge ( pcb) ; if ( prev != NULL ) { LWIP_ASSERT ( "tcp_slowtmr: middle tcp != tcp_active_pcbs" , pcb != tcp_active_pcbs) ; prev-> next = pcb-> next; } else { LWIP_ASSERT ( "tcp_slowtmr: first pcb == tcp_active_pcbs" , tcp_active_pcbs == pcb) ; tcp_active_pcbs = pcb-> next; } if ( pcb_reset) { tcp_rst ( pcb-> snd_nxt, pcb-> rcv_nxt, & pcb-> local_ip, & pcb-> remote_ip, pcb-> local_port, pcb-> remote_port) ; } err_arg = pcb-> callback_arg; last_state = pcb-> state; pcb2 = pcb; pcb = pcb-> next; memp_free ( MEMP_TCP_PCB, pcb2) ; tcp_active_pcbs_changed = 0 ; TCP_EVENT_ERR ( last_state, err_fn, err_arg, ERR_ABRT) ; if ( tcp_active_pcbs_changed) { goto tcp_slowtmr_start; } } else { prev = pcb; pcb = pcb-> next; ++ prev-> polltmr; if ( prev-> polltmr >= prev-> pollinterval) { prev-> polltmr = 0 ; LWIP_DEBUGF ( TCP_DEBUG, ( "tcp_slowtmr: polling application\n" ) ) ; tcp_active_pcbs_changed = 0 ; TCP_EVENT_POLL ( prev, err) ; if ( tcp_active_pcbs_changed) { goto tcp_slowtmr_start; } if ( err == ERR_OK) { tcp_output ( prev) ; } } } }
以上都是处理active链表的pcb,接下来处理timewait的pcb,苍天饶过谁?
prev = NULL ; pcb = tcp_tw_pcbs; while ( pcb != NULL ) { LWIP_ASSERT ( "tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT" , pcb-> state == TIME_WAIT) ; pcb_remove = 0 ; if ( ( u32_t) ( tcp_ticks - pcb-> tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { ++ pcb_remove; } if ( pcb_remove) { struct tcp_pcb * pcb2; tcp_pcb_purge ( pcb) ; if ( prev != NULL ) { LWIP_ASSERT ( "tcp_slowtmr: middle tcp != tcp_tw_pcbs" , pcb != tcp_tw_pcbs) ; prev-> next = pcb-> next; } else { LWIP_ASSERT ( "tcp_slowtmr: first pcb == tcp_tw_pcbs" , tcp_tw_pcbs == pcb) ; tcp_tw_pcbs = pcb-> next; } pcb2 = pcb; pcb = pcb-> next; memp_free ( MEMP_TCP_PCB, pcb2) ; } else { prev = pcb; pcb = pcb-> next; } }
四,小结 tcp定时任务是tcp接收发送的动力来源,需要关注。