作者:爱碩爱你_静莫失心 | 来源:互联网 | 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接收发送的动力来源,需要关注。